1
0
mirror of https://github.com/janet-lang/janet synced 2024-06-26 07:03:16 +00:00
janet/gc.c
2017-02-09 15:02:59 -05:00

210 lines
6.4 KiB
C

#include <stdlib.h>
#include "gc.h"
#include "dict.h"
#include "vstring.h"
#include "parse.h"
#define GCHeader(mem) ((GCMemoryHeader *)(mem) - 1)
typedef struct GCMemoryHeader GCMemoryHeader;
struct GCMemoryHeader {
GCMemoryHeader * next;
uint32_t color;
};
/* Initialize a garbage collector */
void GCInit(GC * gc, uint32_t memoryInterval) {
gc->black = 0;
gc->blocks = NULL;
gc->nextCollection = 0;
gc->memoryInterval = memoryInterval;
gc->handleOutOfMemory = NULL;
}
/* Mark some raw memory */
void GCMarkMemory(GC * gc, void * memory) {
GCHeader(memory)->color = gc->black;
}
/* Helper to mark function environments */
static void GCMarkFuncEnv(GC * gc, FuncEnv * env) {
if (GCHeader(env)->color != gc->black) {
Value temp;
GCHeader(env)->color = gc->black;
if (env->thread) {
temp.type = TYPE_THREAD;
temp.data.array = env->thread;
GCMark(gc, &temp);
} else {
uint32_t count = env->stackOffset;
uint32_t i;
GCHeader(env->values)->color = gc->black;
for (i = 0; i < count; ++i) {
GCMark(gc, env->values + i);
}
}
}
}
/* Mark allocated memory associated with a value. This is
* the main function for doing garbage collection. */
void GCMark(GC * gc, Value * x) {
switch (x->type) {
case TYPE_NIL:
case TYPE_BOOLEAN:
case TYPE_NUMBER:
case TYPE_CFUNCTION:
break;
case TYPE_STRING:
case TYPE_SYMBOL:
GCHeader(VStringRaw(x->data.string))->color = gc->black;
break;
case TYPE_BYTEBUFFER:
GCHeader(x->data.buffer)->color = gc->black;
GCHeader(x->data.buffer->data)->color = gc->black;
break;
case TYPE_ARRAY:
case TYPE_FORM:
if (GCHeader(x->data.array)->color != gc->black) {
uint32_t i, count;
count = x->data.array->count;
GCHeader(x->data.array)->color = gc->black;
GCHeader(x->data.array->data)->color = gc->black;
for (i = 0; i < count; ++i)
GCMark(gc, x->data.array->data + i);
}
break;
case TYPE_THREAD:
if (GCHeader(x->data.array)->color != gc->black) {
uint32_t i, count;
count = x->data.array->count;
GCHeader(x->data.array)->color = gc->black;
GCHeader(x->data.array->data)->color = gc->black;
if (count) {
count += FrameSize(x->data.array);
for (i = 0; i < count; ++i)
GCMark(gc, x->data.array->data + i);
}
}
break;
case TYPE_FUNCTION:
if (GCHeader(x->data.func)->color != gc->black) {
Func * f = x->data.func;
GCHeader(f)->color = gc->black;
GCMarkFuncEnv(gc, f->env);
{
Value temp;
temp.type = TYPE_FUNCDEF;
temp.data.funcdef = x->data.funcdef;
GCMark(gc, &temp);
if (f->parent) {
temp.type = TYPE_FUNCTION;
temp.data.func = f->parent;
GCMark(gc, &temp);
}
}
}
break;
case TYPE_DICTIONARY:
if (GCHeader(x->data.dict)->color != gc->black) {
DictionaryIterator iter;
DictBucket * bucket;
GCHeader(x->data.dict)->color = gc->black;
GCHeader(x->data.dict->buckets)->color = gc->black;
DictIterate(x->data.dict, &iter);
while (DictIterateNext(&iter, &bucket)) {
GCHeader(bucket)->color = gc->black;
GCMark(gc, &bucket->key);
GCMark(gc, &bucket->value);
}
}
break;
case TYPE_FUNCDEF:
if (GCHeader(x->data.funcdef)->color != gc->black) {
GCHeader(x->data.funcdef->byteCode)->color = gc->black;
uint32_t count, i;
count = x->data.funcdef->literalsLen;
if (x->data.funcdef->literals) {
GCHeader(x->data.funcdef->literals)->color = gc->black;
for (i = 0; i < count; ++i)
GCMark(gc, x->data.funcdef->literals + i);
}
}
break;
case TYPE_FUNCENV:
GCMarkFuncEnv(gc, x->data.funcenv);
break;
}
}
/* Iterate over all allocated memory, and free memory that is not
* marked as reachable. Flip the gc color flag for next sweep. */
void GCSweep(GC * gc) {
GCMemoryHeader * previous = NULL;
GCMemoryHeader * current = gc->blocks;
while (current) {
if (current->color != gc->black) {
if (previous) {
previous->next = current->next;
} else {
gc->blocks = current->next;
}
free(current);
} else {
previous = current;
}
current = current->next;
}
/* Rotate flag */
gc->black = !gc->black;
}
/* Clean up all memory */
void GCClear(GC * gc) {
GCMemoryHeader * current = gc->blocks;
while (current) {
GCMemoryHeader * next = current->next;
free(current);
current = next;
}
gc->blocks = NULL;
}
/* Prepare a memory block */
static void * GCPrepare(GC * gc, char * rawBlock, uint32_t size) {
GCMemoryHeader * mdata;
if (rawBlock == NULL) {
if (gc->handleOutOfMemory != NULL)
gc->handleOutOfMemory(gc);
return NULL;
}
gc->nextCollection += size;
mdata = (GCMemoryHeader *) rawBlock;
mdata->next = gc->blocks;
gc->blocks = mdata;
mdata->color = !gc->black;
return rawBlock + sizeof(GCMemoryHeader);
}
/* Allocate some memory that is tracked for garbage collection */
void * GCAlloc(GC * gc, uint32_t size) {
uint32_t totalSize = size + sizeof(GCMemoryHeader);
return GCPrepare(gc, malloc(totalSize), totalSize);
}
/* Allocate some zeroed memory that is tracked for garbage collection */
void * GCZalloc(GC * gc, uint32_t size) {
uint32_t totalSize = size + sizeof(GCMemoryHeader);
return GCPrepare(gc, calloc(1, totalSize), totalSize);
}