1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-04-30 03:40:34 +02:00

Tighten up typing of trace workers and trace worker data

This commit is contained in:
Andy Wingo 2024-07-08 18:44:24 +02:00
parent 5ff78f01c8
commit 247f9432a4
4 changed files with 78 additions and 79 deletions

View file

@ -23,7 +23,7 @@ enum trace_worker_state {
};
struct gc_heap;
struct trace_worker {
struct gc_trace_worker {
struct gc_heap *heap;
struct gc_tracer *tracer;
size_t id;
@ -31,7 +31,8 @@ struct trace_worker {
pthread_t thread;
enum trace_worker_state state;
pthread_mutex_t lock;
struct shared_worklist deque;
struct shared_worklist shared;
struct local_worklist local;
struct gc_trace_worker_data *data;
};
@ -44,17 +45,11 @@ struct gc_tracer {
long epoch;
pthread_mutex_t lock;
pthread_cond_t cond;
struct trace_worker workers[TRACE_WORKERS_MAX_COUNT];
};
struct local_tracer {
struct trace_worker *worker;
struct shared_worklist *share_deque;
struct local_worklist local;
struct gc_trace_worker workers[TRACE_WORKERS_MAX_COUNT];
};
static int
trace_worker_init(struct trace_worker *worker, struct gc_heap *heap,
trace_worker_init(struct gc_trace_worker *worker, struct gc_heap *heap,
struct gc_tracer *tracer, size_t id) {
worker->heap = heap;
worker->tracer = tracer;
@ -64,14 +59,15 @@ trace_worker_init(struct trace_worker *worker, struct gc_heap *heap,
worker->state = TRACE_WORKER_STOPPED;
pthread_mutex_init(&worker->lock, NULL);
worker->data = NULL;
return shared_worklist_init(&worker->deque);
local_worklist_init(&worker->local);
return shared_worklist_init(&worker->shared);
}
static void trace_worker_trace(struct trace_worker *worker);
static void trace_worker_trace(struct gc_trace_worker *worker);
static void*
trace_worker_thread(void *data) {
struct trace_worker *worker = data;
struct gc_trace_worker *worker = data;
struct gc_tracer *tracer = worker->tracer;
long trace_epoch = 0;
@ -88,7 +84,7 @@ trace_worker_thread(void *data) {
}
static int
trace_worker_spawn(struct trace_worker *worker) {
trace_worker_spawn(struct gc_trace_worker *worker) {
if (pthread_create(&worker->thread, NULL, trace_worker_thread, worker)) {
perror("spawning tracer thread failed");
return 0;
@ -129,7 +125,7 @@ static void gc_tracer_prepare(struct gc_tracer *tracer) {
}
static void gc_tracer_release(struct gc_tracer *tracer) {
for (size_t i = 0; i < tracer->worker_count; i++)
shared_worklist_release(&tracer->workers[i].deque);
shared_worklist_release(&tracer->workers[i].shared);
}
static inline void
@ -151,41 +147,40 @@ tracer_maybe_unpark_workers(struct gc_tracer *tracer) {
}
static inline void
tracer_share(struct local_tracer *trace) {
DEBUG("tracer #%zu: sharing\n", trace->worker->id);
tracer_share(struct gc_trace_worker *worker) {
DEBUG("tracer #%zu: sharing\n", worker->id);
size_t to_share = LOCAL_WORKLIST_SHARE_AMOUNT;
while (to_share) {
struct gc_ref *objv;
size_t count = local_worklist_pop_many(&trace->local, &objv, to_share);
shared_worklist_push_many(trace->share_deque, objv, count);
size_t count = local_worklist_pop_many(&worker->local, &objv, to_share);
shared_worklist_push_many(&worker->shared, objv, count);
to_share -= count;
}
tracer_maybe_unpark_workers(trace->worker->tracer);
tracer_maybe_unpark_workers(worker->tracer);
}
static inline void
gc_tracer_enqueue(struct gc_tracer *tracer, struct gc_ref ref,
void *trace_data) {
struct local_tracer *trace = trace_data;
if (local_worklist_full(&trace->local))
tracer_share(trace);
local_worklist_push(&trace->local, ref);
gc_trace_worker_enqueue(struct gc_trace_worker *worker, struct gc_ref ref) {
if (local_worklist_full(&worker->local))
tracer_share(worker);
local_worklist_push(&worker->local, ref);
}
static struct gc_ref
tracer_steal_from_worker(struct gc_tracer *tracer, size_t id) {
ASSERT(id < tracer->worker_count);
return shared_worklist_steal(&tracer->workers[id].deque);
return shared_worklist_steal(&tracer->workers[id].shared);
}
static int
tracer_can_steal_from_worker(struct gc_tracer *tracer, size_t id) {
ASSERT(id < tracer->worker_count);
return shared_worklist_can_steal(&tracer->workers[id].deque);
return shared_worklist_can_steal(&tracer->workers[id].shared);
}
static struct gc_ref
trace_worker_steal_from_any(struct trace_worker *worker, struct gc_tracer *tracer) {
trace_worker_steal_from_any(struct gc_trace_worker *worker,
struct gc_tracer *tracer) {
for (size_t i = 0; i < tracer->worker_count; i++) {
DEBUG("tracer #%zu: stealing from #%zu\n", worker->id, worker->steal_id);
struct gc_ref obj = tracer_steal_from_worker(tracer, worker->steal_id);
@ -201,7 +196,7 @@ trace_worker_steal_from_any(struct trace_worker *worker, struct gc_tracer *trace
}
static int
trace_worker_can_steal_from_any(struct trace_worker *worker,
trace_worker_can_steal_from_any(struct gc_trace_worker *worker,
struct gc_tracer *tracer) {
DEBUG("tracer #%zu: checking if any worker has tasks\n", worker->id);
for (size_t i = 0; i < tracer->worker_count; i++) {
@ -218,7 +213,7 @@ trace_worker_can_steal_from_any(struct trace_worker *worker,
}
static int
trace_worker_should_continue(struct trace_worker *worker) {
trace_worker_should_continue(struct gc_trace_worker *worker) {
// Helper workers should park themselves immediately if they have no work.
if (worker->id != 0)
return 0;
@ -252,8 +247,7 @@ trace_worker_should_continue(struct trace_worker *worker) {
}
static struct gc_ref
trace_worker_steal(struct local_tracer *trace) {
struct trace_worker *worker = trace->worker;
trace_worker_steal(struct gc_trace_worker *worker) {
struct gc_tracer *tracer = worker->tracer;
// It could be that the worker's local trace queue has simply
@ -261,7 +255,7 @@ trace_worker_steal(struct local_tracer *trace) {
// something from the worker's own queue.
{
DEBUG("tracer #%zu: trying to pop worker's own deque\n", worker->id);
struct gc_ref obj = shared_worklist_try_pop(&worker->deque);
struct gc_ref obj = shared_worklist_try_pop(&worker->shared);
if (gc_ref_is_heap_object(obj))
return obj;
}
@ -277,16 +271,10 @@ trace_worker_steal(struct local_tracer *trace) {
static void
trace_with_data(struct gc_tracer *tracer,
struct gc_heap *heap,
struct gc_trace_worker_data *worker_data,
void *data) {
struct trace_worker *worker = data;
struct gc_trace_worker *worker,
struct gc_trace_worker_data *data) {
atomic_fetch_add_explicit(&tracer->active_tracers, 1, memory_order_acq_rel);
worker->data = worker_data;
struct local_tracer trace;
trace.worker = worker;
trace.share_deque = &worker->deque;
local_worklist_init(&trace.local);
worker->data = data;
size_t n = 0;
DEBUG("tracer #%zu: running trace loop\n", worker->id);
@ -294,14 +282,14 @@ trace_with_data(struct gc_tracer *tracer,
do {
while (1) {
struct gc_ref ref;
if (!local_worklist_empty(&trace.local)) {
ref = local_worklist_pop(&trace.local);
if (!local_worklist_empty(&worker->local)) {
ref = local_worklist_pop(&worker->local);
} else {
ref = trace_worker_steal(&trace);
ref = trace_worker_steal(worker);
if (!gc_ref_is_heap_object(ref))
break;
}
trace_one(ref, heap, &trace);
trace_one(ref, heap, worker);
n++;
}
} while (trace_worker_should_continue(worker));
@ -313,21 +301,21 @@ trace_with_data(struct gc_tracer *tracer,
}
static void
trace_worker_trace(struct trace_worker *worker) {
trace_worker_trace(struct gc_trace_worker *worker) {
gc_trace_worker_call_with_data(trace_with_data, worker->tracer,
worker->heap, worker);
}
static inline void
gc_tracer_enqueue_root(struct gc_tracer *tracer, struct gc_ref ref) {
struct shared_worklist *worker0_deque = &tracer->workers[0].deque;
struct shared_worklist *worker0_deque = &tracer->workers[0].shared;
shared_worklist_push(worker0_deque, ref);
}
static inline void
gc_tracer_enqueue_roots(struct gc_tracer *tracer, struct gc_ref *objv,
size_t count) {
struct shared_worklist *worker0_deque = &tracer->workers[0].deque;
struct shared_worklist *worker0_deque = &tracer->workers[0].shared;
shared_worklist_push_many(worker0_deque, objv, count);
}
@ -337,7 +325,7 @@ gc_tracer_trace(struct gc_tracer *tracer) {
ssize_t parallel_threshold =
LOCAL_WORKLIST_SIZE - LOCAL_WORKLIST_SHARE_AMOUNT;
if (shared_worklist_size(&tracer->workers[0].deque) >= parallel_threshold) {
if (shared_worklist_size(&tracer->workers[0].shared) >= parallel_threshold) {
DEBUG("waking workers\n");
tracer_unpark_all_workers(tracer);
} else {

View file

@ -14,6 +14,11 @@ struct gc_tracer {
struct simple_worklist worklist;
};
struct gc_trace_worker {
struct gc_tracer *tracer;
struct gc_trace_worker_data *data;
};
static int
gc_tracer_init(struct gc_tracer *tracer, struct gc_heap *heap,
size_t parallelism) {
@ -35,25 +40,26 @@ gc_tracer_enqueue_roots(struct gc_tracer *tracer, struct gc_ref *objs,
simple_worklist_push_many(&tracer->worklist, objs, count);
}
static inline void
gc_tracer_enqueue(struct gc_tracer *tracer, struct gc_ref ref,
void *trace_data) {
gc_tracer_enqueue_root(tracer, ref);
gc_trace_worker_enqueue(struct gc_trace_worker *worker, struct gc_ref ref) {
gc_tracer_enqueue_root(worker->tracer, ref);
}
static inline void
tracer_trace_with_data(struct gc_tracer *tracer, struct gc_heap *heap,
struct gc_trace_worker_data *worker_data,
void *data) {
struct gc_trace_worker *worker,
struct gc_trace_worker_data *data) {
worker->data = data;
do {
struct gc_ref obj = simple_worklist_pop(&tracer->worklist);
if (!gc_ref_is_heap_object(obj))
break;
trace_one(obj, heap, NULL);
trace_one(obj, heap, worker);
} while (1);
}
static inline void
gc_tracer_trace(struct gc_tracer *tracer) {
struct gc_trace_worker worker = { tracer };
gc_trace_worker_call_with_data(tracer_trace_with_data, tracer, tracer->heap,
NULL);
&worker);
}
#endif // SERIAL_TRACER_H

View file

@ -6,24 +6,28 @@
struct gc_heap;
// Data types to be implemented by tracer.
struct gc_tracer;
struct gc_trace_worker;
// Data types to be implemented by collector.
struct gc_trace_worker_data;
////////////////////////////////////////////////////////////////////////
/// To be implemented by collector.
////////////////////////////////////////////////////////////////////////
struct gc_tracer;
struct gc_trace_worker_data;
// Visit all fields in an object.
static inline void trace_one(struct gc_ref ref, struct gc_heap *heap,
void *trace_data) GC_ALWAYS_INLINE;
struct gc_trace_worker *worker) GC_ALWAYS_INLINE;
static void
gc_trace_worker_call_with_data(void (*f)(struct gc_tracer *tracer,
struct gc_heap *heap,
struct gc_trace_worker_data *worker_data,
void *data),
struct gc_trace_worker *worker,
struct gc_trace_worker_data *data),
struct gc_tracer *tracer,
struct gc_heap *heap,
void *data);
struct gc_trace_worker *worker);
////////////////////////////////////////////////////////////////////////
/// To be implemented by tracer.
@ -47,9 +51,8 @@ static inline void gc_tracer_enqueue_roots(struct gc_tracer *tracer,
size_t count);
// Given that an object has been shaded grey, enqueue for tracing.
static inline void gc_tracer_enqueue(struct gc_tracer *tracer,
struct gc_ref ref,
void *trace_data) GC_ALWAYS_INLINE;
static inline void gc_trace_worker_enqueue(struct gc_trace_worker *worker,
struct gc_ref ref) GC_ALWAYS_INLINE;
// Run the full trace.
static inline void gc_tracer_trace(struct gc_tracer *tracer);

View file

@ -1097,20 +1097,21 @@ void gc_heap_set_extern_space(struct gc_heap *heap,
static void
gc_trace_worker_call_with_data(void (*f)(struct gc_tracer *tracer,
struct gc_heap *heap,
struct gc_trace_worker_data *worker_data,
void *data),
struct gc_trace_worker *worker,
struct gc_trace_worker_data *data),
struct gc_tracer *tracer,
struct gc_heap *heap,
void *data) {
f(tracer, heap, NULL, data);
struct gc_trace_worker *worker) {
f(tracer, heap, worker, NULL);
}
static inline void tracer_visit(struct gc_edge edge, struct gc_heap *heap,
void *trace_data) GC_ALWAYS_INLINE;
static inline void
tracer_visit(struct gc_edge edge, struct gc_heap *heap, void *trace_data) {
struct gc_trace_worker *worker = trace_data;
if (trace_edge(heap, edge))
gc_tracer_enqueue(&heap->tracer, gc_edge_ref(edge), trace_data);
gc_trace_worker_enqueue(worker, gc_edge_ref(edge));
}
static void trace_and_enqueue_locally(struct gc_edge edge,
@ -1193,15 +1194,16 @@ trace_conservative_edges(uintptr_t low,
static inline void tracer_trace_conservative_ref(struct gc_conservative_ref ref,
struct gc_heap *heap,
void *data) {
struct gc_trace_worker *worker = data;
int possibly_interior = 0;
struct gc_ref resolved = trace_conservative_ref(heap, ref, possibly_interior);
if (gc_ref_is_heap_object(resolved))
gc_tracer_enqueue(&heap->tracer, resolved, data);
gc_trace_worker_enqueue(worker, resolved);
}
static inline void trace_one_conservatively(struct gc_ref ref,
struct gc_heap *heap,
void *mark_data) {
struct gc_trace_worker *worker) {
size_t bytes;
if (GC_LIKELY(mark_space_contains(heap_mark_space(heap), ref))) {
// Generally speaking we trace conservatively and don't allow much
@ -1211,7 +1213,7 @@ static inline void trace_one_conservatively(struct gc_ref ref,
uint8_t meta = *metadata_byte_for_addr(gc_ref_value(ref));
if (GC_UNLIKELY(meta & METADATA_BYTE_EPHEMERON)) {
gc_trace_ephemeron(gc_ref_heap_object(ref), tracer_visit, heap,
mark_data);
worker);
return;
}
bytes = mark_space_object_size(heap_mark_space(heap), ref);
@ -1221,15 +1223,15 @@ static inline void trace_one_conservatively(struct gc_ref ref,
trace_conservative_edges(gc_ref_value(ref),
gc_ref_value(ref) + bytes,
tracer_trace_conservative_ref, heap,
mark_data);
worker);
}
static inline void trace_one(struct gc_ref ref, struct gc_heap *heap,
void *mark_data) {
struct gc_trace_worker *worker) {
if (gc_has_conservative_intraheap_edges())
trace_one_conservatively(ref, heap, mark_data);
trace_one_conservatively(ref, heap, worker);
else
gc_trace_object(ref, tracer_visit, heap, mark_data, NULL);
gc_trace_object(ref, tracer_visit, heap, worker, NULL);
}
static void