summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJo-Philipp Wich <jo@mein.io>2022-08-12 00:19:50 +0200
committerJo-Philipp Wich <jo@mein.io>2022-08-12 01:03:00 +0200
commit47528f02e7376f1fbb205c5cf69d17d55619fcb1 (patch)
tree4a6255f3c741cbcbb33b42605177e200122e1ff2
parent381cc7508f797e5158bbd1620d6154ef4a11b76c (diff)
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 <jo@mein.io>
-rw-r--r--include/ucode/types.h4
-rw-r--r--include/ucode/vm.h9
-rw-r--r--types.c12
-rw-r--r--vm.c59
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;
+}