diff options
author | Jo-Philipp Wich <jo@mein.io> | 2022-08-12 00:22:26 +0200 |
---|---|---|
committer | Jo-Philipp Wich <jo@mein.io> | 2022-08-12 01:23:27 +0200 |
commit | 85d7885e226ed79dc071e0cef73ccc918144a8f5 (patch) | |
tree | 0e56769cd7fbe473c18a656b1f63d256a7856547 | |
parent | 47528f02e7376f1fbb205c5cf69d17d55619fcb1 (diff) |
lib: implement gc()
Introduce a new stdlib function `gc()` which allows controlling the periodic
garbage collector from ucode.
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
-rw-r--r-- | lib.c | 44 | ||||
-rw-r--r-- | tests/custom/03_stdlib/60_gc | 107 |
2 files changed, 151 insertions, 0 deletions
@@ -3519,6 +3519,49 @@ uc_hexdec(uc_vm_t *vm, size_t nargs) return ucv_stringbuf_finish(buf); } +static uc_value_t * +uc_gc(uc_vm_t *vm, size_t nargs) +{ + uc_value_t *operation = uc_fn_arg(0); + uc_value_t *argument = uc_fn_arg(1); + const char *op = NULL; + uc_weakref_t *ref; + int64_t n; + + if (operation != NULL && ucv_type(operation) != UC_STRING) + return NULL; + + op = ucv_string_get(operation); + + if (!op || !strcmp(op, "collect")) { + ucv_gc(vm); + + return ucv_boolean_new(true); + } + else if (!strcmp(op, "start")) { + n = argument ? ucv_int64_get(argument) : 0; + + if (errno || n < 0 || n > 0xFFFF) + return NULL; + + if (n == 0) + n = GC_DEFAULT_INTERVAL; + + return ucv_boolean_new(uc_vm_gc_start(vm, n)); + } + else if (!strcmp(op, "stop")) { + return ucv_boolean_new(uc_vm_gc_stop(vm)); + } + else if (!strcmp(op, "count")) { + for (n = 0, ref = vm->values.next; ref != &vm->values; ref = ref->next) + n++; + + return ucv_uint64_new(n); + } + + return NULL; +} + const uc_function_list_t uc_stdlib_functions[] = { { "chr", uc_chr }, @@ -3586,6 +3629,7 @@ const uc_function_list_t uc_stdlib_functions[] = { { "clock", uc_clock }, { "hexdec", uc_hexdec }, { "hexenc", uc_hexenc }, + { "gc", uc_gc } }; diff --git a/tests/custom/03_stdlib/60_gc b/tests/custom/03_stdlib/60_gc new file mode 100644 index 0000000..44e5d9e --- /dev/null +++ b/tests/custom/03_stdlib/60_gc @@ -0,0 +1,107 @@ +The `gc()` function allows controlling the garbage collector of the VM. +It takes the requested operation as first argument and an optional, +operation specific second argument. + +Returns `null` if the given operation is invalid or if the operation +specific argument is invalid. + +Returns `false` if the requested operation would not result in any +changes. + +Returns `true` if the requested oepration succeeded (e.g. starting +the GC when it was previously stopped). + +Returns an object count if the given operation is `count`. + +-- Testcase -- +{% + printf("Count #1: %d\n", gc("count")); + + // create an unreachable cyclic structure + let o = {}; + o.cycle = o; + o = null; + + printf("Count #2: %d\n", gc("count")); + + // invoking gc without any argument defaults to "collect" + gc(); + + printf("Count #3: %d\n", gc("count")); + + + // create another unreachable cyclic structure + o = {}; + o.cycle = o; + o = null; + + printf("Count #4: %d\n", gc("count")); + + // invoking gc with explicit collect argument + gc("collect"); + + printf("Count #5: %d\n", gc("count")); +%} +-- End -- + +-- Expect stdout -- +Count #1: 5 +Count #2: 6 +Count #3: 5 +Count #4: 6 +Count #5: 5 +-- End -- + + +Testing enabling the automatic collector. + +-- Testcase -- +{% + // start GC, trigger every 10 object allocations + gc("start", 10); + + for (let i = 0; i < 100; i++) { + let o = {}; + o.cyle = o; + o = null; + + if ((i % 10) == 0) + printf("Count #%d: %d\n", (i / 10) + 1, gc("count")); + } + + // stop GC + gc("stop"); + + for (let i = 100; i < 200; i++) { + let o = {}; + o.cyle = o; + o = null; + + if ((i % 10) == 0) + printf("Count #%d: %d\n", (i / 10) + 1, gc("count")); + } +%} +-- End -- + +-- Expect stdout -- +Count #1: 6 +Count #2: 12 +Count #3: 12 +Count #4: 12 +Count #5: 12 +Count #6: 12 +Count #7: 12 +Count #8: 12 +Count #9: 12 +Count #10: 12 +Count #11: 12 +Count #12: 22 +Count #13: 32 +Count #14: 42 +Count #15: 52 +Count #16: 62 +Count #17: 72 +Count #18: 82 +Count #19: 92 +Count #20: 102 +-- End -- |