diff --git a/src/cli/gravity.c b/src/cli/gravity.c index 5c8d0964..806d1146 100644 --- a/src/cli/gravity.c +++ b/src/cli/gravity.c @@ -131,6 +131,18 @@ static void gravity_repl (void) { // MARK: - +int x = 0; + +static bool should_preempt(void *xdata) { + x++; + if (x % 3 == 0) { + return true; + } + return false; +} + +// MARK: - + int main (int argc, const char* argv[]) { // parse arguments and return operation type op_type type = parse_args(argc, argv); @@ -158,6 +170,7 @@ int main (int argc, const char* argv[]) { // create VM gravity_vm *vm = gravity_vm_new(&delegate); + delegate.preempt_callback = &should_preempt; // check if input file is source code that needs to be compiled if ((type == OP_COMPILE) || (type == OP_COMPILE_RUN)) { @@ -198,8 +211,15 @@ int main (int argc, const char* argv[]) { // sanity check assert(closure); - - if (gravity_vm_runmain(vm, closure)) { + + bool result; + result = gravity_vm_runmain(vm, closure, true); + + while (!result && gravity_vm_preempted(vm)) { + result = gravity_vm_run_resume_main(vm, closure, true); + } + + if (result) { gravity_value_t result = gravity_vm_result(vm); double t = gravity_vm_time(vm); diff --git a/src/cli/unittest.c b/src/cli/unittest.c index 963d8348..e95fc5b4 100644 --- a/src/cli/unittest.c +++ b/src/cli/unittest.c @@ -132,7 +132,7 @@ static void test_folder (const char *folder_path, test_data *data) { gravity_compiler_free(compiler); if (closure) { - if (gravity_vm_runmain(vm, closure)) { + if (gravity_vm_runmain(vm, closure, NULL)) { data->processed = true; gravity_value_t result = gravity_vm_result(vm); if (gravity_value_equals(result, data->expected_value)) { diff --git a/src/runtime/gravity_vm.c b/src/runtime/gravity_vm.c index 8edf504b..039a4112 100644 --- a/src/runtime/gravity_vm.c +++ b/src/runtime/gravity_vm.c @@ -6,6 +6,7 @@ // Copyright (c) 2014 CreoLabs. All rights reserved. // +#include #include "gravity_hash.h" #include "gravity_array.h" #include "gravity_debug.h" @@ -27,6 +28,15 @@ static void gravity_gc_transform (gravity_hash_t *hashtable, gravity_value_t key static uint32_t cache_refcount = 0; static gravity_value_t cache[GRAVITY_VTABLE_SIZE]; +struct vm_state { + gravity_fiber_t *fiber; + gravity_delegate_t *delegate; + gravity_callframe_t *frame; + gravity_function_t *func; + gravity_value_t *stackstart; + uint32_t *ip; +}; + // Opaque VM struct struct gravity_vm { gravity_hash_t *context; // context hash table @@ -67,6 +77,7 @@ struct gravity_vm { #endif bool preempted; // Set to true if the VM was preempted -- check flag if returns false + struct vm_state vm_state; }; // MARK: - @@ -288,16 +299,36 @@ static bool gravity_vm_exec(gravity_vm *vm, bool preemptable) { register uint32_t *ip; // IP => instruction pointer register uint32_t inst; // IR => instruction register register opcode_t op; // OP => opcode register - - // load current callframe - LOAD_FRAME(); - DEBUG_CALL("Executing", func); - - // sanity check - if ((ip == NULL) || (!func->bytecode) || (func->ninsts == 0)) return true; - - DEBUG_STACK(); - + gravity_preempt_callback preempt_cb = NULL; // Callback to cause preemption + + + + // Set preempt_cb if running in a preemptable context + if (preemptable) { + preempt_cb = vm->delegate->preempt_callback; + } + + if (vm->preempted) { + // Restore the VM state + delegate = vm->vm_state.delegate; + fiber = vm->vm_state.fiber; + frame = vm->vm_state.frame; + func = vm->vm_state.func; + ip = vm->vm_state.ip; + stackstart = vm->vm_state.stackstart; + vm->preempted = false; + } else { + // load current callframe + LOAD_FRAME(); + DEBUG_CALL("Executing", func); + + // sanity check + if ((ip == NULL) || (!func->bytecode) || (func->ninsts == 0)) return true; + + DEBUG_STACK(); + } + + while (1) { INTERPRET_LOOP { @@ -1265,6 +1296,14 @@ static bool gravity_vm_exec(gravity_vm *vm, bool preemptable) { return true; PREEMPT: + vm->preempted = true; + vm->vm_state.delegate = delegate; + vm->vm_state.fiber = fiber; + vm->vm_state.frame = frame; + vm->vm_state.func = func; + vm->vm_state.ip = ip; + vm->vm_state.stackstart = stackstart; + return false; } @@ -1502,7 +1541,7 @@ bool gravity_vm_runclosure (gravity_vm *vm, gravity_closure_t *closure, gravity_ return result; } -bool gravity_vm_runmain (gravity_vm *vm, gravity_closure_t *closure) { +bool gravity_vm_runmain(gravity_vm *vm, gravity_closure_t *closure, bool preemptable) { // first load closure into vm if (closure) gravity_vm_loadclosure(vm, closure); @@ -1519,15 +1558,23 @@ bool gravity_vm_runmain (gravity_vm *vm, gravity_closure_t *closure) { // execute main function RESET_STATS(vm); - nanotime_t tstart = nanotime(); - bool result = gravity_vm_exec(vm, false); - nanotime_t tend = nanotime(); - vm->time = millitime(tstart, tend); - - PRINT_STATS(vm); - return result; + + return gravity_vm_run_resume_main(vm, closure, preemptable); } +bool gravity_vm_run_resume_main(gravity_vm *vm, gravity_closure_t *closure, bool preemptable) { + nanotime_t tstart = nanotime(); + bool result = gravity_vm_exec(vm, preemptable); + nanotime_t tend = nanotime(); + vm->time += millitime(tstart, tend); + + if (!gravity_vm_preempted(vm)) + PRINT_STATS(vm); + + return result; +} + + // MARK: - User - void gravity_vm_setslot (gravity_vm *vm, gravity_value_t value, uint32_t index) { @@ -2064,3 +2111,7 @@ void gravity_gc_push (gravity_vm *vm, gravity_object_t *obj) { void gravity_gc_pop (gravity_vm *vm) { marray_pop(vm->gcsave); } + +bool gravity_vm_preempted (gravity_vm *vm) { + return vm->preempted; +} \ No newline at end of file diff --git a/src/runtime/gravity_vm.h b/src/runtime/gravity_vm.h index 614a885c..ea9d0201 100644 --- a/src/runtime/gravity_vm.h +++ b/src/runtime/gravity_vm.h @@ -26,7 +26,9 @@ void gravity_vm_set_callbacks (gravity_vm *vm, vm_transfer_cb vm_transfer, vm void gravity_vm_free (gravity_vm *vm); void gravity_vm_reset (gravity_vm *vm); bool gravity_vm_runclosure (gravity_vm *vm, gravity_closure_t *closure, gravity_value_t selfvalue, gravity_value_t params[], uint16_t nparams); -bool gravity_vm_runmain (gravity_vm *vm, gravity_closure_t *closure); + +bool gravity_vm_runmain(gravity_vm *vm, gravity_closure_t *closure, bool preemptable); +bool gravity_vm_run_resume_main(gravity_vm *vm, gravity_closure_t *closure, bool preemptable); void gravity_vm_loadclosure (gravity_vm *vm, gravity_closure_t *closure); void gravity_vm_setvalue (gravity_vm *vm, const char *key, gravity_value_t value); gravity_value_t gravity_vm_lookup (gravity_vm *vm, gravity_value_t key); diff --git a/src/runtime/gravity_vmmacros.h b/src/runtime/gravity_vmmacros.h index c26beae8..43e25f64 100644 --- a/src/runtime/gravity_vmmacros.h +++ b/src/runtime/gravity_vmmacros.h @@ -52,7 +52,7 @@ #define GRAVITY_GC_STRESSTEST 0 #define GRAVITY_GC_DEBUG 0 #define GRAVITY_STACK_DEBUG 0 -#define GRAVITY_VM_PREEMPTION 0 +#define GRAVITY_VM_PREEMPTION 1 #if GRAVITY_STACK_DEBUG #define DEBUG_STACK() gravity_stack_dump(fiber) @@ -117,7 +117,7 @@ #define FN_COUNTREG(_f,_nargs) (MAXNUM(_f->nparams,_nargs) + _f->nlocals + _f->ntemps) #if GRAVITY_VM_PREEMPTION -#define SHOULD_PREEMPT +#define SHOULD_PREEMPT if (preempt_cb && preempt_cb(vm->delegate->xdata)) goto PREEMPT; #else #define SHOULD_PREEMPT #endif