Compare commits

...

5 Commits

Author SHA1 Message Date
37e4653d40 don't freezeGC during GC cycle 2023-08-29 23:32:25 -05:00
1ae473383d fix more GC bugs 2023-08-29 23:21:52 -05:00
6ed5589513 fix cparse.c gc bug 2023-08-29 23:01:47 -05:00
1408a07b23 fix this test script 2023-08-29 16:51:04 -05:00
27818b3788 cosmoV_throw() now resets the vm stack as well
also a minor GC bug in cosmoO_newError was fixed.

i'm going to try to phase out cosmoM_freezeGC & friends
since that would cause hell with this new
error handling solution. the only thing still using it is the GC.
2023-08-29 16:48:38 -05:00
14 changed files with 74 additions and 70 deletions

View File

@ -6,7 +6,7 @@ end
// instance of test // instance of test
let obj = test() let obj = test()
test.__index = function(self, key) test.__index = func(self, key)
print("__index called!") print("__index called!")
if (key == "lol") then if (key == "lol") then
return 9001 return 9001

View File

@ -948,16 +948,9 @@ int cosmoB_vnewindexBProto(CState *state, int nargs, CValue *args)
// vm.collect() // vm.collect()
int cosmoB_vcollect(CState *state, int nargs, CValue *args) int cosmoB_vcollect(CState *state, int nargs, CValue *args)
{ {
// first, unfreeze the state (we start frozen on entry to any C Function)
cosmoM_unfreezeGC(state);
// now force a garbage collection // now force a garbage collection
cosmoM_collectGarbage(state); cosmoM_collectGarbage(state);
// and re-freeze the state
cosmoM_freezeGC(state);
// the end!
return 0; return 0;
} }

View File

@ -27,9 +27,9 @@ void initChunk(CState *state, CChunk *chunk, size_t startCapacity)
void cleanChunk(CState *state, CChunk *chunk) void cleanChunk(CState *state, CChunk *chunk)
{ {
// first, free the chunk buffer // first, free the chunk buffer
cosmoM_freearray(state, INSTRUCTION, chunk->buf, chunk->capacity); cosmoM_freeArray(state, INSTRUCTION, chunk->buf, chunk->capacity);
// then the line info // then the line info
cosmoM_freearray(state, int, chunk->lineInfo, chunk->capacity); cosmoM_freeArray(state, int, chunk->lineInfo, chunk->capacity);
// free the constants // free the constants
cleanValArray(state, &chunk->constants); cleanValArray(state, &chunk->constants);
} }
@ -61,8 +61,8 @@ int addConstant(CState *state, CChunk *chunk, CValue value)
void writeu8Chunk(CState *state, CChunk *chunk, INSTRUCTION i, int line) void writeu8Chunk(CState *state, CChunk *chunk, INSTRUCTION i, int line)
{ {
// does the buffer need to be reallocated? // does the buffer need to be reallocated?
cosmoM_growarray(state, INSTRUCTION, chunk->buf, chunk->count, chunk->capacity); cosmoM_growArray(state, INSTRUCTION, chunk->buf, chunk->count, chunk->capacity);
cosmoM_growarray(state, int, chunk->lineInfo, chunk->count, chunk->lineCapacity); cosmoM_growArray(state, int, chunk->lineInfo, chunk->count, chunk->lineCapacity);
// write data to the chunk :) // write data to the chunk :)
chunk->lineInfo[chunk->count] = line; chunk->lineInfo[chunk->count] = line;

View File

@ -54,7 +54,7 @@ static void resetBuffer(CLexState *state)
// cancels the token heap buffer and frees it // cancels the token heap buffer and frees it
static void freeBuffer(CLexState *state) static void freeBuffer(CLexState *state)
{ {
cosmoM_freearray(state->cstate, char, state->buffer, state->bufCap); cosmoM_freeArray(state->cstate, char, state->buffer, state->bufCap);
resetBuffer(state); resetBuffer(state);
} }
@ -62,7 +62,7 @@ static void freeBuffer(CLexState *state)
// adds character to buffer // adds character to buffer
static void appendBuffer(CLexState *state, char c) static void appendBuffer(CLexState *state, char c)
{ {
cosmoM_growarray(state->cstate, char, state->buffer, state->bufCount, state->bufCap); cosmoM_growArray(state->cstate, char, state->buffer, state->bufCount, state->bufCap);
state->buffer[state->bufCount++] = c; state->buffer[state->bufCount++] = c;
} }
@ -90,7 +90,7 @@ static char *cutBuffer(CLexState *state, int *length)
resetBuffer(state); resetBuffer(state);
// shrink the buffer to only use what we need // shrink the buffer to only use what we need
return cosmoM_reallocate(state->cstate, buf, cap, count); return cosmoM_reallocate(state->cstate, buf, cap, count, true);
} }
static CToken makeToken(CLexState *state, CTokenType type) static CToken makeToken(CLexState *state, CTokenType type)

