Skip to content

Commit

Permalink
add critical sections to gc_write_barrier() to let an interrupt handl…
Browse files Browse the repository at this point in the history
…er safely run during garbage collection.
  • Loading branch information
chibash committed Sep 18, 2024
1 parent 53981cc commit f7021dd
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 6 deletions.
77 changes: 71 additions & 6 deletions microcontroller/core/src/c-runtime.c
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,18 @@ pointer_t gc_heap_pointer(pointer_t ptr) {
}
#endif

#ifdef TEST64

#define ATOMIC_ENTER_CRITICAL
#define ATOMIC_EXIT_CRITICAL

#else

#include <FreeRTOS.h>
#include <atomic.h>

#endif

// runtime error handling

static jmp_buf long_jump_buffer;
Expand Down Expand Up @@ -1178,12 +1190,15 @@ struct gc_root_set* gc_root_set_head = NULL;
#define CLEAR_GRAY_BIT(ptr) ((ptr)->header &= ~0b10)
#define SET_GRAY_BIT(ptr) ((ptr)->header |= 0b10)


#define STACK_SIZE (HEAP_SIZE / 65)
static pointer_t gc_stack[STACK_SIZE];
static uint32_t gc_stack_top = 0;
static bool gc_stack_overflowed = false;

#define ISTACK_SIZE (STACK_SIZE / 2)
static pointer_t gc_intr_stack[ISTACK_SIZE]; // used by interrupt handlers
static uint32_t gc_intr_stack_top = 0;

static void push_object_to_stack(pointer_t obj, uint32_t mark) {
WRITE_MARK_BIT(obj, mark);
SET_GRAY_BIT(obj);
Expand All @@ -1199,12 +1214,41 @@ void gc_write_barrier(pointer_t obj, value_t value) {
uint32_t mark = current_no_mark ? 0 : 1;
pointer_t ptr = value_to_ptr(value);
if (IS_WHITE(ptr, mark) && (obj == NULL || IS_BLACK(obj, mark))) {
push_object_to_stack(ptr, mark);
ATOMIC_ENTER_CRITICAL
if (gc_intr_stack_top < ISTACK_SIZE)
gc_intr_stack[gc_intr_stack_top++] = ptr;
else {
WRITE_MARK_BIT(ptr, mark);
SET_GRAY_BIT(ptr);
gc_stack_overflowed = true;
}
ATOMIC_EXIT_CRITICAL
}
}
}
}

static void copy_from_intr_stack(uint32_t mark) {
uint32_t num = gc_intr_stack_top;
uint32_t i = 0;
int failure;
do {
while (i < num)
push_object_to_stack(gc_intr_stack[i++], mark);

ATOMIC_ENTER_CRITICAL
if (num == gc_intr_stack_top) {
failure = 0;
gc_intr_stack_top = 0;
}
else {
num = gc_intr_stack_top;
failure = 1;
}
ATOMIC_EXIT_CRITICAL
} while (failure);
}

static void trace_from_an_object(uint32_t mark) {
while (gc_stack_top > 0) {
pointer_t obj = gc_stack[--gc_stack_top];
Expand Down Expand Up @@ -1240,6 +1284,9 @@ static void scan_and_mark_objects(uint32_t mark) {
if (IS_GRAY(obj)) {
gc_stack[0] = obj;
gc_stack_top = 1;
if (gc_intr_stack_top > 0)
copy_from_intr_stack(mark);

trace_from_an_object(mark);
}
start += real_objsize(size);
Expand Down Expand Up @@ -1268,6 +1315,9 @@ static void mark_objects(struct gc_root_set* root_set, uint32_t mark) {
SET_GRAY_BIT(rootp);
gc_stack[0] = rootp;
gc_stack_top = 1;
if (gc_intr_stack_top > 0)
copy_from_intr_stack(mark);

trace_from_an_object(mark);
}
}
Expand All @@ -1276,10 +1326,18 @@ static void mark_objects(struct gc_root_set* root_set, uint32_t mark) {
root_set = root_set->next;
}

while (gc_stack_overflowed) {
gc_stack_overflowed = false;
scan_and_mark_objects(mark);
}
do {
while (gc_stack_overflowed) {
gc_stack_overflowed = false;
scan_and_mark_objects(mark);
}

if (gc_intr_stack_top > 0) {
gc_stack_top = 0;
copy_from_intr_stack(mark);
trace_from_an_object(mark);
}
} while (gc_stack_overflowed || gc_intr_stack_top > 0);
}

static void sweep_objects(uint32_t mark) {
Expand Down Expand Up @@ -1348,6 +1406,13 @@ void gc_run() {
gc_is_running = false;
}

#ifdef TEST64
uint32_t gc_test_run() {
gc_is_running = true;
return current_no_mark ? 0 : 1;
}
#endif

void gc_init_rootset(struct gc_root_set* set, uint32_t length) {
set->next = gc_root_set_head;
if (length > 0) {
Expand Down
49 changes: 49 additions & 0 deletions microcontroller/core/test/c-runtime-test.c
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,54 @@ void test_gc_sweep() {
gc_run();
}

extern uint32_t gc_test_run();

static void write_mark_bit(value_t obj, uint32_t mark) {
pointer_t ptr = value_to_ptr(obj);
if (mark)
ptr->header |= 1;
else
ptr->header &= ~1;
}

void test_gc_write_barrier() {
gc_initialize();
ROOT_SET(root_set, 5);
value_t obj, obj1, obj2;

obj = gc_new_string("test");
obj1 = gc_new_string("int");

uint32_t mark = gc_test_run();
interrupt_handler_start();

write_mark_bit(obj, mark);
write_mark_bit(obj1, !mark);
gc_write_barrier(value_to_ptr(obj), obj1);

interrupt_handler_end();

gc_new_string("test1");
root_set.values[0] = gc_new_string("test2");
root_set.values[1] = gc_new_string("test3");
root_set.values[2] = gc_new_string("test4");
gc_new_string("test5");
gc_new_vector2(2);
gc_new_vector2(3);
root_set.values[3] = obj = gc_new_vector2(4);
obj2 = gc_new_vector2(3);
gc_vector_setter(obj, 0, obj2);

gc_run();
Assert_true(is_live_object(obj1));
Assert_true(is_live_object(obj2));

gc_run();
Assert_true(!is_live_object(obj1));

DELETE_ROOT_SET(root_set);
}

void test_main() {
test_converters();
test_string();
Expand All @@ -461,6 +509,7 @@ void test_main() {
test_gc_liveness();
test_gc_liveness2();
test_gc_sweep();
test_gc_write_barrier();
puts("done");
}

Expand Down

0 comments on commit f7021dd

Please sign in to comment.