fixed GC bug

This commit is contained in:
CPunch 2020-11-17 03:10:55 -06:00
parent 204bec3d0a
commit 1329b72fcd
5 changed files with 56 additions and 9 deletions

View File

@ -66,7 +66,7 @@ void tableRemoveWhite(CState *state, CTable *tbl) {
for (int i = 0; i < tbl->capacity; i++) { for (int i = 0; i < tbl->capacity; i++) {
CTableEntry *entry = &tbl->table[i]; CTableEntry *entry = &tbl->table[i];
if (IS_OBJ(entry->key) && !(entry->key.val.obj)->isMarked) { // if the key is a object and it's white (unmarked), remove it from the table if (IS_OBJ(entry->key) && !(entry->key.val.obj)->isMarked) { // if the key is a object and it's white (unmarked), remove it from the table
cosmoT_remove(tbl, entry->key); cosmoT_remove(state, tbl, entry->key);
} }
} }
} }
@ -224,6 +224,7 @@ COSMO_API void cosmoM_collectGarbage(CState *state) {
printf("-- GC start\n"); printf("-- GC start\n");
size_t start = state->allocatedBytes; size_t start = state->allocatedBytes;
#endif #endif
cosmoM_freezeGC(state); // we don't want a recursive garbage collection event!
markRoots(state); markRoots(state);
@ -232,10 +233,16 @@ COSMO_API void cosmoM_collectGarbage(CState *state) {
sweep(state); sweep(state);
// set our next GC event // set our next GC event
state->nextGC = state->allocatedBytes * HEAP_GROW_FACTOR; cosmoM_updateThreshhold(state);
cosmoM_unfreezeGC(state);
#ifdef GC_DEBUG #ifdef GC_DEBUG
printf("-- GC end, reclaimed %ld bytes (started at %ld, ended at %ld), next garbage collection scheduled at %ld bytes\n", 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); start - state->allocatedBytes, start, state->allocatedBytes, state->nextGC);
getchar(); // pauses execution
#endif #endif
} }
COSMO_API void cosmoM_updateThreshhold(CState *state) {
state->nextGC = state->allocatedBytes * HEAP_GROW_FACTOR;
}

View File

