diff --git a/api/gc-api.h b/api/gc-api.h index 6cf783703..821891bca 100644 --- a/api/gc-api.h +++ b/api/gc-api.h @@ -34,6 +34,10 @@ struct gc_heap_roots; GC_API_ void gc_heap_set_roots(struct gc_heap *heap, struct gc_heap_roots *roots); +struct gc_extern_space; +GC_API_ void gc_heap_set_extern_space(struct gc_heap *heap, + struct gc_extern_space *space); + GC_API_ struct gc_mutator* gc_init_for_thread(struct gc_stack_addr *base, struct gc_heap *heap); GC_API_ void gc_finish_for_thread(struct gc_mutator *mut); diff --git a/api/gc-embedder-api.h b/api/gc-embedder-api.h index 6e39f05ea..31793316f 100644 --- a/api/gc-embedder-api.h +++ b/api/gc-embedder-api.h @@ -17,9 +17,17 @@ struct gc_heap_roots; struct gc_atomic_forward; struct gc_heap; struct gc_ephemeron; +struct gc_extern_space; GC_EMBEDDER_API inline int gc_is_valid_conservative_ref_displacement(uintptr_t displacement); +GC_EMBEDDER_API inline int gc_extern_space_mark(struct gc_extern_space *space, + struct gc_ref ref) GC_ALWAYS_INLINE; +GC_EMBEDDER_API inline void gc_extern_space_start_gc(struct gc_extern_space *space, + int is_minor_gc); +GC_EMBEDDER_API inline void gc_extern_space_finish_gc(struct gc_extern_space *space, + int is_minor_gc); + GC_EMBEDDER_API inline void gc_trace_object(struct gc_ref ref, void (*visit)(struct gc_edge edge, struct gc_heap *heap, diff --git a/benchmarks/simple-gc-embedder.h b/benchmarks/simple-gc-embedder.h index 14fb142e7..d4276192f 100644 --- a/benchmarks/simple-gc-embedder.h +++ b/benchmarks/simple-gc-embedder.h @@ -18,6 +18,18 @@ gc_is_valid_conservative_ref_displacement(uintptr_t displacement) { #endif } +// No external objects in simple benchmarks. +static inline int gc_extern_space_mark(struct gc_extern_space *space, + struct gc_ref ref) { + GC_CRASH(); +} +static inline void gc_extern_space_start_gc(struct gc_extern_space *space, + int is_minor_gc) { +} +static inline void gc_extern_space_finish_gc(struct gc_extern_space *space, + int is_minor_gc) { +} + static inline void gc_trace_object(struct gc_ref ref, void (*trace_edge)(struct gc_edge edge, struct gc_heap *heap, diff --git a/doc/manual.md b/doc/manual.md index 41ff83d91..e94095635 100644 --- a/doc/manual.md +++ b/doc/manual.md @@ -163,6 +163,19 @@ embedder should return 1 only if the displacement is 0, but if the program allows low-bit tagged pointers, then it should also return 1 for those pointer tags. +### External objects + +Sometimes a system will allocate objects outside the GC, for example on +the stack or in static data sections. To support this use case, Whippet +allows the embedder to provide a `struct gc_extern_space` +implementation. Whippet will call `gc_extern_space_start_gc` at the +start of each collection, and `gc_extern_space_finish_gc` at the end. +External objects will be visited by `gc_extern_space_mark`, which should +return nonzero if the object hasn't been seen before and needs to be +traced via `gc_trace_object` (coloring the object grey). Note, +`gc_extern_space_mark` may be called concurrently from many threads; be +prepared! + ## Configuration, compilation, and linking To the user, Whippet presents an abstract API that does not encode the diff --git a/src/bdw.c b/src/bdw.c index cf17f19e7..3b01e9dcb 100644 --- a/src/bdw.c +++ b/src/bdw.c @@ -325,6 +325,9 @@ void gc_mutator_set_roots(struct gc_mutator *mut, } void gc_heap_set_roots(struct gc_heap *heap, struct gc_heap_roots *roots) { } +void gc_heap_set_extern_space(struct gc_heap *heap, + struct gc_extern_space *space) { +} void gc_print_stats(struct gc_heap *heap) { printf("Completed %ld collections\n", (long)GC_get_gc_no()); diff --git a/src/semi.c b/src/semi.c index 11b74ec5f..151bafb83 100644 --- a/src/semi.c +++ b/src/semi.c @@ -37,12 +37,14 @@ struct gc_heap { struct semi_space semi_space; struct large_object_space large_object_space; struct gc_pending_ephemerons *pending_ephemerons; + struct gc_extern_space *extern_space; double pending_ephemerons_size_factor; double pending_ephemerons_size_slop; size_t size; long count; int check_pending_ephemerons; const struct gc_options *options; + struct gc_heap_roots *roots; }; // One mutator per space, can just store the heap in the mutator. struct gc_mutator { @@ -195,6 +197,17 @@ static int semi_space_contains(struct semi_space *space, struct gc_ref ref) { return region_contains(&space->from_space, addr); } +static void visit_external_object(struct gc_heap *heap, + struct gc_extern_space *space, + struct gc_ref ref) { + if (gc_extern_space_mark(space, ref)) { + if (GC_UNLIKELY(heap->check_pending_ephemerons)) + gc_resolve_pending_ephemerons(ref, heap); + + gc_trace_object(ref, trace, heap, NULL, NULL); + } +} + static void visit(struct gc_edge edge, struct gc_heap *heap) { struct gc_ref ref = gc_edge_ref(edge); if (!gc_ref_is_heap_object(ref)) @@ -204,7 +217,7 @@ static void visit(struct gc_edge edge, struct gc_heap *heap) { else if (large_object_space_contains(heap_large_object_space(heap), ref)) visit_large_object_space(heap, heap_large_object_space(heap), ref); else - GC_CRASH(); + visit_external_object(heap, heap->extern_space, ref); } struct gc_pending_ephemerons * @@ -329,10 +342,13 @@ static void collect(struct gc_mutator *mut, size_t for_alloc) { struct large_object_space *large = heap_large_object_space(heap); // fprintf(stderr, "start collect #%ld:\n", space->count); large_object_space_start_gc(large, 0); + gc_extern_space_start_gc(heap->extern_space, 0); flip(semi); heap->count++; heap->check_pending_ephemerons = 0; uintptr_t grey = semi->hp; + if (heap->roots) + gc_trace_heap_roots(heap->roots, trace, heap, NULL); if (mut->roots) gc_trace_mutator_roots(mut->roots, trace, heap, NULL); // fprintf(stderr, "pushed %zd bytes in roots\n", space->hp - grey); @@ -344,6 +360,7 @@ static void collect(struct gc_mutator *mut, size_t for_alloc) { while(grey < semi->hp) grey = scan(heap, gc_ref(grey)); large_object_space_finish_gc(large, 0); + gc_extern_space_finish_gc(heap->extern_space, 0); semi_space_finish_gc(semi, large->live_pages_at_last_collection); gc_sweep_pending_ephemerons(heap->pending_ephemerons, 0, 1); adjust_heap_size_and_limits(heap, for_alloc); @@ -485,11 +502,13 @@ unsigned gc_heap_ephemeron_trace_epoch(struct gc_heap *heap) { } static int heap_init(struct gc_heap *heap, const struct gc_options *options) { + heap->extern_space = NULL; heap->pending_ephemerons_size_factor = 0.01; heap->pending_ephemerons_size_slop = 0.5; heap->count = 0; heap->options = options; heap->size = options->common.heap_size; + heap->roots = NULL; return heap_prepare_pending_ephemerons(heap); } @@ -559,7 +578,11 @@ void gc_mutator_set_roots(struct gc_mutator *mut, mut->roots = roots; } void gc_heap_set_roots(struct gc_heap *heap, struct gc_heap_roots *roots) { - GC_CRASH(); + heap->roots = roots; +} +void gc_heap_set_extern_space(struct gc_heap *heap, + struct gc_extern_space *space) { + heap->extern_space = space; } struct gc_mutator* gc_init_for_thread(struct gc_stack_addr *base, diff --git a/src/whippet.c b/src/whippet.c index 4771e37e9..ae247482b 100644 --- a/src/whippet.c +++ b/src/whippet.c @@ -300,6 +300,7 @@ enum gc_kind { struct gc_heap { struct mark_space mark_space; struct large_object_space large_object_space; + struct gc_extern_space *extern_space; size_t large_object_pages; pthread_mutex_t lock; pthread_cond_t collector_cond; @@ -360,6 +361,9 @@ static inline struct mark_space* heap_mark_space(struct gc_heap *heap) { static inline struct large_object_space* heap_large_object_space(struct gc_heap *heap) { return &heap->large_object_space; } +static inline struct gc_extern_space* heap_extern_space(struct gc_heap *heap) { + return heap->extern_space; +} static inline struct gc_heap* mutator_heap(struct gc_mutator *mutator) { return mutator->heap; } @@ -667,7 +671,7 @@ static inline int do_trace(struct gc_heap *heap, struct gc_edge edge, return large_object_space_mark_object(heap_large_object_space(heap), ref); else - GC_CRASH(); + return gc_extern_space_mark(heap_extern_space(heap), ref); } static inline int trace_edge(struct gc_heap *heap, struct gc_edge edge) { @@ -1078,6 +1082,10 @@ void gc_mutator_set_roots(struct gc_mutator *mut, 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, + struct gc_extern_space *space) { + heap->extern_space = space; +} static void trace_and_enqueue_locally(struct gc_edge edge, struct gc_heap *heap, @@ -1803,6 +1811,7 @@ static void collect(struct gc_mutator *mut) { struct gc_heap *heap = mutator_heap(mut); struct mark_space *space = heap_mark_space(heap); struct large_object_space *lospace = heap_large_object_space(heap); + struct gc_extern_space *exspace = heap_extern_space(heap); if (maybe_grow_heap(heap)) { DEBUG("grew heap instead of collecting #%ld:\n", heap->count); return; @@ -1811,6 +1820,7 @@ static void collect(struct gc_mutator *mut) { enum gc_kind gc_kind = determine_collection_kind(heap); update_mark_patterns(space, !(gc_kind & GC_KIND_FLAG_MINOR)); large_object_space_start_gc(lospace, gc_kind & GC_KIND_FLAG_MINOR); + gc_extern_space_start_gc(exspace, gc_kind & GC_KIND_FLAG_MINOR); resolve_ephemerons_lazily(heap); tracer_prepare(heap); request_mutators_to_stop(heap); @@ -1832,6 +1842,7 @@ static void collect(struct gc_mutator *mut) { tracer_release(heap); mark_space_finish_gc(space, gc_kind); large_object_space_finish_gc(lospace, gc_kind & GC_KIND_FLAG_MINOR); + gc_extern_space_finish_gc(exspace, gc_kind & GC_KIND_FLAG_MINOR); heap->count++; heap->last_collection_was_minor = gc_kind & GC_KIND_FLAG_MINOR; if (heap->last_collection_was_minor)