mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-05-19 19:20:23 +02:00
BDW collector marks mutator/heap roots
Needed if a mutator has off-heap (mmap) storage.
This commit is contained in:
parent
dc013cfb58
commit
296e5e8458
1 changed files with 100 additions and 29 deletions
129
src/bdw.c
129
src/bdw.c
|
@ -50,13 +50,16 @@
|
||||||
#define GC_INLINE_FREELIST_COUNT (256U / GC_INLINE_GRANULE_BYTES)
|
#define GC_INLINE_FREELIST_COUNT (256U / GC_INLINE_GRANULE_BYTES)
|
||||||
|
|
||||||
struct gc_heap {
|
struct gc_heap {
|
||||||
|
struct gc_heap *freelist; // see mark_heap
|
||||||
pthread_mutex_t lock;
|
pthread_mutex_t lock;
|
||||||
int multithreaded;
|
int multithreaded;
|
||||||
|
struct gc_heap_roots *roots;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct gc_mutator {
|
struct gc_mutator {
|
||||||
void *freelists[GC_INLINE_FREELIST_COUNT];
|
void *freelists[GC_INLINE_FREELIST_COUNT];
|
||||||
struct gc_heap *heap;
|
struct gc_heap *heap;
|
||||||
|
struct gc_mutator_roots *roots;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline size_t gc_inline_bytes_to_freelist_index(size_t bytes) {
|
static inline size_t gc_inline_bytes_to_freelist_index(size_t bytes) {
|
||||||
|
@ -119,6 +122,25 @@ void gc_write_barrier_extern(struct gc_ref obj, size_t obj_size,
|
||||||
struct gc_edge edge, struct gc_ref new_val) {
|
struct gc_edge edge, struct gc_ref new_val) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct bdw_mark_state {
|
||||||
|
struct GC_ms_entry *mark_stack_ptr;
|
||||||
|
struct GC_ms_entry *mark_stack_limit;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void bdw_mark_edge(struct gc_edge edge, struct gc_heap *heap,
|
||||||
|
void *visit_data) {
|
||||||
|
struct bdw_mark_state *state = visit_data;
|
||||||
|
uintptr_t addr = gc_ref_value(gc_edge_ref(edge));
|
||||||
|
state->mark_stack_ptr = GC_MARK_AND_PUSH ((void *) addr,
|
||||||
|
state->mark_stack_ptr,
|
||||||
|
state->mark_stack_limit,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int heap_gc_kind;
|
||||||
|
static int mutator_gc_kind;
|
||||||
|
static int ephemeron_gc_kind;
|
||||||
|
|
||||||
// In BDW-GC, we can't hook into the mark phase to call
|
// In BDW-GC, we can't hook into the mark phase to call
|
||||||
// gc_trace_ephemerons_for_object, so the advertised ephemeron strategy
|
// gc_trace_ephemerons_for_object, so the advertised ephemeron strategy
|
||||||
// doesn't really work. The primitives that we have are mark functions,
|
// doesn't really work. The primitives that we have are mark functions,
|
||||||
|
@ -129,8 +151,6 @@ void gc_write_barrier_extern(struct gc_ref obj, size_t obj_size,
|
||||||
// ephemerons using these primitives. Instead fall back to weak-key
|
// ephemerons using these primitives. Instead fall back to weak-key
|
||||||
// tables.
|
// tables.
|
||||||
|
|
||||||
static int ephemeron_gc_kind;
|
|
||||||
|
|
||||||
struct gc_ephemeron* gc_allocate_ephemeron(struct gc_mutator *mut) {
|
struct gc_ephemeron* gc_allocate_ephemeron(struct gc_mutator *mut) {
|
||||||
return GC_generic_malloc(gc_ephemeron_size(), ephemeron_gc_kind);
|
return GC_generic_malloc(gc_ephemeron_size(), ephemeron_gc_kind);
|
||||||
}
|
}
|
||||||
|
@ -148,31 +168,17 @@ void gc_ephemeron_init(struct gc_mutator *mut, struct gc_ephemeron *ephemeron,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ephemeron_mark_state {
|
|
||||||
struct GC_ms_entry *mark_stack_ptr;
|
|
||||||
struct GC_ms_entry *mark_stack_limit;
|
|
||||||
};
|
|
||||||
|
|
||||||
int gc_visit_ephemeron_key(struct gc_edge edge, struct gc_heap *heap) {
|
int gc_visit_ephemeron_key(struct gc_edge edge, struct gc_heap *heap) {
|
||||||
// Pretend the key is traced, to avoid adding this ephemeron to the
|
// Pretend the key is traced, to avoid adding this ephemeron to the
|
||||||
// global table.
|
// global table.
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
static void trace_ephemeron_edge(struct gc_edge edge, struct gc_heap *heap,
|
|
||||||
void *visit_data) {
|
|
||||||
struct ephemeron_mark_state *state = visit_data;
|
|
||||||
uintptr_t addr = gc_ref_value(gc_edge_ref(edge));
|
|
||||||
state->mark_stack_ptr = GC_MARK_AND_PUSH ((void *) addr,
|
|
||||||
state->mark_stack_ptr,
|
|
||||||
state->mark_stack_limit,
|
|
||||||
NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct GC_ms_entry *
|
static struct GC_ms_entry *
|
||||||
mark_ephemeron(GC_word *addr, struct GC_ms_entry *mark_stack_ptr,
|
mark_ephemeron(GC_word *addr, struct GC_ms_entry *mark_stack_ptr,
|
||||||
struct GC_ms_entry *mark_stack_limit, GC_word env) {
|
struct GC_ms_entry *mark_stack_limit, GC_word env) {
|
||||||
|
|
||||||
struct ephemeron_mark_state state = {
|
struct bdw_mark_state state = {
|
||||||
mark_stack_ptr,
|
mark_stack_ptr,
|
||||||
mark_stack_limit,
|
mark_stack_limit,
|
||||||
};
|
};
|
||||||
|
@ -182,7 +188,7 @@ mark_ephemeron(GC_word *addr, struct GC_ms_entry *mark_stack_ptr,
|
||||||
// If this ephemeron is on a freelist, its first word will be a
|
// If this ephemeron is on a freelist, its first word will be a
|
||||||
// freelist link and everything else will be NULL.
|
// freelist link and everything else will be NULL.
|
||||||
if (!gc_ref_value(gc_edge_ref(gc_ephemeron_value_edge(ephemeron)))) {
|
if (!gc_ref_value(gc_edge_ref(gc_ephemeron_value_edge(ephemeron)))) {
|
||||||
trace_ephemeron_edge(gc_edge(addr), NULL, &state);
|
bdw_mark_edge(gc_edge(addr), NULL, &state);
|
||||||
return state.mark_stack_ptr;
|
return state.mark_stack_ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,21 +198,76 @@ mark_ephemeron(GC_word *addr, struct GC_ms_entry *mark_stack_ptr,
|
||||||
gc_ephemeron_mark_dead(ephemeron);
|
gc_ephemeron_mark_dead(ephemeron);
|
||||||
}
|
}
|
||||||
|
|
||||||
gc_trace_ephemeron(ephemeron, trace_ephemeron_edge, NULL, &state);
|
gc_trace_ephemeron(ephemeron, bdw_mark_edge, NULL, &state);
|
||||||
|
|
||||||
|
return state.mark_stack_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct GC_ms_entry *
|
||||||
|
mark_heap(GC_word *addr, struct GC_ms_entry *mark_stack_ptr,
|
||||||
|
struct GC_ms_entry *mark_stack_limit, GC_word env) {
|
||||||
|
struct bdw_mark_state state = {
|
||||||
|
mark_stack_ptr,
|
||||||
|
mark_stack_limit,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct gc_heap *heap = (struct gc_heap*) addr;
|
||||||
|
|
||||||
|
// If this heap is on a freelist... well probably we are screwed, BDW
|
||||||
|
// isn't really made to do multiple heaps in a process. But still, in
|
||||||
|
// this case, the first word is the freelist and the rest are null.
|
||||||
|
if (heap->freelist) {
|
||||||
|
bdw_mark_edge(gc_edge(addr), NULL, &state);
|
||||||
|
return state.mark_stack_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (heap->roots)
|
||||||
|
gc_trace_heap_roots(heap->roots, bdw_mark_edge, heap, &state);
|
||||||
|
|
||||||
|
return state.mark_stack_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct GC_ms_entry *
|
||||||
|
mark_mutator(GC_word *addr, struct GC_ms_entry *mark_stack_ptr,
|
||||||
|
struct GC_ms_entry *mark_stack_limit, GC_word env) {
|
||||||
|
struct bdw_mark_state state = {
|
||||||
|
mark_stack_ptr,
|
||||||
|
mark_stack_limit,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct gc_mutator *mut = (struct gc_mutator*) addr;
|
||||||
|
|
||||||
|
// If this mutator is on a freelist, its first word will be a
|
||||||
|
// freelist link and everything else will be NULL.
|
||||||
|
if (!mut->heap) {
|
||||||
|
bdw_mark_edge(gc_edge(addr), NULL, &state);
|
||||||
|
return state.mark_stack_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i; i < GC_INLINE_FREELIST_COUNT; i++)
|
||||||
|
state.mark_stack_ptr = GC_MARK_AND_PUSH (mut->freelists[i],
|
||||||
|
state.mark_stack_ptr,
|
||||||
|
state.mark_stack_limit,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
state.mark_stack_ptr = GC_MARK_AND_PUSH (mut->heap,
|
||||||
|
state.mark_stack_ptr,
|
||||||
|
state.mark_stack_limit,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
if (mut->roots)
|
||||||
|
gc_trace_mutator_roots(mut->roots, bdw_mark_edge, mut->heap, &state);
|
||||||
|
|
||||||
return state.mark_stack_ptr;
|
return state.mark_stack_ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline struct gc_mutator *add_mutator(struct gc_heap *heap) {
|
static inline struct gc_mutator *add_mutator(struct gc_heap *heap) {
|
||||||
struct gc_mutator *ret = GC_malloc(sizeof(struct gc_mutator));
|
struct gc_mutator *ret =
|
||||||
|
GC_generic_malloc(sizeof(struct gc_mutator), mutator_gc_kind);
|
||||||
ret->heap = heap;
|
ret->heap = heap;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline struct gc_heap *mutator_heap(struct gc_mutator *mutator) {
|
|
||||||
return mutator->heap;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct gc_options {
|
struct gc_options {
|
||||||
struct gc_common_options common;
|
struct gc_common_options common;
|
||||||
};
|
};
|
||||||
|
@ -281,18 +342,26 @@ int gc_init(const struct gc_options *options, struct gc_stack_addr *stack_base,
|
||||||
if (options->common.heap_size > current_heap_size)
|
if (options->common.heap_size > current_heap_size)
|
||||||
GC_expand_hp(options->common.heap_size - current_heap_size);
|
GC_expand_hp(options->common.heap_size - current_heap_size);
|
||||||
GC_allow_register_threads();
|
GC_allow_register_threads();
|
||||||
*heap = GC_malloc(sizeof(struct gc_heap));
|
|
||||||
pthread_mutex_init(&(*heap)->lock, NULL);
|
|
||||||
*mutator = add_mutator(*heap);
|
|
||||||
|
|
||||||
{
|
{
|
||||||
GC_word descriptor = GC_MAKE_PROC(GC_new_proc(mark_ephemeron), 0);
|
|
||||||
int add_size_to_descriptor = 0;
|
int add_size_to_descriptor = 0;
|
||||||
int clear_memory = 1;
|
int clear_memory = 1;
|
||||||
ephemeron_gc_kind = GC_new_kind(GC_new_free_list(), descriptor,
|
|
||||||
|
heap_gc_kind = GC_new_kind(GC_new_free_list(),
|
||||||
|
GC_MAKE_PROC(GC_new_proc(mark_heap), 0),
|
||||||
|
add_size_to_descriptor, clear_memory);
|
||||||
|
mutator_gc_kind = GC_new_kind(GC_new_free_list(),
|
||||||
|
GC_MAKE_PROC(GC_new_proc(mark_mutator), 0),
|
||||||
|
add_size_to_descriptor, clear_memory);
|
||||||
|
ephemeron_gc_kind = GC_new_kind(GC_new_free_list(),
|
||||||
|
GC_MAKE_PROC(GC_new_proc(mark_ephemeron), 0),
|
||||||
add_size_to_descriptor, clear_memory);
|
add_size_to_descriptor, clear_memory);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*heap = GC_generic_malloc(sizeof(struct gc_heap), heap_gc_kind);
|
||||||
|
pthread_mutex_init(&(*heap)->lock, NULL);
|
||||||
|
*mutator = add_mutator(*heap);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -321,8 +390,10 @@ void* gc_call_without_gc(struct gc_mutator *mut,
|
||||||
|
|
||||||
void gc_mutator_set_roots(struct gc_mutator *mut,
|
void gc_mutator_set_roots(struct gc_mutator *mut,
|
||||||
struct gc_mutator_roots *roots) {
|
struct gc_mutator_roots *roots) {
|
||||||
|
mut->roots = roots;
|
||||||
}
|
}
|
||||||
void gc_heap_set_roots(struct gc_heap *heap, struct gc_heap_roots *roots) {
|
void gc_heap_set_roots(struct gc_heap *heap, struct gc_heap_roots *roots) {
|
||||||
|
heap->roots = roots;
|
||||||
}
|
}
|
||||||
void gc_heap_set_extern_space(struct gc_heap *heap,
|
void gc_heap_set_extern_space(struct gc_heap *heap,
|
||||||
struct gc_extern_space *space) {
|
struct gc_extern_space *space) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue