From 47528f02e7376f1fbb205c5cf69d17d55619fcb1 Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Fri, 12 Aug 2022 00:19:50 +0200 Subject: vm: support automatic periodic GC runs Introduce two new VM api functions uc_vm_gc_start() and uc_vm_gc_stop() which allow starting and stopping automatic periodic garbage collection of cyclic objects in the VM context. Signed-off-by: Jo-Philipp Wich --- include/ucode/types.h | 4 +++- include/ucode/vm.h | 9 ++++++++ types.c | 12 ++++++++--- vm.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 80 insertions(+), 4 deletions(-) diff --git a/include/ucode/types.h b/include/ucode/types.h index e20f3d6..6041b22 100644 --- a/include/ucode/types.h +++ b/include/ucode/types.h @@ -303,8 +303,10 @@ struct uc_vm { uint8_t u8; int8_t s8; } arg; - size_t spread_values; + size_t alloc_refs; uint8_t trace; + uint8_t gc_flags; + uint16_t gc_interval; uc_stringbuf_t *strbuf; uc_exception_handler_t *exhandler; FILE *output; diff --git a/include/ucode/vm.h b/include/ucode/vm.h index 8562524..161f1ee 100644 --- a/include/ucode/vm.h +++ b/include/ucode/vm.h @@ -116,6 +116,12 @@ typedef enum { ERROR_RUNTIME } uc_vm_status_t; +typedef enum { + GC_ENABLED = (1 << 0) +} uc_vm_gc_flags_t; + +#define GC_DEFAULT_INTERVAL 1000 + extern uint32_t insns[__I_MAX]; void uc_vm_init(uc_vm_t *vm, uc_parse_config_t *config); @@ -139,6 +145,9 @@ void uc_vm_exception_handler_set(uc_vm_t *vm, uc_exception_handler_t *exhandler) uint32_t uc_vm_trace_get(uc_vm_t *vm); void uc_vm_trace_set(uc_vm_t *vm, uint32_t level); +bool uc_vm_gc_start(uc_vm_t *vm, uint16_t interval); +bool uc_vm_gc_stop(uc_vm_t *vm); + uc_exception_type_t uc_vm_call(uc_vm_t *vm, bool mcall, size_t nargs); void __attribute__((format(printf, 3, 0))) diff --git a/types.c b/types.c index 983bda3..e05d685 100644 --- a/types.c +++ b/types.c @@ -705,8 +705,10 @@ ucv_array_new_length(uc_vm_t *vm, size_t length) uc_vector_grow(array); - if (vm) + if (vm) { ucv_ref(&vm->values, &array->ref); + vm->alloc_refs++; + } return &array->header; } @@ -901,8 +903,10 @@ ucv_object_new(uc_vm_t *vm) object->header.refcount = 1; object->table = table; - if (vm) + if (vm) { ucv_ref(&vm->values, &object->ref); + vm->alloc_refs++; + } return &object->header; } @@ -1022,8 +1026,10 @@ ucv_closure_new(uc_vm_t *vm, uc_function_t *function, bool arrow_fn) closure->is_arrow = arrow_fn; closure->upvals = function->nupvals ? (uc_upvalref_t **)((uintptr_t)closure + ALIGN(sizeof(*closure))) : NULL; - if (vm) + if (vm) { ucv_ref(&vm->values, &closure->ref); + vm->alloc_refs++; + } uc_program_get(function->program); diff --git a/vm.c b/vm.c index c08f9ce..d8f0074 100644 --- a/vm.c +++ b/vm.c @@ -2508,6 +2508,35 @@ uc_vm_insn_dynload(uc_vm_t *vm, uc_vm_insn_t insn) } } +static void +uc_vm_gc_step(uc_vm_t *vm) +{ + size_t curr_count = 0, prev_count = 0; + uc_weakref_t *ref; + + if (!(vm->gc_flags & GC_ENABLED)) + return; + + if (vm->alloc_refs >= vm->gc_interval) { + if (vm->trace) { + for (ref = vm->values.next; ref != &vm->values; ref = ref->next) + prev_count++; + + ucv_gc(vm); + + for (ref = vm->values.next; ref != &vm->values; ref = ref->next) + curr_count++; + + fprintf(stderr, "! GC reclaimed %zu object(s)\n", prev_count - curr_count); + } + else { + ucv_gc(vm); + } + + vm->alloc_refs = 0; + } +} + static uc_value_t * uc_vm_callframe_pop(uc_vm_t *vm) { @@ -2755,6 +2784,7 @@ uc_vm_execute_chunk(uc_vm_t *vm) case I_POP: ucv_put(uc_vm_stack_pop(vm)); + uc_vm_gc_step(vm); break; case I_CUPV: @@ -3028,3 +3058,32 @@ uc_vm_registry_delete(uc_vm_t *vm, const char *key) { return ucv_object_delete(vm->registry, key); } + +bool +uc_vm_gc_start(uc_vm_t *vm, uint16_t interval) +{ + bool changed = false; + + if (vm->gc_interval != interval) { + vm->gc_interval = interval; + changed = true; + } + + if (!(vm->gc_flags & GC_ENABLED)) { + vm->gc_flags |= GC_ENABLED; + changed = true; + } + + return changed; +} + +bool +uc_vm_gc_stop(uc_vm_t *vm) +{ + if (!(vm->gc_flags & GC_ENABLED)) + return false; + + vm->gc_flags &= ~GC_ENABLED; + + return true; +} -- cgit v1.2.3