gc: Use a callback to get the (non-tmp) roots

This let's us get rid of that awkward hashmap in the GC that was used as a
set, makes determining the roots more flexible and now gc_init can't even
fail any more, as there are no allocations happening here any more :)
This commit is contained in:
Laria 2022-07-01 22:00:58 +02:00
parent 8d947244c9
commit c974b675e1
3 changed files with 26 additions and 61 deletions

View file

@ -356,6 +356,20 @@ apfl_stack_clear(apfl_ctx ctx)
ctx->stack->len = 0;
}
static void
get_roots(void *own_opaque, gc_visitor visitor, void *visitor_opaque)
{
apfl_ctx ctx = own_opaque;
if (ctx->scope != NULL) {
visitor(visitor_opaque, GC_OBJECT_FROM(ctx->scope, GC_TYPE_SCOPE));
}
if (ctx->stack != NULL) {
visitor(visitor_opaque, GC_OBJECT_FROM(ctx->stack, GC_TYPE_STACK));
}
}
apfl_ctx
apfl_ctx_new(struct apfl_allocator base_allocator)
{
@ -364,27 +378,16 @@ apfl_ctx_new(struct apfl_allocator base_allocator)
return NULL;
}
if (!apfl_gc_init(&ctx->gc, base_allocator)) {
FREE_OBJ(base_allocator, ctx);
return NULL;
}
apfl_gc_init(&ctx->gc, base_allocator, get_roots, ctx);
if ((ctx->scope = apfl_scope_new(&ctx->gc)) == NULL) {
goto error;
}
if (!apfl_gc_root_add(&ctx->gc, GC_OBJECT_FROM(ctx->scope, GC_TYPE_SCOPE))) {
goto error;
}
if ((ctx->stack = stack_new(&ctx->gc)) == NULL) {
goto error;
}
if (!apfl_gc_root_add(&ctx->gc, GC_OBJECT_FROM(ctx->stack, GC_TYPE_STACK))) {
goto error;
}
ctx->error_handler = NULL;
ctx->panic_callback = NULL;
ctx->panic_callback_data = NULL;

View file

@ -5,7 +5,6 @@
#include "bytecode.h"
#include "context.h"
#include "gc.h"
#include "hashmap.h"
#include "resizable.h"
#include "scope.h"
#include "value.h"
@ -68,8 +67,8 @@ apfl_gc_object_from_ptr(void *ptr, enum gc_type type)
return object;
}
bool
apfl_gc_init(struct gc *gc, struct apfl_allocator allocator)
void
apfl_gc_init(struct gc *gc, struct apfl_allocator allocator, gc_roots_getter roots_getter, void *roots_getter_opaque)
{
gc->base_allocator = allocator;
gc->allocator = (struct apfl_allocator) {
@ -78,22 +77,14 @@ apfl_gc_init(struct gc *gc, struct apfl_allocator allocator)
};
gc->block = NULL;
gc->roots_getter = roots_getter;
gc->roots_getter_opaque = roots_getter_opaque;
gc->tmproots = (struct gc_tmproots) {
.roots = NULL,
.len = 0,
.cap = 0,
};
gc->tmproot_for_adding = NULL;
// TODO: It's a bit wasteful that we use a hashmap here. We are only
// ever interested in the keys.
return apfl_hashmap_init(
&gc->roots,
allocator,
(struct apfl_hashmap_callbacks) {.opaque = NULL},
sizeof(struct gc_object *),
sizeof(char)
);
}
static struct gc_block *
@ -168,29 +159,6 @@ IMPL_NEW(struct instruction_list, apfl_gc_new_instructions, GC_T
IMPL_NEW(struct scope, apfl_gc_new_scope, GC_TYPE_SCOPE, scope )
IMPL_NEW(struct stack, apfl_gc_new_stack, GC_TYPE_STACK, stack )
bool
apfl_gc_root_add(struct gc *gc, struct gc_object *object)
{
// Since setting the new root can trigger a garbage collection, we need to
// set the root as the tmproot_for_adding, so we'll treat it as a root and
// not free it.
assert(gc->tmproot_for_adding == NULL);
gc->tmproot_for_adding = object;
char v = 0;
bool ok = apfl_hashmap_set(&gc->roots, &object, &v);
gc->tmproot_for_adding = NULL;
return ok;
}
void
apfl_gc_root_remove(struct gc *gc, struct gc_object *object)
{
apfl_hashmap_delete(&gc->roots, &object);
}
size_t
apfl_gc_tmproots_begin(struct gc *gc)
{
@ -237,12 +205,7 @@ color_object_grey(struct gc_object *object)
static void
visit_roots(struct gc *gc, gc_visitor visitor, void *opaque)
{
HASHMAP_EACH(&gc->roots, cur) {
struct gc_object *obj;
assert(apfl_hashmap_cursor_get_key(cur, &obj));
visitor(opaque, obj);
}
gc->roots_getter(gc->roots_getter_opaque, visitor, opaque);
for (size_t i = 0; i < gc->tmproots.len; i++) {
visitor(opaque, gc->tmproots.roots[i]);
@ -542,5 +505,4 @@ apfl_gc_deinit(struct gc *gc)
gc->block = NULL;
FREE_LIST(gc->allocator, gc->tmproots.roots, gc->tmproots.cap);
apfl_hashmap_deinit(&gc->roots);
}

View file

@ -36,18 +36,21 @@ struct gc_tmproots {
size_t cap;
};
typedef void (*gc_visitor)(void *, struct gc_object *);
typedef void (*gc_roots_getter)(void *own_opaque, gc_visitor, void *visitor_opaque);
struct gc {
struct apfl_allocator base_allocator;
struct apfl_allocator allocator;
struct gc_block *block;
struct apfl_hashmap roots;
gc_roots_getter roots_getter;
void *roots_getter_opaque;
struct gc_tmproots tmproots;
struct gc_object *tmproot_for_adding;
};
typedef void (*gc_visitor)(void *, struct gc_object *);
#ifdef NDEBUG
# define GC_OBJECT_FROM(ptr, type) ((struct gc_object *)(ptr))
@ -57,14 +60,11 @@ typedef void (*gc_visitor)(void *, struct gc_object *);
struct gc_object *apfl_gc_object_from_ptr(void *, enum gc_type);
bool apfl_gc_init(struct gc *, struct apfl_allocator);
void apfl_gc_init(struct gc *, struct apfl_allocator, gc_roots_getter, void *roots_getter_opaque);
void apfl_gc_deinit(struct gc *);
void apfl_gc_debug_dump_graph(struct gc *, FILE *);
bool apfl_gc_root_add(struct gc *gc, struct gc_object *object);
void apfl_gc_root_remove(struct gc *gc, struct gc_object *object);
size_t apfl_gc_tmproots_begin(struct gc *gc);
void apfl_gc_tmproots_restore(struct gc *gc, size_t);
bool apfl_gc_tmproot_add(struct gc *gc, struct gc_object *object);