mirror of
https://github.com/CPunch/Cosmo.git
synced 2024-11-22 07:20:05 +00:00
fixed GC bug
This commit is contained in:
parent
204bec3d0a
commit
1329b72fcd
11
src/cmem.c
11
src/cmem.c
@ -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;
|
||||||
}
|
}
|
@ -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)
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
44
src/ctable.c
44
src/ctable.c
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user