diff --git a/build.lua b/build.lua index 18a6e9f..a07e965 100755 --- a/build.lua +++ b/build.lua @@ -68,6 +68,7 @@ local files = { 'src/parser.c', 'src/compiler.c', 'src/vm.c', + 'src/gc.c', 'src/main.c', } diff --git a/build.zig b/build.zig index a43cdb6..1a9e0d6 100644 --- a/build.zig +++ b/build.zig @@ -22,6 +22,7 @@ pub fn build(b: *std.Build) void { "src/parser.c", "src/compiler.c", "src/vm.c", + "src/gc.c", "src/error.c", "src/main.c", } diff --git a/src/gc.c b/src/gc.c new file mode 100644 index 0000000..eb0165c --- /dev/null +++ b/src/gc.c @@ -0,0 +1,89 @@ +#include "noom.h" +#include "vm.h" + +static void noomG_grayObj(noom_LuaVM *vm, noomV_Object *obj); + +static void noomG_grayValue(noom_LuaVM *vm, noomV_Value value) { + if(value.tag != NOOMV_VOBJ) return; + noomG_grayObj(vm, value.obj); +} + +static void noomG_grayObj(noom_LuaVM *vm, noomV_Object *obj) { + if(obj == 0) return; + if(obj->marked) return; + obj->marked = 1; + obj->nextGray = vm->graySet; + vm->graySet = obj; +} + +void noomG_markObj(noom_LuaVM *vm, noomV_Object *obj) { + if(obj->tag == NOOMV_OPOINTER) { + noomV_Pointer *p = (noomV_Pointer *)obj; + noomG_grayValue(vm, p->value); + return; + } + if(obj->tag == NOOMV_OFUNC) { + noomV_Function *f = (noomV_Function *)obj; + noomG_grayObj(vm, &f->chunkname->obj); + noomG_grayObj(vm, &f->env->obj); + for(noom_uint_t i = 0; i < f->constsize; i++) { + noomG_grayValue(vm, f->consts[i]); + } + for(noom_uint_t i = 0; i < f->protosize; i++) { + noomG_grayObj(vm, (noomV_Object *)f->protos[i]); + } + return; + } + if(obj->tag == NOOMV_OTABLE) { + noomV_Table *t = (noomV_Table *)obj; + noomG_grayObj(vm, (noomV_Object *)t->meta); + for(noom_uint_t i = 0; i < t->entries*2; i++) { + noomG_grayValue(vm, t->entrydata[i]); + } + return; + } + if(obj->tag == NOOMV_OTHREAD) { + noomV_Thread *thrd = (noomV_Thread *)obj; + for(noom_uint_t i = 0; i < thrd->stacklen; i++) { + noomG_grayValue(vm, thrd->stack[i]); + } + for(noom_uint_t i = 0; i < thrd->calldepth; i++) { + noomV_CallFrame cf = thrd->calls[i]; + noomG_grayValue(vm, cf.errhandler); + } + noomG_grayValue(vm, thrd->errObj); + noomG_grayObj(vm, (noomV_Object *)thrd->resuming); + noomG_grayObj(vm, (noomV_Object *)thrd->resumedBy); + return; + } +} + +void noom_gc(noom_LuaVM* vm) { + vm->graySet = 0; + + noomG_grayObj(vm, (noomV_Object *)vm->mainThread); + noomG_grayObj(vm, (noomV_Object *)vm->globals); + noomG_grayObj(vm, (noomV_Object *)vm->registry); + + while(vm->graySet) { + noomG_markObj(vm, vm->graySet); + vm->graySet = vm->graySet->nextGray; + } + + noomV_Object **sweep = &vm->heap; + while(*sweep) { + noomV_Object *current = *sweep; + if(current->marked) { + sweep = ¤t->next; + } else { + *sweep = current->next; + noomV_freeObj(vm, current); + } + } + + vm->gcTarget = vm->objCount * vm->gcRatio; +} + +void noom_trygc(noom_LuaVM* vm) { + if(vm->objCount >= vm->gcTarget) noom_gc(vm); +} diff --git a/src/noom.h b/src/noom.h index 055929c..4c6e8ed 100644 --- a/src/noom.h +++ b/src/noom.h @@ -299,6 +299,11 @@ noom_Exit noom_cast2str(noom_LuaVM* vm, noom_slot_t x); // DO NOT RUN IN THE ALLOCATOR IF ANY IS SUPPLIED, OR YOU WILL GET THE WORST POSSIBLE ERRORS. void noom_gc(noom_LuaVM* vm); +// Like [noom_gc], except it will only do things if the GC feels like it. +// You can sneak this in your long-running C functions to reduce memory usage +// without destroying this CPU. +void noom_trygc(noom_LuaVM* vm); + #ifdef __cplusplus } #endif diff --git a/src/vm.c b/src/vm.c index 5964771..f166823 100644 --- a/src/vm.c +++ b/src/vm.c @@ -163,6 +163,7 @@ noomV_Object* noomV_allocObj(noom_LuaVM* vm, noomV_ObjTag tag, noom_uint_t size) o->next = vm->heap; o->nextGray = 0; vm->heap = o; + vm->objCount++; return o; } @@ -444,7 +445,7 @@ noom_Exit noomV_setErrorFromExit(noom_LuaVM *vm, noomV_Thread *coro, noom_Exit e return exit; } -void noomV_freeObj(noomV_Object* obj) { +void noomV_freeObj(noom_LuaVM *vm, noomV_Object* obj) { if(obj->tag == NOOMV_OFUNC) { noomV_Function *f = (noomV_Function *)obj; noom_free(f->consts); @@ -462,6 +463,7 @@ void noomV_freeObj(noomV_Object* obj) { noom_free(thrd->calls); } noom_free(obj); + vm->objCount--; } noom_Exit noomV_pushCallFrame(noom_LuaVM *vm, noomV_Thread *coro, noomV_CallFrame cf); @@ -513,6 +515,9 @@ noom_LuaVM* noom_createVM(noom_LuaVersion version) { vm->mainThread = 0; vm->currentThread = 0; vm->version = version; + vm->objCount = 0; + vm->gcTarget = 100; + vm->gcRatio = 2; const char *oomStr = "out of memory"; vm->oomVal.obj = (noomV_Object *)noomV_allocStr(vm, oomStr, noom_strlen(oomStr)); @@ -539,6 +544,8 @@ noom_LuaVM* noom_createVM(noom_LuaVersion version) { assert(noomV_rawequalValue(noomV_rawgetTable(vm->registry, _GKey), _GVal)); + noom_gc(vm); + return vm; rip: noom_destroyVM(vm); @@ -550,7 +557,7 @@ void noom_destroyVM(noom_LuaVM* vm) { while (iter) { noomV_Object* cur = iter; iter = iter->next; - noomV_freeObj(cur); + noomV_freeObj(vm, cur); } noom_free(vm); } diff --git a/src/vm.h b/src/vm.h index 09a23d0..f9e7d9c 100644 --- a/src/vm.h +++ b/src/vm.h @@ -352,6 +352,9 @@ struct noom_LuaVM { noomV_Thread* mainThread; noomV_Thread* currentThread; noom_LuaVersion version; + noom_uint_t objCount; + noom_uint_t gcTarget; + double gcRatio; }; // for conveniently passing around nil @@ -379,7 +382,7 @@ void noomV_setErrorStr(noom_LuaVM *vm, noomV_Thread *coro, const char *str); // Returns the exit for convenience noom_Exit noomV_setErrorFromExit(noom_LuaVM *vm, noomV_Thread *coro, noom_Exit exit); -void noomV_freeObj(noomV_Object* obj); +void noomV_freeObj(noom_LuaVM *vm, noomV_Object* obj); noom_Exit noomV_pushCallFrame(noom_LuaVM *vm, noomV_Thread *coro, noomV_CallFrame cf);