Add concurrency
This commit is contained in:
377
vm.c
377
vm.c
@@ -5,13 +5,14 @@
|
||||
#include"str.h"
|
||||
|
||||
#include<math.h>
|
||||
#include<malloc.h>
|
||||
|
||||
size_t lvm_run(LVM *L, LFunc *func, size_t arg_count, LValue *regs) {
|
||||
static size_t lvm_run_internal(LVM *L, LFunc *func, size_t arg_count, set_LValueU *heap, LRegSet *regset) {
|
||||
if(func->is_native) {
|
||||
return func->native_func(L, func->ud, arg_count, regs);
|
||||
return func->native_func(L, func->ud, arg_count, regset);
|
||||
}
|
||||
|
||||
static void *dispatch_table[] = {
|
||||
static const void *dispatch_table[] = {
|
||||
[L_GETGLOBAL] = &&do_getglobal,
|
||||
[L_SETGLOBAL] = &&do_setglobal,
|
||||
[L_SETINT16] = &&do_setint16,
|
||||
@@ -44,6 +45,8 @@ size_t lvm_run(LVM *L, LFunc *func, size_t arg_count, LValue *regs) {
|
||||
LInst *inst = func->lua_instrs;
|
||||
#define DISPATCH() goto *dispatch_table[(++inst)->opcode]
|
||||
|
||||
LThreadPrivates privates = {.regset = regset, .heap = heap};
|
||||
|
||||
inst--;
|
||||
DISPATCH();
|
||||
|
||||
@@ -53,9 +56,15 @@ do_getglobal:;
|
||||
size_t len = *(uint16_t*) area;
|
||||
area += 2;
|
||||
|
||||
LString *str = lstring_new(len, area);
|
||||
regs[inst->a] = ltable_get(func->env, lvalue_from_string(str));
|
||||
lstring_free(str);
|
||||
LString *str = realloc(NULL, sizeof(*str) + len);
|
||||
str->length = len;
|
||||
memcpy(str->data, area, len);
|
||||
|
||||
regset->regs[inst->a] = ltable_get(func->env, lvalue_from_string(str));
|
||||
|
||||
set_LValueU_insert(heap, lvalue_from_string(str).u);
|
||||
|
||||
lvm_gc_alert(L, &privates, sizeof(*str) + len);
|
||||
}
|
||||
DISPATCH();
|
||||
|
||||
@@ -65,18 +74,24 @@ do_setglobal:;
|
||||
size_t len = *(uint16_t*) area;
|
||||
area += 2;
|
||||
|
||||
LString *str = lstring_new(len, area);
|
||||
ltable_set(func->env, lvalue_from_string(str), regs[inst->a]);
|
||||
lvm_gc_add(L, lvalue_from_string(str));
|
||||
LString *str = realloc(NULL, sizeof(*str) + len);
|
||||
str->length = len;
|
||||
memcpy(str->data, area, len);
|
||||
|
||||
ltable_set(func->env, lvalue_from_string(str), regset->regs[inst->a]);
|
||||
|
||||
set_LValueU_insert(heap, lvalue_from_string(str).u);
|
||||
|
||||
lvm_gc_alert(L, &privates, sizeof(*str) + len);
|
||||
}
|
||||
DISPATCH();
|
||||
|
||||
do_setint16:;
|
||||
regs[inst->a] = lvalue_from_int32((int16_t) inst->bc);
|
||||
regset->regs[inst->a] = lvalue_from_int32((int16_t) inst->bc);
|
||||
DISPATCH();
|
||||
|
||||
do_setint32:;
|
||||
regs[inst->a] = lvalue_from_int32(*(int32_t*) &unit->abyss[inst->bc]);
|
||||
regset->regs[inst->a] = lvalue_from_int32(*(int32_t*) &unit->abyss[inst->bc]);
|
||||
DISPATCH();
|
||||
|
||||
do_setfloat:;
|
||||
@@ -88,87 +103,94 @@ do_setstr:;
|
||||
size_t len = *(uint16_t*) area;
|
||||
area += 2;
|
||||
|
||||
regs[inst->a] = lvalue_raw(LTAG_STRING, (uintptr_t) lstring_new(len, area));
|
||||
lvm_gc_add(L, regs[inst->a]);
|
||||
LString *str = realloc(NULL, sizeof(*str) + len);
|
||||
str->length = len;
|
||||
memcpy(str->data, area, len);
|
||||
|
||||
regset->regs[inst->a] = lvalue_from_string(str);
|
||||
|
||||
set_LValueU_insert(heap, lvalue_from_string(str).u);
|
||||
|
||||
lvm_gc_alert(L, &privates, sizeof(*str) + len);
|
||||
}
|
||||
DISPATCH();
|
||||
|
||||
do_settable:;
|
||||
{
|
||||
LTable *tbl = ltable_new(inst->bc);
|
||||
lvm_gc_add(L, lvalue_from_table(tbl));
|
||||
regs[inst->a] = lvalue_from_table(tbl);
|
||||
regset->regs[inst->a] = lvalue_from_table(tbl);
|
||||
set_LValueU_insert(heap, lvalue_from_table(tbl).u);
|
||||
}
|
||||
DISPATCH();
|
||||
|
||||
do_setbool:;
|
||||
regs[inst->a] = lvalue_from_bool(inst->b);
|
||||
regset->regs[inst->a] = lvalue_from_bool(inst->b);
|
||||
DISPATCH();
|
||||
|
||||
do_setnil:;
|
||||
regs[inst->a] = lvalue_from_nil();
|
||||
regset->regs[inst->a] = lvalue_from_nil();
|
||||
DISPATCH();
|
||||
|
||||
do_setfunc:;
|
||||
regs[inst->a] = lvalue_from_func(&func->unit->funcs[inst->bc]);
|
||||
regset->regs[inst->a] = lvalue_from_func(&func->unit->funcs[inst->bc]);
|
||||
DISPATCH();
|
||||
|
||||
do_add:;
|
||||
{
|
||||
LValue x = regs[inst->b];
|
||||
LValue y = regs[inst->c];
|
||||
LValue x = regset->regs[inst->b];
|
||||
LValue y = regset->regs[inst->c];
|
||||
if(lvalue_tag(x) == LTAG_I32 && lvalue_tag(y) == LTAG_FLOAT) {
|
||||
regs[inst->a] = lvalue_from_double(lvalue_to_int32(x) + y.f);
|
||||
regset->regs[inst->a] = lvalue_from_double(lvalue_to_int32(x) + y.f);
|
||||
} else if(lvalue_tag(x) == LTAG_FLOAT && lvalue_tag(y) == LTAG_I32) {
|
||||
regs[inst->a] = lvalue_from_double(x.f + lvalue_to_int32(y));
|
||||
regset->regs[inst->a] = lvalue_from_double(x.f + lvalue_to_int32(y));
|
||||
} else if(lvalue_tag(x) == LTAG_I32 && lvalue_tag(y) == LTAG_I32) {
|
||||
regs[inst->a] = lvalue_from_int32(lvalue_to_int32(x) + lvalue_to_int32(y));
|
||||
regset->regs[inst->a] = lvalue_from_int32(lvalue_to_int32(x) + lvalue_to_int32(y));
|
||||
} else goto err;
|
||||
}
|
||||
DISPATCH();
|
||||
|
||||
do_sub:;
|
||||
{
|
||||
LValue x = regs[inst->b];
|
||||
LValue y = regs[inst->c];
|
||||
LValue x = regset->regs[inst->b];
|
||||
LValue y = regset->regs[inst->c];
|
||||
if(lvalue_tag(x) == LTAG_I32 && lvalue_tag(y) == LTAG_FLOAT) {
|
||||
regs[inst->a] = lvalue_from_double(lvalue_to_int32(x) - y.f);
|
||||
regset->regs[inst->a] = lvalue_from_double(lvalue_to_int32(x) - y.f);
|
||||
} else if(lvalue_tag(x) == LTAG_FLOAT && lvalue_tag(y) == LTAG_I32) {
|
||||
regs[inst->a] = lvalue_from_double(x.f - lvalue_to_int32(y));
|
||||
regset->regs[inst->a] = lvalue_from_double(x.f - lvalue_to_int32(y));
|
||||
} else if(lvalue_tag(x) == LTAG_I32 && lvalue_tag(y) == LTAG_I32) {
|
||||
regs[inst->a] = lvalue_from_int32(lvalue_to_int32(x) - lvalue_to_int32(y));
|
||||
regset->regs[inst->a] = lvalue_from_int32(lvalue_to_int32(x) - lvalue_to_int32(y));
|
||||
} else goto err;
|
||||
}
|
||||
DISPATCH();
|
||||
|
||||
do_mul:;
|
||||
{
|
||||
LValue x = regs[inst->b];
|
||||
LValue y = regs[inst->c];
|
||||
LValue x = regset->regs[inst->b];
|
||||
LValue y = regset->regs[inst->c];
|
||||
if(lvalue_tag(x) == LTAG_I32 && lvalue_tag(y) == LTAG_FLOAT) {
|
||||
regs[inst->a] = lvalue_from_double(lvalue_to_int32(x) * y.f);
|
||||
regset->regs[inst->a] = lvalue_from_double(lvalue_to_int32(x) * y.f);
|
||||
} else if(lvalue_tag(x) == LTAG_FLOAT && lvalue_tag(y) == LTAG_I32) {
|
||||
regs[inst->a] = lvalue_from_double(x.f * lvalue_to_int32(y));
|
||||
regset->regs[inst->a] = lvalue_from_double(x.f * lvalue_to_int32(y));
|
||||
} else if(lvalue_tag(x) == LTAG_I32 && lvalue_tag(y) == LTAG_I32) {
|
||||
regs[inst->a] = lvalue_from_int32(lvalue_to_int32(x) * lvalue_to_int32(y));
|
||||
regset->regs[inst->a] = lvalue_from_int32(lvalue_to_int32(x) * lvalue_to_int32(y));
|
||||
} else goto err;
|
||||
}
|
||||
DISPATCH();
|
||||
|
||||
do_div:;
|
||||
{
|
||||
LValue x = regs[inst->b];
|
||||
LValue y = regs[inst->c];
|
||||
LValue x = regset->regs[inst->b];
|
||||
LValue y = regset->regs[inst->c];
|
||||
if(lvalue_tag(x) == LTAG_I32 && lvalue_tag(y) == LTAG_FLOAT) {
|
||||
regs[inst->a] = lvalue_from_double(lvalue_to_int32(x) / y.f);
|
||||
regset->regs[inst->a] = lvalue_from_double(lvalue_to_int32(x) / y.f);
|
||||
} else if(lvalue_tag(x) == LTAG_FLOAT && lvalue_tag(y) == LTAG_I32) {
|
||||
regs[inst->a] = lvalue_from_double(x.f / lvalue_to_int32(y));
|
||||
regset->regs[inst->a] = lvalue_from_double(x.f / lvalue_to_int32(y));
|
||||
} else if(lvalue_tag(x) == LTAG_I32 && lvalue_tag(y) == LTAG_I32) {
|
||||
int32_t yv = lvalue_to_int32(y);
|
||||
if(yv == 0) {
|
||||
regs[inst->a] = lvalue_from_nil();
|
||||
regset->regs[inst->a] = lvalue_from_nil();
|
||||
} else {
|
||||
regs[inst->a] = lvalue_from_int32(lvalue_to_int32(x) / yv);
|
||||
regset->regs[inst->a] = lvalue_from_int32(lvalue_to_int32(x) / yv);
|
||||
}
|
||||
} else goto err;
|
||||
}
|
||||
@@ -176,19 +198,19 @@ do_div:;
|
||||
|
||||
do_mod:;
|
||||
{
|
||||
LValue x = regs[inst->b];
|
||||
LValue y = regs[inst->c];
|
||||
LValue x = regset->regs[inst->b];
|
||||
LValue y = regset->regs[inst->c];
|
||||
if(lvalue_tag(x) == LTAG_I32 && lvalue_tag(y) == LTAG_FLOAT) {
|
||||
regs[inst->a] = lvalue_from_double(fmod(fmod(lvalue_to_int32(x), y.f) + y.f, y.f));
|
||||
regset->regs[inst->a] = lvalue_from_double(fmod(fmod(lvalue_to_int32(x), y.f) + y.f, y.f));
|
||||
} else if(lvalue_tag(x) == LTAG_FLOAT && lvalue_tag(y) == LTAG_I32) {
|
||||
int32_t yv = lvalue_to_int32(y);
|
||||
regs[inst->a] = lvalue_from_double(fmod(fmod(x.f, yv) + yv, yv));
|
||||
regset->regs[inst->a] = lvalue_from_double(fmod(fmod(x.f, yv) + yv, yv));
|
||||
} else if(lvalue_tag(x) == LTAG_I32 && lvalue_tag(y) == LTAG_I32) {
|
||||
int32_t yv = lvalue_to_int32(y);
|
||||
if(yv == 0) {
|
||||
goto err;
|
||||
} else {
|
||||
regs[inst->a] = lvalue_from_int32((lvalue_to_int32(x) % yv + yv) % yv);
|
||||
regset->regs[inst->a] = lvalue_from_int32((lvalue_to_int32(x) % yv + yv) % yv);
|
||||
}
|
||||
} else goto err;
|
||||
}
|
||||
@@ -196,11 +218,13 @@ do_mod:;
|
||||
|
||||
do_jump:;
|
||||
inst += (int16_t) inst->bc;
|
||||
|
||||
L->safepoint_func(L, heap, regset);
|
||||
DISPATCH();
|
||||
|
||||
do_jnotcond:;
|
||||
{
|
||||
LValue v = regs[inst->a];
|
||||
LValue v = regset->regs[inst->a];
|
||||
if(v.u == LTAG_NIL || v.u == LTAG_FALSE) {
|
||||
inst += (int16_t) inst->bc;
|
||||
}
|
||||
@@ -209,7 +233,7 @@ do_jnotcond:;
|
||||
|
||||
do_call:;
|
||||
{
|
||||
if(lvalue_tag(regs[inst->a]) != LTAG_FUNCTION) {
|
||||
if(lvalue_tag(regset->regs[inst->a]) != LTAG_FUNCTION) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
@@ -218,30 +242,30 @@ do_call:;
|
||||
uint8_t ret_vreg = abyss_data[0];
|
||||
uint8_t arg_count = abyss_data[1];
|
||||
uint8_t *args = &abyss_data[2];
|
||||
|
||||
LValue regs2[256];
|
||||
lvm_reset_regs(regs2);
|
||||
|
||||
LRegSet regset2 = {.parent = regset};
|
||||
lvm_reset_regs(®set2);
|
||||
for(int i = 0; i < arg_count; i++) {
|
||||
regs2[i] = regs[args[i]];
|
||||
regset2.regs[i] = regset->regs[args[i]];
|
||||
}
|
||||
size_t returned_count = lvm_run(L, (LFunc*) (regs[inst->a].u & ~LTAG_MASK), arg_count, regs2);
|
||||
size_t returned_count = lvm_run_internal(L, (LFunc*) (regset->regs[inst->a].u & ~LTAG_MASK), arg_count, heap, ®set2);
|
||||
|
||||
if(returned_count) {
|
||||
// TODO: more than 1 return
|
||||
regs[ret_vreg] = regs2[0];
|
||||
regset->regs[ret_vreg] = regset2.regs[0];
|
||||
}
|
||||
}
|
||||
DISPATCH();
|
||||
|
||||
do_move:;
|
||||
regs[inst->a] = regs[inst->b];
|
||||
regset->regs[inst->a] = regset->regs[inst->b];
|
||||
DISPATCH();
|
||||
|
||||
do_advancetest:;
|
||||
{
|
||||
int64_t a = lvalue_to_int32(regs[inst->a]);
|
||||
int64_t b = lvalue_to_int32(regs[inst->b]);
|
||||
int64_t c = lvalue_to_int32(regs[inst->c]);
|
||||
int64_t a = lvalue_to_int32(regset->regs[inst->a]);
|
||||
int64_t b = lvalue_to_int32(regset->regs[inst->b]);
|
||||
int64_t c = lvalue_to_int32(regset->regs[inst->c]);
|
||||
if(!((c >= 0 && a > b) || (c < 0 && a < b))) {
|
||||
inst++;
|
||||
}
|
||||
@@ -249,49 +273,65 @@ do_advancetest:;
|
||||
DISPATCH();
|
||||
|
||||
do_cond_eq:;
|
||||
regs[inst->a] = lvalue_from_bool(lvalue_eq(regs[inst->b], regs[inst->c]));
|
||||
regset->regs[inst->a] = lvalue_from_bool(lvalue_eq(regset->regs[inst->b], regset->regs[inst->c]));
|
||||
DISPATCH();
|
||||
|
||||
do_cond_neq:;
|
||||
regs[inst->a] = lvalue_from_bool(!lvalue_eq(regs[inst->b], regs[inst->c]));
|
||||
regset->regs[inst->a] = lvalue_from_bool(!lvalue_eq(regset->regs[inst->b], regset->regs[inst->c]));
|
||||
DISPATCH();
|
||||
|
||||
do_setfield:;
|
||||
{
|
||||
if(lvalue_tag(regs[inst->a]) != LTAG_TABLE) {
|
||||
if(lvalue_tag(regset->regs[inst->a]) != LTAG_TABLE) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
if(lvalue_tag(regs[inst->b]) == LTAG_NIL) {
|
||||
if(lvalue_tag(regset->regs[inst->b]) == LTAG_NIL) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
LTable *tbl = (void*) (regs[inst->a].u & ~LTAG_MASK);
|
||||
LTable *tbl = (void*) (regset->regs[inst->a].u & ~LTAG_MASK);
|
||||
|
||||
ltable_set(tbl, regs[inst->b], regs[inst->c]);
|
||||
ltable_set(tbl, regset->regs[inst->b], regset->regs[inst->c]);
|
||||
}
|
||||
DISPATCH();
|
||||
|
||||
do_getfield:;
|
||||
{
|
||||
if(lvalue_tag(regs[inst->a]) != LTAG_TABLE) {
|
||||
if(lvalue_tag(regset->regs[inst->a]) != LTAG_TABLE) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
LTable *tbl = (void*) (regs[inst->b].u & ~LTAG_MASK);
|
||||
LTable *tbl = (void*) (regset->regs[inst->b].u & ~LTAG_MASK);
|
||||
|
||||
regs[inst->a] = ltable_get(tbl, regs[inst->c]);
|
||||
regset->regs[inst->a] = ltable_get(tbl, regset->regs[inst->c]);
|
||||
}
|
||||
DISPATCH();
|
||||
|
||||
err:;
|
||||
puts("Error");
|
||||
do_ret:;
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
void lvm_gc_add(LVM *L, LValue lvalue) {
|
||||
set_LValueU_insert(&L->gc_objects, lvalue.u);
|
||||
size_t lvm_run(LVM *L, LFunc *func, size_t arg_count, LRegSet *regset) {
|
||||
set_LValueU heap = {};
|
||||
|
||||
atomic_fetch_add(&L->active_thread_count, 1);
|
||||
|
||||
size_t ret = lvm_run_internal(L, func, arg_count, &heap, regset);
|
||||
|
||||
mtx_lock(&L->dead_heap_mut);
|
||||
for(c_each(i, set_LValueU, heap)) {
|
||||
set_LValueU_insert(&L->dead_heap, *i.ref);
|
||||
}
|
||||
atomic_fetch_sub(&L->active_thread_count, 1);
|
||||
mtx_unlock(&L->dead_heap_mut);
|
||||
|
||||
set_LValueU_drop(&heap);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
LFunc *lvm_func_from_native(LFuncCallback cb, void *ud) {
|
||||
@@ -324,3 +364,206 @@ bool lvalue_eq(LValue a, LValue b) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void gc_unmark_heap(set_LValueU *heap) {
|
||||
for(c_each(i, set_LValueU, *heap)) {
|
||||
LValue v = (LValue) {.u = *i.ref};
|
||||
|
||||
assert(lvalue_tag(v) == LTAG_TABLE || lvalue_tag(v) == LTAG_STRING);
|
||||
|
||||
void *gco = (void*) (v.u & ~LTAG_MASK);
|
||||
|
||||
if(lvalue_tag(v) == LTAG_TABLE) {
|
||||
LTable *tbl = gco;
|
||||
tbl->ref = false;
|
||||
} else if(lvalue_tag(v) == LTAG_STRING) {
|
||||
LString *str = gco;
|
||||
str->ref = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
static void gc_unmark_all(LVM *L, size_t thread_count) {
|
||||
for(size_t thrd = 0; thrd < thread_count; thrd++) {
|
||||
LThreadPrivates *privates = &L->privates[thrd];
|
||||
gc_unmark_heap(privates->heap);
|
||||
}
|
||||
gc_unmark_heap(&L->dead_heap);
|
||||
}
|
||||
static void gc_mark(LValue v) {
|
||||
if(lvalue_tag(v) != LTAG_TABLE && lvalue_tag(v) != LTAG_STRING) {
|
||||
return;
|
||||
}
|
||||
|
||||
void *gco = (void*) (v.u & ~LTAG_MASK);
|
||||
|
||||
if(lvalue_tag(v) == LTAG_TABLE) {
|
||||
LTable *tbl = gco;
|
||||
tbl->ref = true;
|
||||
|
||||
for(size_t i = 0; tbl->buckets->capacity; i++) {
|
||||
LTableEntry e = tbl->buckets->data[i];
|
||||
gc_mark(e.key);
|
||||
gc_mark(e.val);
|
||||
}
|
||||
} else if(lvalue_tag(v) == LTAG_STRING) {
|
||||
LString *str = gco;
|
||||
str->ref = true;
|
||||
}
|
||||
}
|
||||
static void gc_mark_units(LVM *L) {
|
||||
for(size_t u = 0; u < L->unit_count; u++) {
|
||||
LUnit *unit = &L->units[u];
|
||||
for(size_t f = 0; f < unit->func_count; f++) {
|
||||
LFunc *func = &unit->funcs[f];
|
||||
gc_mark(lvalue_from_table(func->env));
|
||||
for(size_t upv = 0; upv < func->upvalue_count; upv++) {
|
||||
gc_mark(func->upvalues[upv]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
static void safepoint_active(LVM *L, set_LValueU *heap, LRegSet *regset) {
|
||||
size_t my_privates_index = atomic_fetch_add(&L->privates_index, 1);
|
||||
|
||||
L->privates[my_privates_index].heap = heap;
|
||||
L->privates[my_privates_index].regset = regset;
|
||||
|
||||
atomic_fetch_add(&L->privates_ready, 1);
|
||||
|
||||
// Wait until GC finishes
|
||||
while(atomic_load(&L->safepoint_func) == safepoint_active);
|
||||
|
||||
atomic_fetch_sub(&L->privates_ready, 1);
|
||||
}
|
||||
static void gc_mark_stacks(LVM *L, size_t thread_count) {
|
||||
for(size_t thrd = 0; thrd < thread_count; thrd++) {
|
||||
LThreadPrivates *privates = &L->privates[thrd];
|
||||
|
||||
LRegSet *rset = privates->regset;
|
||||
while(rset) {
|
||||
for(size_t r = 0; r < 256; r++) {
|
||||
gc_mark(rset->regs[r]);
|
||||
}
|
||||
rset = rset->parent;
|
||||
}
|
||||
}
|
||||
}
|
||||
static void safepoint_inactive(LVM *L, set_LValueU *heap, LRegSet *regset) {
|
||||
}
|
||||
static void gc_delete_unmarked_in_heap(LVM *L, set_LValueU *heap) {
|
||||
for(set_LValueU_iter i = set_LValueU_begin(heap); i.ref;) {
|
||||
LValue v = (LValue) {.u = *i.ref};
|
||||
|
||||
void *gco = (void*) (v.u & ~LTAG_MASK);
|
||||
|
||||
if(lvalue_tag(v) == LTAG_TABLE) {
|
||||
LTable *tbl = gco;
|
||||
|
||||
if(tbl->ref == false) {
|
||||
free(tbl->buckets);
|
||||
free(tbl);
|
||||
|
||||
i = set_LValueU_erase_at(heap, i);
|
||||
continue;
|
||||
}
|
||||
} else if(lvalue_tag(v) == LTAG_STRING) {
|
||||
LString *str = gco;
|
||||
|
||||
if(str->ref == false) {
|
||||
lvm_gc_alert(L, NULL, -sizeof(*str) - str->length);
|
||||
|
||||
free(str);
|
||||
|
||||
i = set_LValueU_erase_at(heap, i);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
set_LValueU_next(&i);
|
||||
}
|
||||
}
|
||||
static void gc_delete_unmarked(LVM *L, size_t thread_count) {
|
||||
for(size_t thrd = 0; thrd < thread_count; thrd++) {
|
||||
LThreadPrivates *privates = &L->privates[thrd];
|
||||
gc_delete_unmarked_in_heap(L, privates->heap);
|
||||
}
|
||||
gc_delete_unmarked_in_heap(L, &L->dead_heap);
|
||||
}
|
||||
static void lvm_gc_force(LVM *L, LThreadPrivates *callerPrivates) {
|
||||
// At most one thread can force GC, while others must behave as usual and enter a safepoint instead
|
||||
if(atomic_compare_exchange_strong(&L->gcInProgress, &(bool) {false}, true)) {
|
||||
|
||||
//static size_t gcidx = 0;
|
||||
//fprintf(stderr, "GC %i (%lu bytes)\n", atomic_fetch_add(&gcidx, 1), L->memUsage);
|
||||
|
||||
if(callerPrivates) {
|
||||
// Called from within VM
|
||||
atomic_store(&L->privates_index, 1);
|
||||
atomic_store(&L->privates_ready, 1);
|
||||
|
||||
L->privates[0] = *callerPrivates;
|
||||
} else {
|
||||
// Called outside of VM, probably by lvm_destroy
|
||||
atomic_store(&L->privates_index, 0);
|
||||
atomic_store(&L->privates_ready, 0);
|
||||
}
|
||||
|
||||
L->safepoint_func = safepoint_active;
|
||||
|
||||
// Wait until other threads have entered GC stage
|
||||
while(atomic_load(&L->privates_ready) < atomic_load(&L->active_thread_count));
|
||||
|
||||
size_t thread_count = atomic_load(&L->privates_ready);
|
||||
|
||||
mtx_lock(&L->dead_heap_mut);
|
||||
gc_unmark_all(L, thread_count);
|
||||
gc_mark_stacks(L, thread_count);
|
||||
gc_mark_units(L);
|
||||
gc_delete_unmarked(L, thread_count);
|
||||
mtx_unlock(&L->dead_heap_mut);
|
||||
|
||||
while(L->memUsage > L->nextGCThreshold) {
|
||||
L->nextGCThreshold <<= 1;
|
||||
}
|
||||
|
||||
L->safepoint_func = safepoint_inactive;
|
||||
|
||||
if(callerPrivates) {
|
||||
// Called from within VM
|
||||
atomic_fetch_sub(&L->privates_ready, 1);
|
||||
}
|
||||
|
||||
// Wait until other threads have left GC stage
|
||||
while(atomic_load(&L->privates_ready) > 0);
|
||||
|
||||
atomic_store(&L->gcInProgress, false);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// `privates` can be NULL but ONLY IF diff < 0
|
||||
void lvm_gc_alert(LVM *L, LThreadPrivates *privates, intmax_t diff) {
|
||||
L->memUsage += diff;
|
||||
|
||||
assert(L->memUsage >= 0);
|
||||
|
||||
if(L->memUsage > L->nextGCThreshold) {
|
||||
lvm_gc_force(L, privates);
|
||||
}
|
||||
}
|
||||
|
||||
void lvm_init(LVM *L) {
|
||||
memset(L, 0, sizeof(*L));
|
||||
|
||||
L->safepoint_func = safepoint_inactive;
|
||||
L->nextGCThreshold = 16384;
|
||||
mtx_init(&L->dead_heap_mut, mtx_plain);
|
||||
}
|
||||
|
||||
void lvm_destroy(LVM *L) {
|
||||
mtx_destroy(&L->dead_heap_mut);
|
||||
|
||||
lvm_gc_force(L, NULL);
|
||||
|
||||
set_LValueU_drop(&L->dead_heap);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user