From ea5c3b01ebb3e89ea27e3dd63c59db673f1b6864 Mon Sep 17 00:00:00 2001 From: Bob Nystrom Date: Sun, 13 Sep 2015 11:32:39 -0700 Subject: [PATCH] Add module for Scheduler. Also reorganizes some code to make it easier to add more modules. --- project/xcode/wren.xcodeproj/project.pbxproj | 30 ++++++- script/wren.mk | 3 +- src/cli/io.c | 6 +- src/cli/modules.c | 83 ++++++++++++++++++++ src/cli/modules.h | 19 +++++ src/cli/vm.c | 28 +++---- src/module/scheduler.c | 42 ++++++++++ src/module/scheduler.h | 13 +++ src/module/scheduler.wren | 26 ++++++ src/module/scheduler.wren.inc | 28 +++++++ src/module/timer.c | 31 +------- src/module/timer.h | 4 - src/module/timer.wren | 29 +------ src/module/timer.wren.inc | 29 +------ test/scheduler/add.wren | 15 ++++ test/timer/sleep_fibers.wren | 5 +- test/timer/sleep_float.wren | 5 +- test/timer/sleep_zero.wren | 5 +- 18 files changed, 291 insertions(+), 110 deletions(-) create mode 100644 src/cli/modules.c create mode 100644 src/cli/modules.h create mode 100644 src/module/scheduler.c create mode 100644 src/module/scheduler.h create mode 100644 src/module/scheduler.wren create mode 100644 src/module/scheduler.wren.inc create mode 100644 test/scheduler/add.wren diff --git a/project/xcode/wren.xcodeproj/project.pbxproj b/project/xcode/wren.xcodeproj/project.pbxproj index 63a6e6b6..8689aff2 100644 --- a/project/xcode/wren.xcodeproj/project.pbxproj +++ b/project/xcode/wren.xcodeproj/project.pbxproj @@ -8,6 +8,11 @@ /* Begin PBXBuildFile section */ 2901D7641B74F4050083A2C8 /* timer.c in Sources */ = {isa = PBXBuildFile; fileRef = 2901D7621B74F4050083A2C8 /* timer.c */; }; + 291647C41BA5EA45006142EE /* scheduler.c in Sources */ = {isa = PBXBuildFile; fileRef = 291647C21BA5EA45006142EE /* scheduler.c */; }; + 291647C71BA5EC5E006142EE /* modules.c in Sources */ = {isa = PBXBuildFile; fileRef = 291647C51BA5EC5E006142EE /* modules.c */; }; + 291647C81BA5EC5E006142EE /* modules.c in Sources */ = {isa = PBXBuildFile; fileRef = 291647C51BA5EC5E006142EE /* modules.c */; }; + 291647D01BA5ED26006142EE /* scheduler.wren.inc in Sources */ = {isa = PBXBuildFile; fileRef = 291647CD1BA5ED26006142EE /* scheduler.wren.inc */; }; + 291647D21BA5ED26006142EE /* timer.wren.inc in Sources */ = {isa = PBXBuildFile; fileRef = 291647CE1BA5ED26006142EE /* timer.wren.inc */; }; 29205C8F1AB4E5C90073018D /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = 29205C8E1AB4E5C90073018D /* main.c */; }; 29205C991AB4E6430073018D /* wren_compiler.c in Sources */ = {isa = PBXBuildFile; fileRef = 29205C921AB4E6430073018D /* wren_compiler.c */; }; 29205C9A1AB4E6430073018D /* wren_core.c in Sources */ = {isa = PBXBuildFile; fileRef = 29205C931AB4E6430073018D /* wren_core.c */; }; @@ -48,6 +53,12 @@ /* Begin PBXFileReference section */ 2901D7621B74F4050083A2C8 /* timer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = timer.c; path = ../../src/module/timer.c; sourceTree = ""; }; 2901D7631B74F4050083A2C8 /* timer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = timer.h; path = ../../src/module/timer.h; sourceTree = ""; }; + 291647C21BA5EA45006142EE /* scheduler.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = scheduler.c; path = ../../src/module/scheduler.c; sourceTree = ""; }; + 291647C31BA5EA45006142EE /* scheduler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = scheduler.h; path = ../../src/module/scheduler.h; sourceTree = ""; }; + 291647C51BA5EC5E006142EE /* modules.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = modules.c; path = ../../src/cli/modules.c; sourceTree = ""; }; + 291647C61BA5EC5E006142EE /* modules.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = modules.h; path = ../../src/cli/modules.h; sourceTree = ""; }; + 291647CD1BA5ED26006142EE /* scheduler.wren.inc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.pascal; name = scheduler.wren.inc; path = ../../src/module/scheduler.wren.inc; sourceTree = ""; }; + 291647CE1BA5ED26006142EE /* timer.wren.inc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.pascal; name = timer.wren.inc; path = ../../src/module/timer.wren.inc; sourceTree = ""; }; 29205C8E1AB4E5C90073018D /* main.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = main.c; path = ../../src/cli/main.c; sourceTree = ""; }; 29205C901AB4E62B0073018D /* wren.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = wren.h; path = ../../src/include/wren.h; sourceTree = ""; }; 29205C921AB4E6430073018D /* wren_compiler.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = wren_compiler.c; path = ../../src/vm/wren_compiler.c; sourceTree = ""; }; @@ -109,8 +120,12 @@ 2901D7611B74F3E20083A2C8 /* module */ = { isa = PBXGroup; children = ( + 291647C31BA5EA45006142EE /* scheduler.h */, + 291647C21BA5EA45006142EE /* scheduler.c */, + 291647CD1BA5ED26006142EE /* scheduler.wren.inc */, 2901D7631B74F4050083A2C8 /* timer.h */, 2901D7621B74F4050083A2C8 /* timer.c */, + 291647CE1BA5ED26006142EE /* timer.wren.inc */, ); name = module; sourceTree = ""; @@ -148,6 +163,8 @@ 29C8A9301AB71C3300DEC81D /* io.h */, 29C8A92E1AB71C1C00DEC81D /* io.c */, 29205C8E1AB4E5C90073018D /* main.c */, + 291647C61BA5EC5E006142EE /* modules.h */, + 291647C51BA5EC5E006142EE /* modules.c */, 29C8A9321AB71FFF00DEC81D /* vm.h */, 29C8A9311AB71FFF00DEC81D /* vm.c */, ); @@ -274,9 +291,11 @@ files = ( 29205C991AB4E6430073018D /* wren_compiler.c in Sources */, 2986F6D71ACF93BA00BCE26C /* wren_primitive.c in Sources */, + 291647C71BA5EC5E006142EE /* modules.c in Sources */, 29205C9A1AB4E6430073018D /* wren_core.c in Sources */, 2901D7641B74F4050083A2C8 /* timer.c in Sources */, 29C8A9331AB71FFF00DEC81D /* vm.c in Sources */, + 291647C41BA5EA45006142EE /* scheduler.c in Sources */, 29205C9B1AB4E6430073018D /* wren_debug.c in Sources */, 29205C9C1AB4E6430073018D /* wren_io.c in Sources */, 29205C9D1AB4E6430073018D /* wren_utils.c in Sources */, @@ -292,6 +311,9 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 291647C81BA5EC5E006142EE /* modules.c in Sources */, + 291647D21BA5ED26006142EE /* timer.wren.inc in Sources */, + 291647D01BA5ED26006142EE /* scheduler.wren.inc in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -379,7 +401,7 @@ GCC_WARN_PEDANTIC = NO; LIBRARY_SEARCH_PATHS = ../../build; PRODUCT_NAME = "$(TARGET_NAME)"; - USER_HEADER_SEARCH_PATHS = ../../deps/libuv/include; + USER_HEADER_SEARCH_PATHS = "../../deps/libuv/include ../../src/module"; }; name = Debug; }; @@ -392,7 +414,7 @@ GCC_WARN_PEDANTIC = NO; LIBRARY_SEARCH_PATHS = ../../build; PRODUCT_NAME = "$(TARGET_NAME)"; - USER_HEADER_SEARCH_PATHS = ../../deps/libuv/include; + USER_HEADER_SEARCH_PATHS = "../../deps/libuv/include ../../src/module"; }; name = Release; }; @@ -416,7 +438,7 @@ MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_NAME = "$(TARGET_NAME)"; - USER_HEADER_SEARCH_PATHS = ../../deps/libuv/include; + USER_HEADER_SEARCH_PATHS = "../../deps/libuv/include ../../src/module"; }; name = Debug; }; @@ -436,7 +458,7 @@ MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_NAME = "$(TARGET_NAME)"; - USER_HEADER_SEARCH_PATHS = ../../deps/libuv/include; + USER_HEADER_SEARCH_PATHS = "../../deps/libuv/include ../../src/module"; }; name = Release; }; diff --git a/script/wren.mk b/script/wren.mk index 1b6fdb95..29c37f00 100644 --- a/script/wren.mk +++ b/script/wren.mk @@ -153,7 +153,8 @@ lib/lib$(WREN).$(SHARED_EXT): $(VM_OBJECTS) # Test executable. $(BUILD_DIR)/test/$(WREN): $(TEST_OBJECTS) $(MODULE_OBJECTS) $(VM_OBJECTS) \ - $(BUILD_DIR)/cli/io.o $(BUILD_DIR)/cli/vm.o $(LIBUV) + $(BUILD_DIR)/cli/io.o $(BUILD_DIR)/cli/modules.o $(BUILD_DIR)/cli/vm.o \ + $(LIBUV) @ printf "%10s %-30s %s\n" $(CC) $@ "$(C_OPTIONS)" @ mkdir -p $(BUILD_DIR)/test @ $(CC) $(CFLAGS) $^ -o $@ -lm $(LIBUV_LIBS) diff --git a/src/cli/io.c b/src/cli/io.c index 18bb6d04..43326db0 100644 --- a/src/cli/io.c +++ b/src/cli/io.c @@ -3,8 +3,7 @@ #include #include "io.h" - -#include "timer.h" +#include "modules.h" char const* rootDirectory = NULL; @@ -73,7 +72,8 @@ char* wrenFilePath(const char* name) char* readModule(WrenVM* vm, const char* module) { - if (strcmp(module, "timer") == 0) return timerGetSource(); + char* source = readBuiltInModule(module); + if (source != NULL) return source; // First try to load the module with a ".wren" extension. char* modulePath = wrenFilePath(module); diff --git a/src/cli/modules.c b/src/cli/modules.c new file mode 100644 index 00000000..7f3bca56 --- /dev/null +++ b/src/cli/modules.c @@ -0,0 +1,83 @@ +#include +#include + +#include "modules.h" + +#include "scheduler.wren.inc" +#include "timer.wren.inc" + +#include "scheduler.h" +#include "timer.h" + +typedef struct +{ + // The name of the module. + const char* name; + + // Pointer to the string containing the source code of the module. We use a + // pointer here because the string variable itself is not a constant + // expression so can't be used in the initializer below. + const char **source; + + // The function that binds foreign methods in this module. + WrenForeignMethodFn (*bindMethodFn)(WrenVM* vm, const char* className, + bool isStatic, const char* signature); + + // The function that binds foreign classes in this module. + WrenForeignClassMethods (*bindClassFn)(WrenVM* vm, const char* className); +} BuiltInModule; + +// The array of built-in modules. +static BuiltInModule modules[] = +{ + {"scheduler", &schedulerModuleSource, schedulerBindForeign, NULL}, + {"timer", &timerModuleSource, timerBindForeign, NULL}, + + // Sentinel marking the end of the list. + {NULL, NULL, NULL, NULL} +}; + +// Looks for a built-in module with [name]. +// +// Returns the BuildInModule for it or NULL if not found. +static BuiltInModule* findModule(const char* name) +{ + for (int i = 0; modules[i].name != NULL; i++) + { + if (strcmp(name, modules[i].name) == 0) return &modules[i]; + } + + return NULL; +} + +char* readBuiltInModule(const char* name) +{ + BuiltInModule* module = findModule(name); + if (module == NULL) return NULL; + + size_t length = strlen(*module->source); + char* copy = (char*)malloc(length + 1); + strncpy(copy, *module->source, length + 1); + return copy; +} + +WrenForeignMethodFn bindBuiltInForeignMethod( + WrenVM* vm, const char* moduleName, const char* className, bool isStatic, + const char* signature) +{ + BuiltInModule* module = findModule(moduleName); + if (module == NULL) return NULL; + + return module->bindMethodFn(vm, className, isStatic, signature); +} + +WrenForeignClassMethods bindBuiltInForeignClass( + WrenVM* vm, const char* moduleName, const char* className) +{ + WrenForeignClassMethods methods = { NULL, NULL }; + + BuiltInModule* module = findModule(moduleName); + if (module == NULL) return methods; + + return module->bindClassFn(vm, className); +} \ No newline at end of file diff --git a/src/cli/modules.h b/src/cli/modules.h new file mode 100644 index 00000000..b29238fd --- /dev/null +++ b/src/cli/modules.h @@ -0,0 +1,19 @@ +#ifndef modules_h +#define modules_h + +#include "wren.h" + +char* readBuiltInModule(const char* module); + +// Looks up a foreign method in a built-in module. +// +// Returns `NULL` if [moduleName] is not a built-in module. +WrenForeignMethodFn bindBuiltInForeignMethod( + WrenVM* vm, const char* moduleName, const char* className, bool isStatic, + const char* signature); + +// Binds foreign classes declared in a built-in modules. +WrenForeignClassMethods bindBuiltInForeignClass( + WrenVM* vm, const char* moduleName, const char* className); + +#endif diff --git a/src/cli/vm.c b/src/cli/vm.c index 14c6de8b..90073411 100644 --- a/src/cli/vm.c +++ b/src/cli/vm.c @@ -2,8 +2,9 @@ #include #include "io.h" +#include "modules.h" #include "vm.h" -#include "timer.h" +#include "scheduler.h" #define MAX_LINE_LENGTH 1024 // TODO: Something less arbitrary. @@ -15,16 +16,15 @@ static WrenBindForeignClassFn bindClassFn = NULL; uv_loop_t* loop; -/// Binds foreign methods declared in either built in modules, or the injected -/// API test modules. +// Binds foreign methods declared in either built in modules, or the injected +// API test modules. static WrenForeignMethodFn bindForeignMethod(WrenVM* vm, const char* module, const char* className, bool isStatic, const char* signature) { - if (strcmp(module, "timer") == 0) - { - return timerBindForeign(vm, className, isStatic, signature); - } - + WrenForeignMethodFn method = bindBuiltInForeignMethod(vm, module, className, + isStatic, signature); + if (method != NULL) return method; + if (bindMethodFn != NULL) { return bindMethodFn(vm, module, className, isStatic, signature); @@ -33,14 +33,14 @@ static WrenForeignMethodFn bindForeignMethod(WrenVM* vm, const char* module, return NULL; } -/// Binds foreign classes declared in either built in modules, or the injected -/// API test modules. +// Binds foreign classes declared in either built in modules, or the injected +// API test modules. static WrenForeignClassMethods bindForeignClass( WrenVM* vm, const char* module, const char* className) { - WrenForeignClassMethods methods = { NULL, NULL }; - - // TODO: Bind classes for built-in modules here. + WrenForeignClassMethods methods = bindBuiltInForeignClass(vm, module, + className); + if (methods.allocate != NULL) return methods; if (bindClassFn != NULL) { @@ -70,7 +70,7 @@ static void initVM() static void freeVM() { - timerReleaseMethods(); + schedulerReleaseMethods(); uv_loop_close(loop); free(loop); diff --git a/src/module/scheduler.c b/src/module/scheduler.c new file mode 100644 index 00000000..66fa418c --- /dev/null +++ b/src/module/scheduler.c @@ -0,0 +1,42 @@ +#include +#include + +#include "uv.h" + +#include "scheduler.h" +#include "wren.h" +#include "vm.h" + +// This method resumes a fiber that is suspended waiting on an asynchronous +// operation. The first resumes it with zero arguments, and the second passes +// one. +static WrenValue* resume; +static WrenValue* resumeWithArg; + +static void captureMethods(WrenVM* vm) +{ + resume = wrenGetMethod(vm, "scheduler", "Scheduler", "resume_(_)"); + resumeWithArg = wrenGetMethod(vm, "scheduler", "Scheduler", "resume_(_,_)"); +} + +WrenForeignMethodFn schedulerBindForeign( + WrenVM* vm, const char* className, bool isStatic, const char* signature) +{ + if (strcmp(className, "Scheduler") != 0) return NULL; + + if (isStatic && strcmp(signature, "captureMethods_()") == 0) return captureMethods; + + return NULL; +} + +void schedulerResume(WrenValue* fiber) +{ + wrenCall(getVM(), resume, NULL, "v", fiber); + wrenReleaseValue(getVM(), fiber); +} + +void schedulerReleaseMethods() +{ + if (resume != NULL) wrenReleaseValue(getVM(), resume); + if (resumeWithArg != NULL) wrenReleaseValue(getVM(), resumeWithArg); +} diff --git a/src/module/scheduler.h b/src/module/scheduler.h new file mode 100644 index 00000000..a083f2b4 --- /dev/null +++ b/src/module/scheduler.h @@ -0,0 +1,13 @@ +#ifndef scheduler_h +#define scheduler_h + +#include "wren.h" + +WrenForeignMethodFn schedulerBindForeign( + WrenVM* vm, const char* className, bool isStatic, const char* signature); + +void schedulerResume(WrenValue* fiber); + +void schedulerReleaseMethods(); + +#endif diff --git a/src/module/scheduler.wren b/src/module/scheduler.wren new file mode 100644 index 00000000..9c0459a7 --- /dev/null +++ b/src/module/scheduler.wren @@ -0,0 +1,26 @@ +class Scheduler { + static add(callable) { + if (__scheduled == null) __scheduled = [] + + __scheduled.add(Fiber.new { + callable.call() + runNextScheduled_() + }) + } + + // Called by native code. + static resume_(fiber) { fiber.transfer() } + static resume_(fiber, arg) { fiber.transfer(arg) } + + static runNextScheduled_() { + if (__scheduled == null || __scheduled.isEmpty) { + return Fiber.suspend() + } else { + return __scheduled.removeAt(0).transfer() + } + } + + foreign static captureMethods_() +} + +Scheduler.captureMethods_() diff --git a/src/module/scheduler.wren.inc b/src/module/scheduler.wren.inc new file mode 100644 index 00000000..e3eed1b8 --- /dev/null +++ b/src/module/scheduler.wren.inc @@ -0,0 +1,28 @@ +// Generated automatically from src/module/scheduler.wren. Do not edit. +static const char* schedulerModuleSource = +"class Scheduler {\n" +" static add(callable) {\n" +" if (__scheduled == null) __scheduled = []\n" +"\n" +" __scheduled.add(Fiber.new {\n" +" callable.call()\n" +" runNextScheduled_()\n" +" })\n" +" }\n" +"\n" +" // Called by native code.\n" +" static resume_(fiber) { fiber.transfer() }\n" +" static resume_(fiber, arg) { fiber.transfer(arg) }\n" +"\n" +" static runNextScheduled_() {\n" +" if (__scheduled == null || __scheduled.isEmpty) {\n" +" return Fiber.suspend()\n" +" } else {\n" +" return __scheduled.removeAt(0).transfer()\n" +" }\n" +" }\n" +"\n" +" foreign static captureMethods_()\n" +"}\n" +"\n" +"Scheduler.captureMethods_()\n"; diff --git a/src/module/timer.c b/src/module/timer.c index 9046d5f5..4826b7a8 100644 --- a/src/module/timer.c +++ b/src/module/timer.c @@ -3,14 +3,10 @@ #include "uv.h" +#include "scheduler.h" #include "timer.h" -#include "wren.h" #include "vm.h" - -#include "timer.wren.inc" - -// The Wren method to call when a timer has completed. -static WrenValue* resumeTimer; +#include "wren.h" // Called by libuv when the timer finished closing. static void timerCloseCallback(uv_handle_t* handle) @@ -27,18 +23,11 @@ static void timerCallback(uv_timer_t* handle) uv_close((uv_handle_t*)handle, timerCloseCallback); // Run the fiber that was sleeping. - wrenCall(getVM(), resumeTimer, NULL, "v", fiber); - wrenReleaseValue(getVM(), fiber); + schedulerResume(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); @@ -50,15 +39,6 @@ static void startTimer(WrenVM* vm) uv_timer_start(handle, timerCallback, milliseconds, 0); } -char* timerGetSource() -{ - size_t length = strlen(timerModuleSource); - char* copy = (char*)malloc(length + 1); - strncpy(copy, timerModuleSource, length + 1); - - return copy; -} - WrenForeignMethodFn timerBindForeign( WrenVM* vm, const char* className, bool isStatic, const char* signature) { @@ -68,8 +48,3 @@ WrenForeignMethodFn timerBindForeign( return NULL; } - -void timerReleaseMethods() -{ - if (resumeTimer != NULL) wrenReleaseValue(getVM(), resumeTimer); -} diff --git a/src/module/timer.h b/src/module/timer.h index 8c3384b9..48d6899b 100644 --- a/src/module/timer.h +++ b/src/module/timer.h @@ -3,11 +3,7 @@ #include "wren.h" -char* timerGetSource(); - WrenForeignMethodFn timerBindForeign( WrenVM* vm, const char* className, bool isStatic, const char* signature); -void timerReleaseMethods(); - #endif diff --git a/src/module/timer.wren b/src/module/timer.wren index d358ebec..77533a66 100644 --- a/src/module/timer.wren +++ b/src/module/timer.wren @@ -1,34 +1,13 @@ +import "scheduler" for Scheduler + class Timer { static sleep(milliseconds) { if (!(milliseconds is Num)) Fiber.abort("Milliseconds must be a number.") if (milliseconds < 0) Fiber.abort("Milliseconds cannot be negative.") + startTimer_(milliseconds, Fiber.current) - - runNextScheduled_() - } - - // TODO: Once the CLI modules are more fleshed out, find a better place to - // put this. - static schedule(callable) { - if (__scheduled == null) __scheduled = [] - __scheduled.add(Fiber.new { - callable.call() - runNextScheduled_() - }) + Scheduler.runNextScheduled_() } foreign static startTimer_(milliseconds, fiber) - - // Called by native code. - static resumeTimer_(fiber) { - fiber.transfer() - } - - static runNextScheduled_() { - if (__scheduled == null || __scheduled.isEmpty) { - Fiber.suspend() - } else { - __scheduled.removeAt(0).transfer() - } - } } diff --git a/src/module/timer.wren.inc b/src/module/timer.wren.inc index fbb18daf..71f8f999 100644 --- a/src/module/timer.wren.inc +++ b/src/module/timer.wren.inc @@ -1,36 +1,15 @@ // Generated automatically from src/module/timer.wren. Do not edit. static const char* timerModuleSource = +"import \"scheduler\" for Scheduler\n" +"\n" "class Timer {\n" " static sleep(milliseconds) {\n" " if (!(milliseconds is Num)) Fiber.abort(\"Milliseconds must be a number.\")\n" " if (milliseconds < 0) Fiber.abort(\"Milliseconds cannot be negative.\")\n" +"\n" " startTimer_(milliseconds, Fiber.current)\n" -"\n" -" runNextScheduled_()\n" -" }\n" -"\n" -" // TODO: Once the CLI modules are more fleshed out, find a better place to\n" -" // put this.\n" -" static schedule(callable) {\n" -" if (__scheduled == null) __scheduled = []\n" -" __scheduled.add(Fiber.new {\n" -" callable.call()\n" -" runNextScheduled_()\n" -" })\n" +" Scheduler.runNextScheduled_()\n" " }\n" "\n" " foreign static startTimer_(milliseconds, fiber)\n" -"\n" -" // Called by native code.\n" -" static resumeTimer_(fiber) {\n" -" fiber.transfer()\n" -" }\n" -"\n" -" static runNextScheduled_() {\n" -" if (__scheduled == null || __scheduled.isEmpty) {\n" -" Fiber.suspend()\n" -" } else {\n" -" __scheduled.removeAt(0).transfer()\n" -" }\n" -" }\n" "}\n"; diff --git a/test/scheduler/add.wren b/test/scheduler/add.wren new file mode 100644 index 00000000..7dd7d08e --- /dev/null +++ b/test/scheduler/add.wren @@ -0,0 +1,15 @@ +import "scheduler" for Scheduler +import "timer" for Timer + +var run = false +Scheduler.add { + run = true +} + +// Does not run immediately. +IO.print(run) // expect: false + +Timer.sleep(0) + +// Runs when the main fiber suspends on IO. +IO.print(run) // expect: true diff --git a/test/timer/sleep_fibers.wren b/test/timer/sleep_fibers.wren index cc74568e..e7038234 100644 --- a/test/timer/sleep_fibers.wren +++ b/test/timer/sleep_fibers.wren @@ -1,11 +1,12 @@ +import "scheduler" for Scheduler import "timer" for Timer -Timer.schedule { +Scheduler.add { Timer.sleep(2) IO.print("a") } -Timer.schedule { +Scheduler.add { Timer.sleep(1) IO.print("b") } diff --git a/test/timer/sleep_float.wren b/test/timer/sleep_float.wren index 09993093..43142986 100644 --- a/test/timer/sleep_float.wren +++ b/test/timer/sleep_float.wren @@ -1,12 +1,13 @@ +import "scheduler" for Scheduler import "timer" for Timer // These are both rounded to 1, so "a" should complete first. -Timer.schedule { +Scheduler.add { Timer.sleep(1.5) IO.print("a") } -Timer.schedule { +Scheduler.add { Timer.sleep(1.3) IO.print("b") } diff --git a/test/timer/sleep_zero.wren b/test/timer/sleep_zero.wren index 3074fff3..2240ea2c 100644 --- a/test/timer/sleep_zero.wren +++ b/test/timer/sleep_zero.wren @@ -1,12 +1,13 @@ +import "scheduler" for Scheduler import "timer" for Timer -Timer.schedule { +Scheduler.add { IO.print("a before") Timer.sleep(0) IO.print("a after") } -Timer.schedule { +Scheduler.add { IO.print("b before") Timer.sleep(0) IO.print("b after")