From 160dd830a0a3b3fa2adae7fa22e13088d0e9b540 Mon Sep 17 00:00:00 2001 From: bakpakin Date: Sat, 24 Jul 2021 15:14:37 -0500 Subject: [PATCH] Add janet_interpreter_interrupt for custom scheduling. This would allow an embedder to suspend the current Janet fiber via an external event like a signal, other thread, or really anything. This is a useful primitive for custom schedulers that would call janet_interpreter_interupt periodically (say, in an interval with SIG_ALRM), do some work, and then use janet_continue on the janet_root_fiber, or for embedding into other soft-realtime applications like a game. To say, only allow about 5ms per frame of interpreter time. --- meson.build | 1 + meson_options.txt | 1 + src/conf/janetconf.h | 1 + src/core/state.c | 15 +++++++++++++++ src/core/state.h | 4 ++++ src/core/vm.c | 22 ++++++++++++++++++++++ src/include/janet.h | 2 ++ 7 files changed, 46 insertions(+) diff --git a/meson.build b/meson.build index 30888bd8..772d0c99 100644 --- a/meson.build +++ b/meson.build @@ -74,6 +74,7 @@ conf.set('JANET_NO_PROCESSES', not get_option('processes')) conf.set('JANET_SIMPLE_GETLINE', get_option('simple_getline')) conf.set('JANET_EV_NO_EPOLL', not get_option('epoll')) conf.set('JANET_NO_THREADS', get_option('threads')) +conf.set('JANET_NO_INTERPRETER_INTERRUPT', not get_option('interpreter_interrupt')) if get_option('os_name') != '' conf.set('JANET_OS_NAME', get_option('os_name')) endif diff --git a/meson_options.txt b/meson_options.txt index 7d1c0e01..9a2818e6 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -18,6 +18,7 @@ option('umask', type : 'boolean', value : true) option('realpath', type : 'boolean', value : true) option('simple_getline', type : 'boolean', value : false) option('epoll', type : 'boolean', value : false) +option('interpreter_interrupt', type : 'boolean', value : false) option('recursion_guard', type : 'integer', min : 10, max : 8000, value : 1024) option('max_proto_depth', type : 'integer', min : 10, max : 8000, value : 200) diff --git a/src/conf/janetconf.h b/src/conf/janetconf.h index dfabc1cb..9beb8657 100644 --- a/src/conf/janetconf.h +++ b/src/conf/janetconf.h @@ -48,6 +48,7 @@ /* #define JANET_OS_NAME my-custom-os */ /* #define JANET_ARCH_NAME pdp-8 */ /* #define JANET_EV_NO_EPOLL */ +/* #define JANET_NO_INTERPRETER_INTERRUPT */ /* Custom vm allocator support */ /* #include */ diff --git a/src/core/state.c b/src/core/state.c index c2d4d4bd..027fa4a0 100644 --- a/src/core/state.c +++ b/src/core/state.c @@ -28,6 +28,10 @@ JANET_THREAD_LOCAL JanetVM janet_vm; +JanetVM *janet_local_vm(void) { + return &janet_vm; +} + JanetVM *janet_vm_alloc(void) { JanetVM *mem = janet_malloc(sizeof(JanetVM)); if (NULL == mem) { @@ -47,3 +51,14 @@ void janet_vm_save(JanetVM *into) { void janet_vm_load(JanetVM *from) { janet_vm = *from; } + +/* Trigger suspension of the Janet vm by trying to + * exit the interpeter loop when convenient. You can optionally + * use NULL to interrupt the current VM when convenient */ +void janet_interpreter_interrupt(JanetVM *vm) { + if (NULL == vm) { + janet_vm.auto_suspend = 1; + } else { + vm->auto_suspend = 1; + } +} diff --git a/src/core/state.h b/src/core/state.h index c048464f..9f0ab5f8 100644 --- a/src/core/state.h +++ b/src/core/state.h @@ -72,6 +72,10 @@ struct JanetVM { /* How many VM stacks have been entered */ int stackn; + /* If this flag is true, suspend on function calls and backwards jumps. + * When this occurs, this flag will be reset to 0. */ + int auto_suspend; + /* The current running fiber on the current thread. * Set and unset by janet_run. */ JanetFiber *fiber; diff --git a/src/core/vm.c b/src/core/vm.c index f624b9e2..3a8db3ae 100644 --- a/src/core/vm.c +++ b/src/core/vm.c @@ -111,6 +111,17 @@ janet_panicf("expected %T, got %v", (TS), (X)); \ } \ } while (0) +#ifdef JANET_NO_INTERPRETER_INTERRUPT +#define vm_maybe_auto_suspend(COND) +#else +#define vm_maybe_auto_suspend(COND) do { \ + if ((COND) && janet_vm.auto_suspend) { \ + janet_vm.auto_suspend = 0; \ + fiber->flags |= (JANET_FIBER_RESUME_NO_USEVAL | JANET_FIBER_RESUME_NO_SKIP); \ + vm_return(JANET_SIGNAL_EVENT, janet_wrap_nil()); \ + } \ +} while (0) +#endif /* Templates for certain patterns in opcodes */ #define vm_binop_immediate(op)\ @@ -746,11 +757,13 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) { VM_OP(JOP_JUMP) pc += DS; + vm_maybe_auto_suspend(DS < 0); vm_next(); VM_OP(JOP_JUMP_IF) if (janet_truthy(stack[A])) { pc += ES; + vm_maybe_auto_suspend(ES < 0); } else { pc++; } @@ -761,12 +774,14 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) { pc++; } else { pc += ES; + vm_maybe_auto_suspend(ES < 0); } vm_next(); VM_OP(JOP_JUMP_IF_NIL) if (janet_checktype(stack[A], JANET_NIL)) { pc += ES; + vm_maybe_auto_suspend(ES < 0); } else { pc++; } @@ -777,6 +792,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) { pc++; } else { pc += ES; + vm_maybe_auto_suspend(ES < 0); } vm_next(); @@ -950,6 +966,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) { vm_checkgc_pcnext(); VM_OP(JOP_CALL) { + vm_maybe_auto_suspend(1); Janet callee = stack[E]; if (fiber->stacktop > fiber->maxstack) { vm_throw("stack overflow"); @@ -989,6 +1006,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) { } VM_OP(JOP_TAILCALL) { + vm_maybe_auto_suspend(1); Janet callee = stack[D]; if (fiber->stacktop > fiber->maxstack) { vm_throw("stack overflow"); @@ -1035,6 +1053,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) { VM_OP(JOP_RESUME) { Janet retreg; + vm_maybe_auto_suspend(1); vm_assert_type(stack[B], JANET_FIBER); JanetFiber *child = janet_unwrap_fiber(stack[B]); if (janet_check_can_resume(child, &retreg)) { @@ -1519,6 +1538,9 @@ int janet_init(void) { /* Core env */ janet_vm.core_env = NULL; + /* Auto suspension */ + janet_vm.auto_suspend = 0; + /* Dynamic bindings */ janet_vm.top_dyns = NULL; diff --git a/src/include/janet.h b/src/include/janet.h index 7e68236e..61f0e7ad 100644 --- a/src/include/janet.h +++ b/src/include/janet.h @@ -1660,9 +1660,11 @@ JANET_API int32_t janet_sorted_keys(const JanetKV *dict, int32_t cap, int32_t *i JANET_API int janet_init(void); JANET_API void janet_deinit(void); JANET_API JanetVM *janet_vm_alloc(void); +JANET_API JanetVM *janet_local_vm(void); JANET_API void janet_vm_free(JanetVM *vm); JANET_API void janet_vm_save(JanetVM *into); JANET_API void janet_vm_load(JanetVM *from); +JANET_API void janet_interpreter_interrupt(JanetVM *vm); JANET_API JanetSignal janet_continue(JanetFiber *fiber, Janet in, Janet *out); JANET_API JanetSignal janet_continue_signal(JanetFiber *fiber, Janet in, Janet *out, JanetSignal sig); JANET_API JanetSignal janet_pcall(JanetFunction *fun, int32_t argn, const Janet *argv, Janet *out, JanetFiber **f);