View File

@ -8,7 +8,7 @@
#include "cvalue.h" #include "cvalue.h"
// realloc wrapper // realloc wrapper
void *cosmoM_reallocate(CState *state, void *buf, size_t oldSize, size_t newSize) void *cosmoM_reallocate(CState *state, void *buf, size_t oldSize, size_t newSize, bool isGC)
{ {
if (buf == NULL) if (buf == NULL)
oldSize = 0; oldSize = 0;
@ -34,18 +34,20 @@ void *cosmoM_reallocate(CState *state, void *buf, size_t oldSize, size_t newSize
return NULL; return NULL;
} }
if (isGC) {
#ifdef GC_STRESS #ifdef GC_STRESS
if (!(cosmoM_isFrozen(state)) && newSize > oldSize) { if (!(cosmoM_isFrozen(state)) && newSize > oldSize) {
cosmoM_collectGarbage(state); cosmoM_collectGarbage(state);
} }
# ifdef GC_DEBUG # ifdef GC_DEBUG
else { else {
printf("GC event ignored! state frozen! [%d]\n", state->freezeGC); printf("GC event ignored! state frozen! [%d]\n", state->freezeGC);
} }
# endif # endif
#else #else
cosmoM_checkGarbage(state, 0); cosmoM_checkGarbage(state, 0);
#endif #endif
}
// if NULL is passed, realloc() acts like malloc() // if NULL is passed, realloc() acts like malloc()
void *newBuf = realloc(buf, newSize); void *newBuf = realloc(buf, newSize);
@ -206,8 +208,8 @@ static void markObject(CState *state, CObj *obj)
return; return;
// we can use cosmoM_growarray because we lock the GC when we entered in cosmoM_collectGarbage // we can use cosmoM_growarray because we lock the GC when we entered in cosmoM_collectGarbage
cosmoM_growarray(state, CObj *, state->grayStack.array, state->grayStack.count, cosmoM_growArrayNonGC(state, CObj *, state->grayStack.array, state->grayStack.count,
state->grayStack.capacity); state->grayStack.capacity);
state->grayStack.array[state->grayStack.count++] = obj; state->grayStack.array[state->grayStack.count++] = obj;
} }
@ -300,8 +302,6 @@ 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);
tableRemoveWhite( tableRemoveWhite(
@ -312,9 +312,6 @@ COSMO_API void cosmoM_collectGarbage(CState *state)
// set our next GC event // set our next GC event
cosmoM_updateThreshhold(state); cosmoM_updateThreshhold(state);
state->freezeGC--; // we don't want to use cosmoM_unfreezeGC because that might trigger a GC
// event (if GC_STRESS is defined)
#ifdef GC_DEBUG #ifdef GC_DEBUG
printf("-- GC end, reclaimed %ld bytes (started at %ld, ended at %ld), next garbage collection " printf("-- GC end, reclaimed %ld bytes (started at %ld, ended at %ld), next garbage collection "
"scheduled at %ld bytes\n", "scheduled at %ld bytes\n",

View File

@ -12,28 +12,37 @@
#define ARRAY_START 8 #define ARRAY_START 8
#ifdef GC_DEBUG #ifdef GC_DEBUG
# define cosmoM_freearray(state, type, buf, capacity) \ # define cosmoM_freeArray(state, type, buf, capacity) \
printf("freeing array %p [size %lu] at %s:%d\n", buf, sizeof(type) * capacity, __FILE__, \ printf("freeing array %p [size %lu] at %s:%d\n", buf, sizeof(type) * capacity, __FILE__, \
__LINE__); \ __LINE__); \
cosmoM_reallocate(state, buf, sizeof(type) * capacity, 0) cosmoM_reallocate(state, buf, sizeof(type) * capacity, 0, true)
#else #else
# define cosmoM_freearray(state, type, buf, capacity) \ # define cosmoM_freeArray(state, type, buf, capacity) \
cosmoM_reallocate(state, buf, sizeof(type) * capacity, 0) cosmoM_reallocate(state, buf, sizeof(type) * capacity, 0, true)
#endif #endif
#define cosmoM_growarray(state, type, buf, count, capacity) \ #define cosmoM_growArray(state, type, buf, count, capacity) \
if (count >= capacity || buf == NULL) { \ if (count >= capacity || buf == NULL) { \
int old = capacity; \ int old = capacity; \
capacity = old * GROW_FACTOR; \ capacity = old * GROW_FACTOR; \
buf = (type *)cosmoM_reallocate(state, buf, sizeof(type) * old, sizeof(type) * capacity); \ buf = (type *)cosmoM_reallocate(state, buf, sizeof(type) * old, sizeof(type) * capacity, \
true); \
}
#define cosmoM_growArrayNonGC(state, type, buf, count, capacity) \
if (count >= capacity || buf == NULL) { \
int old = capacity; \
capacity = old * GROW_FACTOR; \
buf = (type *)cosmoM_reallocate(state, buf, sizeof(type) * old, sizeof(type) * capacity, \
false); \
} }
#ifdef GC_DEBUG #ifdef GC_DEBUG
# define cosmoM_free(state, type, x) \ # define cosmoM_free(state, type, x) \
printf("freeing %p [size %lu] at %s:%d\n", x, sizeof(type), __FILE__, __LINE__); \ printf("freeing %p [size %lu] at %s:%d\n", x, sizeof(type), __FILE__, __LINE__); \
cosmoM_reallocate(state, x, sizeof(type), 0) cosmoM_reallocate(state, x, sizeof(type), 0, true)
#else #else
# define cosmoM_free(state, type, x) cosmoM_reallocate(state, x, sizeof(type), 0) # define cosmoM_free(state, type, x) cosmoM_reallocate(state, x, sizeof(type), 0, true)
#endif #endif
#define cosmoM_isFrozen(state) (state->freezeGC > 0) #define cosmoM_isFrozen(state) (state->freezeGC > 0)
@ -60,7 +69,8 @@
#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,
bool isGC);
COSMO_API bool cosmoM_checkGarbage(CState *state, COSMO_API bool cosmoM_checkGarbage(CState *state,
size_t needed); // returns true if GC event was triggered size_t needed); // returns true if GC event was triggered
COSMO_API void cosmoM_collectGarbage(CState *state); COSMO_API void cosmoM_collectGarbage(CState *state);
@ -76,10 +86,7 @@ COSMO_API void cosmoM_removeRoot(CState *state, CObj *oldRoot);
// wrapper for cosmoM_reallocate so we can track our memory usage // wrapper for cosmoM_reallocate so we can track our memory usage
static inline void *cosmoM_xmalloc(CState *state, size_t sz) static inline void *cosmoM_xmalloc(CState *state, size_t sz)
{ {
return cosmoM_reallocate(state, NULL, 0, sz); return cosmoM_reallocate(state, NULL, 0, sz, true);
} }
// #define cosmoM_xmalloc(state, sz) \
// (printf("allocating new buffer at %s:%d of size %ld\n", __FILE__, __LINE__, sz), cosmoM_reallocate(state, NULL, 0, sz))
#endif #endif

View File

@ -46,7 +46,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 + 1); cosmoM_freeArray(state, char, objStr->str, objStr->length + 1);
cosmoM_free(state, CObjString, objStr); cosmoM_free(state, CObjString, objStr);
break; break;
} }
@ -82,13 +82,13 @@ void cosmoO_free(CState *state, CObj *obj)
} }
case COBJ_ERROR: { case COBJ_ERROR: {
CObjError *err = (CObjError *)obj; CObjError *err = (CObjError *)obj;
cosmoM_freearray(state, CCallFrame, err->frames, err->frameCount); cosmoM_freeArray(state, CCallFrame, err->frames, err->frameCount);
cosmoM_free(state, CObjError, obj); cosmoM_free(state, CObjError, obj);
break; break;
} }
case COBJ_CLOSURE: { case COBJ_CLOSURE: {
CObjClosure *closure = (CObjClosure *)obj; CObjClosure *closure = (CObjClosure *)obj;
cosmoM_freearray(state, CObjUpval *, closure->upvalues, closure->upvalueCount); cosmoM_freeArray(state, CObjUpval *, closure->upvalues, closure->upvalueCount);
cosmoM_free(state, CObjClosure, closure); cosmoM_free(state, CObjClosure, closure);
break; break;
} }
@ -229,14 +229,13 @@ CObjCFunction *cosmoO_newCFunction(CState *state, CosmoCFunction func)
CObjError *cosmoO_newError(CState *state, CValue err) CObjError *cosmoO_newError(CState *state, CValue err)
{ {
CCallFrame *frames = cosmoM_xmalloc(state, sizeof(CCallFrame) * state->frameCount);
CObjError *cerror = (CObjError *)cosmoO_allocateBase(state, sizeof(CObjError), COBJ_ERROR); CObjError *cerror = (CObjError *)cosmoO_allocateBase(state, sizeof(CObjError), COBJ_ERROR);
cerror->err = err; cerror->err = err;
cerror->frameCount = state->frameCount; cerror->frameCount = state->frameCount;
cerror->frames = frames;
cerror->parserError = false; cerror->parserError = false;
// allocate the callframe
cerror->frames = cosmoM_xmalloc(state, sizeof(CCallFrame) * cerror->frameCount);
// clone the call frame // clone the call frame
for (int i = 0; i < state->frameCount; i++) for (int i = 0; i < state->frameCount; i++)
cerror->frames[i] = state->callFrame[i]; cerror->frames[i] = state->callFrame[i];
@ -306,7 +305,7 @@ CObjString *cosmoO_takeString(CState *state, char *str, size_t length)
// have we already interned this string? // have we already interned this string?
if (lookup != NULL) { if (lookup != NULL) {
cosmoM_freearray(state, char, str, cosmoM_freeArray(state, char, str,
length + 1); // free our passed character array, it's unneeded! length + 1); // free our passed character array, it's unneeded!
return lookup; return lookup;
} }
@ -322,8 +321,7 @@ CObjString *cosmoO_allocateString(CState *state, const char *str, size_t sz, uin
strObj->length = sz; strObj->length = sz;
strObj->hash = hash; strObj->hash = hash;
// we push & pop the string so our GC can find it (we don't use freezeGC/unfreezeGC because we // push/pop to make sure GC doesn't collect it
// *want* a GC event to happen)
cosmoV_pushRef(state, (CObj *)strObj); cosmoV_pushRef(state, (CObj *)strObj);
cosmoT_insert(state, &state->strings, cosmoV_newRef((CObj *)strObj)); cosmoT_insert(state, &state->strings, cosmoV_newRef((CObj *)strObj));
cosmoV_pop(state); cosmoV_pop(state);
@ -762,6 +760,8 @@ const char *cosmoO_typeStr(CObj *obj)
return "<function>"; return "<function>";
case COBJ_CFUNCTION: case COBJ_CFUNCTION:
return "<c function>"; return "<c function>";
case COBJ_ERROR:
return "<error>";
case COBJ_METHOD: case COBJ_METHOD:
return "<method>"; return "<method>";
case COBJ_CLOSURE: case COBJ_CLOSURE:

View File

@ -64,9 +64,10 @@ typedef struct
CCompilerState *compiler; CCompilerState *compiler;
CObjString *module; // name of the module CObjString *module; // name of the module
CToken current; CToken current;
CToken previous; // token right after the current token CToken previous; // token right after the current token
int workingStackCount; // we push CValues of objects we need onto the stack so the garbage collector can see them. int workingStackCount; // we push CValues of objects we need onto the stack so the garbage
// this is the count of those values so we'll know how many to pop off when we're done // collector can see them. this is the count of those values so we'll
// know how many to pop off when we're done
} CParseState; } CParseState;
typedef enum typedef enum
@ -481,6 +482,7 @@ static void string(CParseState *pstate, bool canAssign, Precedence prec)
{ {
CObjString *strObj = CObjString *strObj =
cosmoO_takeString(pstate->state, pstate->previous.start, pstate->previous.length); cosmoO_takeString(pstate->state, pstate->previous.start, pstate->previous.length);
keepTrackOf(pstate, cosmoV_newRef((CObj *)strObj));
writeConstant(pstate, cosmoV_newRef((CObj *)strObj)); writeConstant(pstate, cosmoV_newRef((CObj *)strObj));
} }
@ -1369,7 +1371,7 @@ static void endLoop(CParseState *pstate)
patchJmp(pstate, pstate->compiler->loop.breaks[--pstate->compiler->loop.breakCount]); patchJmp(pstate, pstate->compiler->loop.breaks[--pstate->compiler->loop.breakCount]);
} }
cosmoM_freearray(pstate->state, int, pstate->compiler->loop.breaks, cosmoM_freeArray(pstate->state, int, pstate->compiler->loop.breaks,
pstate->compiler->loop.breakCapacity); pstate->compiler->loop.breakCapacity);
} }
@ -1658,7 +1660,7 @@ static void breakStatement(CParseState *pstate)
pstate->compiler->localCount = savedLocals; pstate->compiler->localCount = savedLocals;
// add break to loop // add break to loop
cosmoM_growarray(pstate->state, int, pstate->compiler->loop.breaks, cosmoM_growArray(pstate->state, int, pstate->compiler->loop.breaks,
pstate->compiler->loop.breakCount, pstate->compiler->loop.breakCapacity); pstate->compiler->loop.breakCount, pstate->compiler->loop.breakCapacity);
pstate->compiler->loop.breaks[pstate->compiler->loop.breakCount++] = writeJmp(pstate, OP_JMP); pstate->compiler->loop.breaks[pstate->compiler->loop.breakCount++] = writeJmp(pstate, OP_JMP);
} }

