1
0
mirror of https://github.com/janet-lang/janet synced 2024-11-13 04:09:54 +00:00
janet/core/gc.c

343 lines
11 KiB
C
Raw Normal View History

/*
* Copyright (c) 2017 Calvin Rose
2017-07-02 01:51:16 +00:00
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
2017-07-02 01:51:16 +00:00
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
2017-07-02 01:51:16 +00:00
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <dst/dst.h>
#include "symcache.h"
/* GC State */
void *dst_vm_blocks;
uint32_t dst_vm_memory_interval;
uint32_t dst_vm_next_collection;
2017-11-01 21:53:43 +00:00
/* Helpers for marking the various gc types */
static void dst_mark_funcenv(DstFuncEnv *env);
static void dst_mark_funcdef(DstFuncDef *def);
2017-11-01 21:53:43 +00:00
static void dst_mark_function(DstFunction *func);
static void dst_mark_array(DstArray *array);
static void dst_mark_table(DstTable *table);
static void dst_mark_struct(const DstValue *st);
static void dst_mark_tuple(const DstValue *tuple);
static void dst_mark_buffer(DstBuffer *buffer);
static void dst_mark_string(const uint8_t *str);
static void dst_mark_fiber(DstFiber *fiber);
2017-11-01 21:53:43 +00:00
static void dst_mark_udata(void *udata);
/* Mark a value */
void dst_mark(DstValue x) {
switch (dst_type(x)) {
2017-11-01 21:53:43 +00:00
default: break;
case DST_STRING:
case DST_SYMBOL: dst_mark_string(dst_unwrap_string(x)); break;
case DST_FUNCTION: dst_mark_function(dst_unwrap_function(x)); break;
case DST_ARRAY: dst_mark_array(dst_unwrap_array(x)); break;
case DST_TABLE: dst_mark_table(dst_unwrap_table(x)); break;
case DST_STRUCT: dst_mark_struct(dst_unwrap_struct(x)); break;
case DST_TUPLE: dst_mark_tuple(dst_unwrap_tuple(x)); break;
case DST_BUFFER: dst_mark_buffer(dst_unwrap_buffer(x)); break;
case DST_FIBER: dst_mark_fiber(dst_unwrap_fiber(x)); break;
case DST_USERDATA: dst_mark_udata(dst_unwrap_pointer(x)); break;
2017-11-01 21:53:43 +00:00
}
}
/* Pin a value. This prevents a value from being garbage collected.
* Needed if the valueis not accesible to the garbage collector, but
* still in use by the program. For example, a c function that
* creates a table, and then runs the garbage collector without
* ever saving the table anywhere (except on the c stack, which
* the gc cannot inspect). */
2017-11-01 21:53:43 +00:00
void dst_pin(DstValue x) {
switch (dst_type(x)) {
2017-11-01 21:53:43 +00:00
default: break;
case DST_STRING:
case DST_SYMBOL: dst_pin_string(dst_unwrap_string(x)); break;
case DST_FUNCTION: dst_pin_function(dst_unwrap_function(x)); break;
case DST_ARRAY: dst_pin_array(dst_unwrap_array(x)); break;
case DST_TABLE: dst_pin_table(dst_unwrap_table(x)); break;
case DST_STRUCT: dst_pin_struct(dst_unwrap_struct(x)); break;
case DST_TUPLE: dst_pin_tuple(dst_unwrap_tuple(x)); break;
case DST_BUFFER: dst_pin_buffer(dst_unwrap_buffer(x)); break;
case DST_FIBER: dst_pin_fiber(dst_unwrap_fiber(x)); break;
case DST_USERDATA: dst_pin_userdata(dst_unwrap_pointer(x)); break;
2017-11-01 21:53:43 +00:00
}
}
/* Unpin a value. This enables the GC to collect the value's
* memory again. */
void dst_unpin(DstValue x) {
switch (dst_type(x)) {
default: break;
case DST_STRING:
case DST_SYMBOL: dst_unpin_string(dst_unwrap_string(x)); break;
case DST_FUNCTION: dst_unpin_function(dst_unwrap_function(x)); break;
case DST_ARRAY: dst_unpin_array(dst_unwrap_array(x)); break;
case DST_TABLE: dst_unpin_table(dst_unwrap_table(x)); break;
case DST_STRUCT: dst_unpin_struct(dst_unwrap_struct(x)); break;
case DST_TUPLE: dst_unpin_tuple(dst_unwrap_tuple(x)); break;
case DST_BUFFER: dst_unpin_buffer(dst_unwrap_buffer(x)); break;
case DST_FIBER: dst_unpin_fiber(dst_unwrap_fiber(x)); break;
case DST_USERDATA: dst_unpin_userdata(dst_unwrap_pointer(x)); break;
}
}
2017-11-01 21:53:43 +00:00
static void dst_mark_string(const uint8_t *str) {
dst_gc_mark(dst_string_raw(str));
2017-11-01 21:53:43 +00:00
}
2017-11-01 21:53:43 +00:00
static void dst_mark_buffer(DstBuffer *buffer) {
dst_gc_mark(buffer);
2017-11-01 21:53:43 +00:00
}
static void dst_mark_udata(void *udata) {
dst_gc_mark(dst_userdata_header(udata));
2017-11-01 21:53:43 +00:00
}
/* Mark a bunch of items in memory */
static void dst_mark_many(const DstValue *values, int32_t n) {
2017-11-01 21:53:43 +00:00
const DstValue *end = values + n;
while (values < end) {
dst_mark(*values);
values += 1;
2017-11-01 21:53:43 +00:00
}
}
static void dst_mark_array(DstArray *array) {
if (dst_gc_reachable(array))
2017-11-01 21:53:43 +00:00
return;
dst_gc_mark(array);
2017-11-01 21:53:43 +00:00
dst_mark_many(array->data, array->count);
}
static void dst_mark_table(DstTable *table) {
if (dst_gc_reachable(table))
2017-11-01 21:53:43 +00:00
return;
dst_gc_mark(table);
2017-11-01 21:53:43 +00:00
dst_mark_many(table->data, table->capacity);
}
static void dst_mark_struct(const DstValue *st) {
if (dst_gc_reachable(dst_struct_raw(st)))
2017-11-01 21:53:43 +00:00
return;
dst_gc_mark(dst_struct_raw(st));
2017-11-01 21:53:43 +00:00
dst_mark_many(st, dst_struct_capacity(st));
}
static void dst_mark_tuple(const DstValue *tuple) {
if (dst_gc_reachable(dst_tuple_raw(tuple)))
2017-11-01 21:53:43 +00:00
return;
dst_gc_mark(dst_tuple_raw(tuple));
dst_mark_many(tuple, dst_tuple_length(tuple));
2017-05-03 23:57:06 +00:00
}
/* Helper to mark function environments */
2017-11-01 21:53:43 +00:00
static void dst_mark_funcenv(DstFuncEnv *env) {
if (dst_gc_reachable(env))
2017-11-01 21:53:43 +00:00
return;
dst_gc_mark(env);
if (env->offset) {
/* On stack */
dst_mark_fiber(env->as.fiber);
} else {
/* Not on stack */
dst_mark_many(env->as.values, env->length);
}
}
/* GC helper to mark a FuncDef */
2017-11-01 21:53:43 +00:00
static void dst_mark_funcdef(DstFuncDef *def) {
int32_t count, i;
if (dst_gc_reachable(def))
2017-11-01 21:53:43 +00:00
return;
dst_gc_mark(def);
if (def->constants) {
count = def->constants_length;
2017-11-01 21:53:43 +00:00
for (i = 0; i < count; ++i) {
DstValue v = def->constants[i];
/* Funcdefs use nil literals to store other funcdefs */
2017-11-29 20:17:56 +00:00
if (dst_checktype(v, DST_NIL)) {
dst_mark_funcdef((DstFuncDef *) dst_unwrap_pointer(v));
2017-11-01 21:53:43 +00:00
} else {
dst_mark(v);
}
}
}
}
2017-11-01 21:53:43 +00:00
static void dst_mark_function(DstFunction *func) {
int32_t i;
int32_t numenvs;
if (dst_gc_reachable(func))
2017-11-01 21:53:43 +00:00
return;
dst_gc_mark(func);
numenvs = func->def->environments_length;
if (NULL != func->envs)
for (i = 0; i < numenvs; ++i)
if (NULL != func->envs[i])
dst_mark_funcenv(func->envs[i]);
2017-11-01 21:53:43 +00:00
dst_mark_funcdef(func->def);
}
static void dst_mark_fiber(DstFiber *fiber) {
int32_t i, j;
DstStackFrame *frame;
if (dst_gc_reachable(fiber))
2017-11-01 21:53:43 +00:00
return;
dst_gc_mark(fiber);
i = fiber->frame;
j = fiber->frametop;
while (i > 0) {
frame = (DstStackFrame *)(fiber->data + i - DST_FRAME_SIZE);
if (NULL != frame->func)
dst_mark_function(frame->func);
/* Mark all values in the stack frame */
dst_mark_many(fiber->data + i, j - i);
j = i - DST_FRAME_SIZE;
i = frame->prevframe;
}
if (NULL != fiber->parent)
dst_mark_fiber(fiber->parent);
dst_mark(fiber->ret);
}
2017-11-01 21:53:43 +00:00
/* Deinitialize a block of memory */
static void dst_deinit_block(DstGCMemoryHeader *block) {
2017-11-01 21:53:43 +00:00
void *mem = ((char *)(block + 1));
DstUserdataHeader *h = (DstUserdataHeader *)mem;
switch (block->flags & DST_MEM_TYPEBITS) {
2017-07-02 01:51:16 +00:00
default:
2017-11-01 21:53:43 +00:00
break; /* Do nothing for non gc types */
case DST_MEMORY_SYMBOL:
dst_symbol_deinit((const uint8_t *)mem + 2 * sizeof(int32_t));
break;
2017-11-01 21:53:43 +00:00
case DST_MEMORY_ARRAY:
dst_array_deinit((DstArray*) mem);
break;
2017-11-01 21:53:43 +00:00
case DST_MEMORY_TABLE:
dst_table_deinit((DstTable*) mem);
2017-03-07 20:29:40 +00:00
break;
case DST_MEMORY_FIBER:
free(((DstFiber *) mem)->data);
2017-04-15 20:05:59 +00:00
break;
2017-11-01 21:53:43 +00:00
case DST_MEMORY_BUFFER:
dst_buffer_deinit((DstBuffer *) mem);
2017-11-01 21:53:43 +00:00
break;
case DST_MEMORY_FUNCTION:
free(((DstFunction *)mem)->envs);
break;
2017-11-01 21:53:43 +00:00
case DST_MEMORY_USERDATA:
if (h->type->finalize)
h->type->finalize((void *)(h + 1), h->size);
break;
2017-11-01 21:53:43 +00:00
case DST_MEMORY_FUNCENV:
{
DstFuncEnv *env = (DstFuncEnv *)mem;
if (0 == env->offset)
free(env->as.values);
}
break;
2017-11-01 21:53:43 +00:00
case DST_MEMORY_FUNCDEF:
{
DstFuncDef *def = (DstFuncDef *)mem;
2017-11-01 21:53:43 +00:00
/* TODO - get this all with one alloc and one free */
free(def->environments);
free(def->constants);
free(def->bytecode);
}
break;
}
}
/* Iterate over all allocated memory, and free memory that is not
* marked as reachable. Flip the gc color flag for next sweep. */
void dst_sweep() {
DstGCMemoryHeader *previous = NULL;
DstGCMemoryHeader *current = dst_vm_blocks;
DstGCMemoryHeader *next;
while (NULL != current) {
next = current->next;
2017-11-01 21:53:43 +00:00
if (current->flags & (DST_MEM_REACHABLE | DST_MEM_DISABLED)) {
previous = current;
current->flags &= ~DST_MEM_REACHABLE;
2017-11-01 21:53:43 +00:00
} else {
dst_deinit_block(current);
if (NULL != previous) {
previous->next = next;
} else {
dst_vm_blocks = next;
}
2017-11-01 21:53:43 +00:00
free(current);
}
current = next;
}
}
2017-11-01 21:53:43 +00:00
/* Allocate some memory that is tracked for garbage collection */
void *dst_alloc(DstMemoryType type, size_t size) {
DstGCMemoryHeader *mdata;
size_t total = size + sizeof(DstGCMemoryHeader);
/* Make sure everything is inited */
if (NULL == dst_vm_cache) {
DST_PLEASE_INIT;
}
void *mem = malloc(total);
2017-11-01 21:53:43 +00:00
/* Check for bad malloc */
if (NULL == mem) {
DST_OUT_OF_MEMORY;
}
2017-11-01 21:53:43 +00:00
mdata = (DstGCMemoryHeader *)mem;
2017-11-01 21:53:43 +00:00
/* Configure block */
mdata->flags = type;
2017-11-01 21:53:43 +00:00
/* Prepend block to heap list */
dst_vm_next_collection += size;
mdata->next = dst_vm_blocks;
dst_vm_blocks = mdata;
return mem + sizeof(DstGCMemoryHeader);
2017-03-22 04:27:18 +00:00
}
/* Run garbage collection */
void dst_collect() {
if (dst_vm_fiber)
dst_mark_fiber(dst_vm_fiber);
dst_sweep();
dst_vm_next_collection = 0;
}
/* Free all allocated memory */
void dst_clear_memory() {
DstGCMemoryHeader *current = dst_vm_blocks;
while (current) {
dst_deinit_block(current);
DstGCMemoryHeader *next = current->next;
2017-11-01 21:53:43 +00:00
free(current);
current = next;
}
dst_vm_blocks = NULL;
}