@ -45,8 +45,9 @@
state->freezeGC-- state->freezeGC--
#endif #endif
COSMO_API void *cosmoM_reallocate(CState* state, void *buf, size_t oldSize, size_t newSize); COSMO_API void *cosmoM_reallocate(CState *state, void *buf, size_t oldSize, size_t newSize);
COSMO_API void cosmoM_collectGarbage(CState* state); 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) wrapper for cosmoM_reallocate so we can track our memory usage (it's also safer :P)

View File

@ -39,7 +39,7 @@ void cosmoO_free(CState *state, CObj* obj) {
switch(obj->type) { switch(obj->type) {
case COBJ_STRING: { case COBJ_STRING: {
CObjString *objStr = (CObjString*)obj; CObjString *objStr = (CObjString*)obj;
cosmoM_freearray(state, char, objStr->str, objStr->length); cosmoM_freearray(state, char, objStr->str, objStr->length + 1);
cosmoM_free(state, CObjString, objStr); cosmoM_free(state, CObjString, objStr);
break; break;
} }

View File

@ -6,10 +6,27 @@
#include <string.h> #include <string.h>
#define MAX_TABLE_FILL 0.75 #define MAX_TABLE_FILL 0.75
// at 30% capacity with capacity > ARRAY_START, shrink the array
#define MIN_TABLE_CAPACITY ARRAY_START
// bit-twiddling hacks, gets the next power of 2
unsigned int nextPow2(unsigned int x) {
if (x <= ARRAY_START - 1) return ARRAY_START; // sanity check
x--;
int power = 2;
while (x >>= 1) power <<= 1;
if (power < ARRAY_START)
return ARRAY_START;
return power;
}
void cosmoT_initTable(CState *state, CTable *tbl, int startCap) { void cosmoT_initTable(CState *state, CTable *tbl, int startCap) {
tbl->capacity = startCap != 0 ? startCap : ARRAY_START; // sanity check :P tbl->capacity = startCap != 0 ? startCap : ARRAY_START; // sanity check :P
tbl->count = 0; tbl->count = 0;
tbl->tombstones = 0;
tbl->table = NULL; // to let out GC know we're initalizing tbl->table = NULL; // to let out GC know we're initalizing
tbl->table = cosmoM_xmalloc(state, sizeof(CTableEntry) * tbl->capacity); tbl->table = cosmoM_xmalloc(state, sizeof(CTableEntry) * tbl->capacity);
@ -90,8 +107,26 @@ static CTableEntry *findEntry(CTableEntry *entries, int mask, CValue key) {
} }
} }
static void growTbl(CState *state, CTable *tbl, size_t newCapacity) { static void resizeTbl(CState *state, CTable *tbl, size_t newCapacity) {
CTableEntry *entries = cosmoM_xmalloc(state, sizeof(CTableEntry) * newCapacity); CTableEntry *entries = cosmoM_xmalloc(state, sizeof(CTableEntry) * newCapacity);
/* Before someone asks, no we shouldn't move the tombstone check to before the entries allocation.
The garbage collector is threshhold based, based on the currently allocated bytes. There's an
edgecase where if GC_STRESS is not enabled the GC will really only be called on growth of the
string interning table (this.) However the new size of the table is accounted for in the next threshhold
cycle, causing allocations to become less and less frequent until your computer develops dementia.
*/
// if count > 8 and active entries < tombstones
if (tbl->count > MIN_TABLE_CAPACITY && tbl->count - tbl->tombstones < tbl->tombstones) {
cosmoM_freearray(state, CTableEntry, entries, newCapacity);
int tombs = tbl->tombstones;
tbl->tombstones = 0;
resizeTbl(state, tbl, nextPow2((tbl->count - tombs) * GROW_FACTOR));
cosmoM_updateThreshhold(state); // force a threshhold update since this *could* be such a huge memory difference
return;
}
int newCount = 0; int newCount = 0;
// set all nodes as NIL : NIL // set all nodes as NIL : NIL
@ -119,14 +154,16 @@ static void growTbl(CState *state, CTable *tbl, size_t newCapacity) {
tbl->table = entries; tbl->table = entries;
tbl->capacity = newCapacity; tbl->capacity = newCapacity;
tbl->count = newCount; tbl->count = newCount;
tbl->tombstones = 0;
} }
// returns a pointer to the allocated value // returns a pointer to the allocated value
COSMO_API CValue* cosmoT_insert(CState *state, CTable *tbl, CValue key) { COSMO_API CValue* cosmoT_insert(CState *state, CTable *tbl, CValue key) {
// make sure we have enough space allocated // make sure we have enough space allocated
if (tbl->count + 1 > (int)(tbl->capacity * MAX_TABLE_FILL)) { if (tbl->count + 1 > (int)(tbl->capacity * MAX_TABLE_FILL)) {
// grow table
int newCap = tbl->capacity * GROW_FACTOR; int newCap = tbl->capacity * GROW_FACTOR;
growTbl(state, tbl, newCap); resizeTbl(state, tbl, newCap);
} }
// insert into the table // insert into the table
@ -151,7 +188,7 @@ bool cosmoT_get(CTable *tbl, CValue key, CValue *val) {
return !(IS_NIL(entry->key)); return !(IS_NIL(entry->key));
} }
bool cosmoT_remove(CTable *tbl, CValue key) { bool cosmoT_remove(CState* state, CTable *tbl, CValue key) {
if (tbl->count == 0) return 0; // sanity check if (tbl->count == 0) return 0; // sanity check
CTableEntry *entry = findEntry(tbl->table, tbl->capacity - 1, key); CTableEntry *entry = findEntry(tbl->table, tbl->capacity - 1, key);
@ -161,6 +198,7 @@ bool cosmoT_remove(CTable *tbl, CValue key) {
// crafts tombstone // crafts tombstone
entry->key = cosmoV_newNil(); // this has to be nil entry->key = cosmoV_newNil(); // this has to be nil
entry->val = cosmoV_newBoolean(false); // doesn't reall matter what this is, as long as it isn't nil entry->val = cosmoV_newBoolean(false); // doesn't reall matter what this is, as long as it isn't nil
tbl->tombstones++;
return true; return true;
} }

View File

@ -12,6 +12,7 @@ typedef struct CTableEntry {
typedef struct CTable { typedef struct CTable {
int count; int count;
int capacity; int capacity;
int tombstones;
CTableEntry *table; CTableEntry *table;
} CTable; } CTable;
@ -22,7 +23,7 @@ COSMO_API CValue *cosmoT_insert(CState *state, CTable *tbl, CValue key);
CObjString *cosmoT_lookupString(CTable *tbl, const char *str, size_t length, uint32_t hash); CObjString *cosmoT_lookupString(CTable *tbl, const char *str, size_t length, uint32_t hash);
bool cosmoT_get(CTable *tbl, CValue key, CValue *val); bool cosmoT_get(CTable *tbl, CValue key, CValue *val);
bool cosmoT_remove(CTable *tbl, CValue key); bool cosmoT_remove(CState *state, CTable *tbl, CValue key);
void cosmoT_printTable(CTable *tbl, const char *name); void cosmoT_printTable(CTable *tbl, const char *name);