View File

@ -4,8 +4,7 @@
#include "clex.h" #include "clex.h"
#include "cosmo.h" #include "cosmo.h"
// compiles source into CChunk, if NULL is returned, a syntaxical error has occurred and pushed onto // compiles source into CChunk
// the stack
CObjFunction *cosmoP_compileString(CState *state, const char *source, const char *module); CObjFunction *cosmoP_compileString(CState *state, const char *source, const char *module);
#endif #endif

View File

@ -10,6 +10,8 @@
CPanic *cosmoV_newPanic(CState *state) CPanic *cosmoV_newPanic(CState *state)
{ {
CPanic *panic = cosmoM_xmalloc(state, sizeof(CPanic)); CPanic *panic = cosmoM_xmalloc(state, sizeof(CPanic));
panic->top = state->top;
panic->frameCount = state->frameCount;
panic->prev = state->panic; panic->prev = state->panic;
state->panic = panic; state->panic = panic;
@ -95,7 +97,6 @@ void cosmoV_freeState(CState *state)
#ifdef GC_DEBUG #ifdef GC_DEBUG
printf("state %p is being free'd!\n", state); printf("state %p is being free'd!\n", state);
#endif #endif
cosmoM_freezeGC(state);
// frees all the objects // frees all the objects
CObj *objs = state->objects; CObj *objs = state->objects;
@ -119,7 +120,7 @@ void cosmoV_freeState(CState *state)
cosmoT_clearTable(state, &state->strings); cosmoT_clearTable(state, &state->strings);
// free our gray stack & finally free the state structure // free our gray stack & finally free the state structure
cosmoM_freearray(state, CObj *, state->grayStack.array, state->grayStack.capacity); cosmoM_freeArray(state, CObj *, state->grayStack.array, state->grayStack.capacity);
#ifdef GC_DEBUG #ifdef GC_DEBUG
if (state->allocatedBytes != 0) { if (state->allocatedBytes != 0) {

View File

@ -43,6 +43,8 @@ typedef struct ArrayCObj
typedef struct CPanic typedef struct CPanic
{ {
jmp_buf jmp; jmp_buf jmp;
StkPtr top;
int frameCount;
struct CPanic *prev; struct CPanic *prev;
} CPanic; } CPanic;
@ -52,9 +54,9 @@ struct CState
int frameCount; int frameCount;
CPanic *panic; CPanic *panic;
CObj *objects; // tracks all of our allocated objects CObj *objects; // tracks all of our allocated objects
CObj *userRoots; // user definable roots, this holds CObjs that should be considered "roots", 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 // 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 ArrayCObj grayStack; // keeps track of which objects *haven't yet* been traversed in our GC, but
// *have been* found // *have been* found
size_t allocatedBytes; size_t allocatedBytes;

View File

@ -61,7 +61,7 @@ void cosmoT_addTable(CState *state, CTable *from, CTable *to)
void cosmoT_clearTable(CState *state, CTable *tbl) void cosmoT_clearTable(CState *state, CTable *tbl)
{ {
cosmoM_freearray(state, CTableEntry, tbl->table, cosmoT_getCapacity(tbl)); cosmoM_freeArray(state, CTableEntry, tbl->table, cosmoT_getCapacity(tbl));
} }
static uint32_t getObjectHash(CObj *obj) static uint32_t getObjectHash(CObj *obj)
@ -165,7 +165,7 @@ static void resizeTbl(CState *state, CTable *tbl, int newCapacity, bool canShrin
} }
// free the old table // free the old table
cosmoM_freearray(state, CTableEntry, tbl->table, oldCap); cosmoM_freeArray(state, CTableEntry, tbl->table, oldCap);
tbl->table = entries; tbl->table = entries;
tbl->capacityMask = newCapacity - 1; tbl->capacityMask = newCapacity - 1;

View File

@ -13,12 +13,12 @@ void initValArray(CState *state, CValueArray *val, size_t startCapacity)
void cleanValArray(CState *state, CValueArray *array) void cleanValArray(CState *state, CValueArray *array)
{ {
cosmoM_freearray(state, CValue, array->values, array->capacity); cosmoM_freeArray(state, CValue, array->values, array->capacity);
} }
void appendValArray(CState *state, CValueArray *array, CValue val) void appendValArray(CState *state, CValueArray *array, CValue val)
{ {
cosmoM_growarray(state, CValue, array->values, array->count, array->capacity); cosmoM_growArray(state, CValue, array->values, array->count, array->capacity);
array->values[array->count++] = val; array->values[array->count++] = val;
} }

View File

@ -118,11 +118,14 @@ void cosmoV_throw(CState *state)
StkPtr temp = cosmoV_getTop(state, 0); StkPtr temp = cosmoV_getTop(state, 0);
CObjError *error = cosmoO_newError(state, *temp); CObjError *error = cosmoO_newError(state, *temp);
// replace the value on the stack with the error CValue val = cosmoV_newRef((CObj *)cosmoO_newError(state, *temp));
*temp = cosmoV_newRef((CObj *)cosmoO_newError(state, *temp));
if (state->panic) { if (state->panic) {
state->top = state->panic->top;
state->frameCount = state->panic->frameCount;
cosmoV_pushValue(state, val);
longjmp(state->panic->jmp, 1); longjmp(state->panic->jmp, 1);
} else { } else {
cosmoV_pushValue(state, val);
fprintf(stderr, "Unhandled panic! "); fprintf(stderr, "Unhandled panic! ");
cosmoV_printError(state, error); cosmoV_printError(state, error);
exit(1); exit(1);