feat: add libuv event loop integration with timer module and CLI restructuring
Add a vertical slice of real libuv integration by introducing a "timer" CLI module with a Timer class and static sleep() method. This required significant infrastructure changes: setting up libuv event loop in the CLI, creating a new src/module directory for CLI modules, updating all build scripts (Makefile, wren.mk, Xcode project) to handle the new module compilation, and reorganizing CLI code by moving REPL logic from main.c to vm.c. The generate_builtins.py script was also updated to support copying Wren sources into the module directory.
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "uv.h"
|
||||
|
||||
#include "timer.h"
|
||||
#include "wren.h"
|
||||
#include "vm.h"
|
||||
|
||||
// This is generated from builtin/module/timer.wren. Do not edit here.
|
||||
static const char* timerLibSource =
|
||||
"class Timer {\n"
|
||||
" static sleep(milliseconds) {\n"
|
||||
" startTimer_(milliseconds, Fiber.current)\n"
|
||||
" Fiber.yield()\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" foreign static startTimer_(milliseconds, fiber)\n"
|
||||
"\n"
|
||||
" // Called by native code.\n"
|
||||
" static resumeTimer_(fiber) {\n"
|
||||
" fiber.run()\n"
|
||||
" }\n"
|
||||
"}\n";
|
||||
|
||||
// The Wren method to call when a timer has completed.
|
||||
static WrenMethod* resumeTimer;
|
||||
|
||||
// Called by libuv when the timer has completed.
|
||||
static void timerCallback(uv_timer_t* handle)
|
||||
{
|
||||
WrenValue* fiber = (WrenValue*)handle->data;
|
||||
free(handle);
|
||||
|
||||
// Run the fiber that was sleeping.
|
||||
wrenCall(getVM(), resumeTimer, "v", fiber);
|
||||
wrenReleaseValue(getVM(), fiber);
|
||||
}
|
||||
|
||||
static void startTimer(WrenVM* vm)
|
||||
{
|
||||
// If we haven't looked up the resume method yet, grab it now.
|
||||
if (resumeTimer == NULL)
|
||||
{
|
||||
resumeTimer = wrenGetMethod(vm, "timer", "Timer", "resumeTimer_(_)");
|
||||
}
|
||||
|
||||
int milliseconds = (int)wrenGetArgumentDouble(vm, 1);
|
||||
WrenValue* fiber = wrenGetArgumentValue(vm, 2);
|
||||
|
||||
// Store the fiber to resume when the timer completes.
|
||||
uv_timer_t* handle = (uv_timer_t*)malloc(sizeof(uv_timer_t));
|
||||
handle->data = fiber;
|
||||
|
||||
uv_timer_init(getLoop(), handle);
|
||||
uv_timer_start(handle, timerCallback, milliseconds, 0);
|
||||
}
|
||||
|
||||
char* timerGetSource()
|
||||
{
|
||||
size_t length = strlen(timerLibSource);
|
||||
char* copy = malloc(length + 1);
|
||||
strncpy(copy, timerLibSource, length);
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
WrenForeignMethodFn timerBindForeign(
|
||||
WrenVM* vm, const char* className, bool isStatic, const char* signature)
|
||||
{
|
||||
if (strcmp(className, "Timer") != 0) return NULL;
|
||||
|
||||
if (isStatic && strcmp(signature, "startTimer_(_,_)") == 0) return startTimer;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void timerReleaseMethods()
|
||||
{
|
||||
if (resumeTimer != NULL) wrenReleaseMethod(getVM(), resumeTimer);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#ifndef timer_h
|
||||
#define timer_h
|
||||
|
||||
#include "wren.h"
|
||||
|
||||
// TODO: Coherent naming scheme.
|
||||
char* timerGetSource();
|
||||
|
||||
WrenForeignMethodFn timerBindForeign(
|
||||
WrenVM* vm, const char* className, bool isStatic, const char* signature);
|
||||
|
||||
void timerReleaseMethods();
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,13 @@
|
||||
class Timer {
|
||||
static sleep(milliseconds) {
|
||||
startTimer_(milliseconds, Fiber.current)
|
||||
Fiber.yield()
|
||||
}
|
||||
|
||||
foreign static startTimer_(milliseconds, fiber)
|
||||
|
||||
// Called by native code.
|
||||
static resumeTimer_(fiber) {
|
||||
fiber.run()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user