Cosmo/src/cmem.c

256 lines
7.3 KiB
C
Raw Normal View History

2020-10-28 05:16:30 +00:00
#include "cmem.h"
#include "cstate.h"
#include "cvalue.h"
#include "ctable.h"
#include "cparse.h"
#include "cobj.h"
2020-11-13 05:04:09 +00:00
#include "cbaselib.h"
2020-10-28 05:16:30 +00:00
/*
copy buffer to new larger buffer, and free the old buffer
*/
void *cosmoM_reallocate(CState* state, void *buf, size_t oldSize, size_t newSize) {
state->allocatedBytes += newSize - oldSize;
if (newSize == 0) { // it needs to be free'd
free(buf);
return NULL;
}
#ifdef GC_STRESS
if (!(cosmoM_isFrozen(state)) && newSize > oldSize) {
cosmoM_collectGarbage(state);
}
#ifdef GC_DEBUG
else {
printf("GC event ignored! state frozen! [%d]\n", state->freezeGC);
}
#endif
2020-10-28 05:16:30 +00:00
#else
2020-11-17 20:32:20 +00:00
cosmoM_checkGarbage(state, 0);
2020-10-28 05:16:30 +00:00
#endif
// otherwise just use realloc to do all the heavy lifting
void *newBuf = realloc(buf, newSize);
if (newBuf == NULL) {
CERROR("failed to allocate memory!");
exit(1);
}
return newBuf;
}
2020-11-17 20:32:20 +00:00
COSMO_API bool cosmoM_checkGarbage(CState *state, size_t needed) {
if (!(cosmoM_isFrozen(state)) && state->allocatedBytes + needed > state->nextGC) {
cosmoM_collectGarbage(state); // cya lol
return true;
}
return false;
}
2020-10-28 05:16:30 +00:00
void markObject(CState *state, CObj *obj);
void markValue(CState *state, CValue val);
void markTable(CState *state, CTable *tbl) {
if (tbl->table == NULL) // table is still being initialized
return;
for (int i = 0; i < tbl->capacity; i++) {
CTableEntry *entry = &tbl->table[i];
markValue(state, entry->key);
markValue(state, entry->val);
}
}
// free's white members from the table
void tableRemoveWhite(CState *state, CTable *tbl) {
if (tbl->table == NULL) // table is still being initialized
return;
for (int i = 0; i < tbl->capacity; i++) {
CTableEntry *entry = &tbl->table[i];
if (IS_OBJ(entry->key) && !(cosmoV_readObj(entry->key))->isMarked) { // if the key is a object and it's white (unmarked), remove it from the table
2020-11-17 09:10:55 +00:00
cosmoT_remove(state, tbl, entry->key);
2020-10-28 05:16:30 +00:00
}
}
cosmoT_checkShrink(state, tbl); // recovers the memory we're no longer using
2020-10-28 05:16:30 +00:00
}
void markArray(CState *state, CValueArray *array) {
for (int i = 0; i < array->count; i++) {
markValue(state, array->values[i]);
}
}
// mark all references associated with the object
void blackenObject(CState *state, CObj *obj) {
switch (obj->type) {
case COBJ_STRING:
case COBJ_CFUNCTION:
// stubbed
break;
2020-11-05 03:37:34 +00:00
case COBJ_OBJECT: {
// mark everything this object is keeping track of
CObjObject *cobj = (CObjObject*)obj;
markTable(state, &cobj->tbl);
2020-11-15 18:22:11 +00:00
markObject(state, (CObj*)cobj->proto);
2020-11-05 03:37:34 +00:00
break;
}
2020-10-28 05:16:30 +00:00
case COBJ_UPVALUE: {
markValue(state, ((CObjUpval*)obj)->closed);
break;
}
case COBJ_FUNCTION: {
CObjFunction *func = (CObjFunction*)obj;
markObject(state, (CObj*)func->name);
markArray(state, &func->chunk.constants);
break;
}
case COBJ_METHOD: {
CObjMethod *method = (CObjMethod*)obj;
2020-11-17 21:07:56 +00:00
markValue(state, method->func);
markObject(state, (CObj*)method->obj);
break;
}
2020-10-28 05:16:30 +00:00
case COBJ_CLOSURE: {
CObjClosure *closure = (CObjClosure*)obj;
markObject(state, (CObj*)closure->function);
// mark all upvalues
for (int i = 0; i < closure->upvalueCount; i++) {
markObject(state, (CObj*)closure->upvalues[i]);
}
break;
}
default:
printf("Unknown type in blackenObject with %p, type %d\n", obj, obj->type);
break;
}
}
void markObject(CState *state, CObj *obj) {
if (obj == NULL || obj->isMarked) // skip if NULL or already marked
return;
obj->isMarked = true;
#ifdef GC_DEBUG
printf("marking %p, [", obj);
printObject(obj);
printf("]\n");
#endif
// they don't need to be added to the gray stack, they don't reference any other CObjs
if (obj->type == COBJ_CFUNCTION || obj->type == COBJ_STRING)
return;
// we don't use cosmoM_growarray because we don't want to trigger another GC event while in the GC!
if (state->grayCount >= state->grayCapacity || state->grayStack == NULL) {
int old = state->grayCapacity;
state->grayCapacity = old * GROW_FACTOR;
state->grayStack = (CObj**)realloc(state->grayStack, sizeof(CObj*) * state->grayCapacity);
if (state->grayStack == NULL) {
CERROR("failed to allocate memory for grayStack!");
exit(1);
}
}
state->grayStack[state->grayCount++] = obj;
}
void markValue(CState *state, CValue val) {
if (IS_OBJ(val))
markObject(state, cosmoV_readObj(val));
}
// trace our gray references
void traceGrays(CState *state) {
while (state->grayCount > 0) {
CObj* obj = state->grayStack[--state->grayCount];
blackenObject(state, obj);
}
}
void sweep(CState *state) {
CObj *prev = NULL;
CObj *object = state->objects;
while (object != NULL) {
if (object->isMarked) { // skip over it
object->isMarked = false; // rest to white
prev = object;
object = object->next;
} else { // free it!
CObj *oldObj = object;
object = object->next;
if (prev == NULL) {
state->objects = object;
} else {
prev->next = object;
}
2020-11-06 01:53:55 +00:00
cosmoO_free(state, oldObj);
2020-10-28 05:16:30 +00:00
}
}
}
void markRoots(CState *state) {
// mark all values on the stack
for (StkPtr value = state->stack; value < state->top; value++) {
markValue(state, *value);
}
// mark all active callframe closures
for (int i = 0; i < state->frameCount; i++) {
markObject(state, (CObj*)state->callFrame[i].closure);
}
// mark all open upvalues
for (CObjUpval *upvalue = state->openUpvalues; upvalue != NULL; upvalue = upvalue->next) {
markObject(state, (CObj*)upvalue);
}
markTable(state, &state->globals);
2020-11-12 22:52:56 +00:00
// mark all internal strings
for (int i = 0; i < ISTRING_MAX; i++)
markObject(state, (CObj*)state->iStrings[i]);
2020-10-28 05:16:30 +00:00
2020-11-15 18:22:11 +00:00
// mark our proto object
markObject(state, (CObj*)state->protoObj);
2020-10-28 05:16:30 +00:00
traceGrays(state);
}
COSMO_API void cosmoM_collectGarbage(CState *state) {
#ifdef GC_DEBUG
printf("-- GC start\n");
size_t start = state->allocatedBytes;
#endif
2020-11-17 09:10:55 +00:00
cosmoM_freezeGC(state); // we don't want a recursive garbage collection event!
2020-10-28 05:16:30 +00:00
markRoots(state);
tableRemoveWhite(state, &state->strings); // make sure we aren't referencing any strings that are about to be free'd
// now finally, free all the unmarked objects
sweep(state);
// set our next GC event
2020-11-17 09:10:55 +00:00
cosmoM_updateThreshhold(state);
2020-10-28 05:16:30 +00:00
2020-11-17 09:10:55 +00:00
cosmoM_unfreezeGC(state);
2020-10-28 05:16:30 +00:00
#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);
2020-11-17 09:10:55 +00:00
getchar(); // pauses execution
2020-10-28 05:16:30 +00:00
#endif
2020-11-17 09:10:55 +00:00
}
COSMO_API void cosmoM_updateThreshhold(CState *state) {
state->nextGC = state->allocatedBytes * HEAP_GROW_FACTOR;
2020-10-28 05:16:30 +00:00
}