diff --git a/src/cmem.c b/src/cmem.c index 034033e..01ef61b 100644 --- a/src/cmem.c +++ b/src/cmem.c @@ -191,6 +191,16 @@ void sweep(CState *state) { } } +void markUserRoots(CState *state) { + CObj *root = state->userRoots; + + // traverse userRoots and mark all the object + while (root != NULL) { + markObject(state, root); + root = root->nextRoot; + } +} + void markRoots(CState *state) { // mark all values on the stack for (StkPtr value = state->stack; value < state->top; value++) { @@ -213,6 +223,9 @@ void markRoots(CState *state) { for (int i = 0; i < ISTRING_MAX; i++) markObject(state, (CObj*)state->iStrings[i]); + // mark the user defined roots + markUserRoots(state); + // mark our proto object markObject(state, (CObj*)state->protoObj); traceGrays(state); @@ -234,7 +247,7 @@ COSMO_API void cosmoM_collectGarbage(CState *state) { // set our next GC event cosmoM_updateThreshhold(state); - cosmoM_unfreezeGC(state); + state->freezeGC--; // we don't want to use cosmoM_unfreezeGC because that might trigger a GC event #ifdef GC_DEBUG printf("-- GC end, reclaimed %ld bytes (started at %ld, ended at %ld), next garbage collection scheduled at %ld bytes\n", start - state->allocatedBytes, start, state->allocatedBytes, state->nextGC); @@ -244,4 +257,43 @@ COSMO_API void cosmoM_collectGarbage(CState *state) { COSMO_API void cosmoM_updateThreshhold(CState *state) { state->nextGC = state->allocatedBytes * HEAP_GROW_FACTOR; +} + +COSMO_API void cosmoM_addRoot(CState *state, CObj *newRoot) { + // first, check and make sure this root doesn't already exist in the list + CObj *root = state->userRoots; + while (root != NULL) { + if (root == newRoot) // found in the list, abort + return; + + root = root->nextRoot; + } + + // adds root to userRoot linked list + newRoot->nextRoot = state->userRoots; + state->userRoots = newRoot; +} + +COSMO_API void cosmoM_removeRoot(CState *state, CObj *oldRoot) { + CObj *prev = NULL; + CObj *root = state->userRoots; + + // traverse the userRoot linked list + while (root != NULL) { + if (root == oldRoot) { // found root in list + + // remove from the linked list + if (prev == NULL) { + state->userRoots = root->nextRoot; + } else { + prev->nextRoot = root->nextRoot; + } + + root->nextRoot = NULL; + break; + } + + prev = root; + root = root->nextRoot; + } } \ No newline at end of file diff --git a/src/cmem.h b/src/cmem.h index 2906427..c9e7cc4 100644 --- a/src/cmem.h +++ b/src/cmem.h @@ -39,9 +39,12 @@ printf("unfreezing state at %s:%d [%d]\n", __FILE__, __LINE__, state->freezeGC); \ cosmoM_checkGarbage(state, 0) #else + +// freeze's the garbage collector until cosmoM_unfreezeGC is called #define cosmoM_freezeGC(state) \ state->freezeGC++ +// unfreeze's the garbage collector and tries to run a garbage collection cycle #define cosmoM_unfreezeGC(state) \ state->freezeGC--; \ cosmoM_checkGarbage(state, 0) @@ -53,9 +56,13 @@ COSMO_API bool cosmoM_checkGarbage(CState *state, size_t needed); // returns tru COSMO_API void cosmoM_collectGarbage(CState *state); COSMO_API void cosmoM_updateThreshhold(CState *state); -/* - wrapper for cosmoM_reallocate so we can track our memory usage (it's also safer :P) -*/ +// lets the VM know you are holding a reference to a CObj and to not free it +COSMO_API void cosmoM_addRoot(CState *state, CObj *newRoot); + +// lets the VM know this root is no longer held in a reference and is able to be free'd +COSMO_API void cosmoM_removeRoot(CState *state, CObj *oldRoot); + +// wrapper for cosmoM_reallocate so we can track our memory usage (it's also safer :P) static inline void *cosmoM_xmalloc(CState *state, size_t sz) { return cosmoM_reallocate(state, NULL, 0, sz); } diff --git a/src/cobj.c b/src/cobj.c index dc3ad72..c0650e6 100644 --- a/src/cobj.c +++ b/src/cobj.c @@ -24,6 +24,8 @@ CObj *cosmoO_allocateBase(CState *state, size_t sz, CObjType type) { obj->next = state->objects; state->objects = obj; + + obj->nextRoot = NULL; #ifdef GC_DEBUG printf("allocated %p with OBJ_TYPE %d\n", obj, type); #endif diff --git a/src/cobj.h b/src/cobj.h index 3f7a25f..1550526 100644 --- a/src/cobj.h +++ b/src/cobj.h @@ -31,6 +31,7 @@ typedef struct CObj { CObjType type; bool isMarked; // for the GC struct CObj *next; + struct CObj *nextRoot; // for the root linked list } CObj; typedef struct CObjString { diff --git a/src/cstate.c b/src/cstate.c index cb2544c..e68d793 100644 --- a/src/cstate.c +++ b/src/cstate.c @@ -20,6 +20,7 @@ CState *cosmoV_newState() { // GC state->objects = NULL; + state->userRoots = NULL; state->grayStack.count = 0; state->grayStack.capacity = 2; state->grayStack.array = NULL; diff --git a/src/cstate.h b/src/cstate.h index b65529e..d38acde 100644 --- a/src/cstate.h +++ b/src/cstate.h @@ -32,6 +32,7 @@ typedef struct CState { bool panic; int freezeGC; // when > 0, GC events will be ignored (for internal use) CObj *objects; // tracks all of our allocated objects + CObj *userRoots; // user definable roots, this holds CObjs that should be considered "roots", lets the VM know you are holding a reference to a CObj in your code ArrayCObj grayStack; // keeps track of which objects *haven't yet* been traversed in our GC, but *have been* found size_t allocatedBytes; size_t nextGC; // when allocatedBytes reaches this threshhold, trigger a GC event diff --git a/src/cvalue.h b/src/cvalue.h index 08613de..7d6b325 100644 --- a/src/cvalue.h +++ b/src/cvalue.h @@ -18,13 +18,15 @@ typedef double cosmo_Number; #ifdef NAN_BOXXED /* - NaN box, this is great for performance on x86_64 or ARM64 architectures. If you don't know how this works please reference these + NaN box, this is great for fitting more in the cpu cache on x86_64 or ARM64 architectures. If you don't know how this works please reference these two articles: https://leonardschuetz.ch/blog/nan-boxing/ and https://piotrduperas.com/posts/nan-boxing/ both are great resources :) + Performance notes: this can actually degrade performance, so only enable if you know what you're doing. + TL;DR: we can store payloads in the NaN value in the IEEE 754 standard. */ typedef union CValue { diff --git a/src/main.c b/src/main.c index e3edafc..0eb160e 100644 --- a/src/main.c +++ b/src/main.c @@ -30,7 +30,6 @@ CValue cosmoB_input(CState *state, int nargs, CValue *args) { } static void interpret(CState *state, const char* script) { - // cosmoP_compileString pushes the result onto the stack (NIL or COBJ_FUNCTION) CObjFunction* func = cosmoP_compileString(state, script); if (func != NULL) {