Skip to content

Commit

Permalink
Save thread context before yielding for GC (#78)
Browse files Browse the repository at this point in the history
This PR ports mmtk/mmtk-julia#159 to `dev`. The difference is that this PR adds a general call to the GC interface `jl_gc_notify_thread_yield`. In this case, each GC will do what they need in the call, and the context is saved in the GC specific TLS.
  • Loading branch information
qinsoon authored Dec 6, 2024
1 parent dd0a1c3 commit 8dc5535
Show file tree
Hide file tree
Showing 5 changed files with 30 additions and 0 deletions.
3 changes: 3 additions & 0 deletions src/gc-interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@ JL_DLLEXPORT int gc_is_collector_thread(int tid) JL_NOTSAFEPOINT;
JL_DLLEXPORT unsigned char jl_gc_pin_object(void* obj);
// Returns the version of which GC implementation is being used according to the list of supported GCs
JL_DLLEXPORT const char* jl_active_gc_impl(void);
// Notifies the GC that the given thread is about to yield for a GC. ctx is the ucontext for the thread
// if it is already fetched by the caller, otherwise it is NULL.
JL_DLLEXPORT void jl_gc_notify_thread_yield(jl_ptls_t ptls, void* ctx);

// TODO: The preserve hook functions may be temporary. We should see the performance impact of the change.

Expand Down
15 changes: 15 additions & 0 deletions src/gc-mmtk.c
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,8 @@ JL_DLLEXPORT void jl_mmtk_prepare_to_collect(void)
gc_num.total_time_to_safepoint += duration;

if (!jl_atomic_load_acquire(&jl_gc_disable_counter)) {
// This thread will yield.
jl_gc_notify_thread_yield(ptls, NULL);
JL_LOCK_NOGC(&finalizers_lock); // all the other threads are stopped, so this does not make sense, right? otherwise, failing that, this seems like plausibly a deadlock
#ifndef __clang_gcanalyzer__
mmtk_block_thread_for_gc();
Expand Down Expand Up @@ -303,6 +305,19 @@ JL_DLLEXPORT unsigned char jl_gc_pin_object(void* obj) {
return mmtk_pin_object(obj);
}

JL_DLLEXPORT void jl_gc_notify_thread_yield(jl_ptls_t ptls, void* ctx) {
if (ctx == NULL) {
// Save the context for the thread as it was running at the time of the call
int r = getcontext(&ptls->gc_tls.ctx_at_the_time_gc_started);
if (r == -1) {
jl_safe_printf("Failed to save context for conservative scanning\n");
abort();
}
return;
}
memcpy(&ptls->gc_tls.ctx_at_the_time_gc_started, ctx, sizeof(ucontext_t));
}

// ========================================================================= //
// GC Statistics
// ========================================================================= //
Expand Down
9 changes: 9 additions & 0 deletions src/gc-stock.c
Original file line number Diff line number Diff line change
Expand Up @@ -3361,6 +3361,11 @@ JL_DLLEXPORT void jl_gc_collect(jl_gc_collection_t collection)
gc_cblist_pre_gc, (collection));

if (!jl_atomic_load_acquire(&jl_gc_disable_counter)) {
// This thread will yield.
// jl_gc_notify_thread_yield does nothing for the stock GC at the point, but it may be non empty in the future,
// and this is a place where we should call jl_gc_notify_thread_yield.
// TODO: This call can be removed if requested.
jl_gc_notify_thread_yield(ptls, NULL);
JL_LOCK_NOGC(&finalizers_lock); // all the other threads are stopped, so this does not make sense, right? otherwise, failing that, this seems like plausibly a deadlock
#ifndef __clang_gcanalyzer__
if (_jl_gc_collect(ptls, collection)) {
Expand Down Expand Up @@ -4022,6 +4027,10 @@ JL_DLLEXPORT const char* jl_active_gc_impl(void) {
return "";
}

JL_DLLEXPORT void jl_gc_notify_thread_yield(jl_ptls_t ptls, void* ctx) {
// Do nothing before a thread yields
}

#ifdef __cplusplus
}
#endif
1 change: 1 addition & 0 deletions src/gc-tls-mmtk.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ extern "C" {
typedef struct {
MMTkMutatorContext mmtk_mutator;
size_t malloc_sz_since_last_poll;
ucontext_t ctx_at_the_time_gc_started;
} jl_gc_tls_states_t;

#ifdef __cplusplus
Expand Down
2 changes: 2 additions & 0 deletions src/signals-unix.c
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,8 @@ JL_NO_ASAN static void segv_handler(int sig, siginfo_t *info, void *context)
return;
}
if (sig == SIGSEGV && info->si_code == SEGV_ACCERR && jl_addr_is_safepoint((uintptr_t)info->si_addr) && !is_write_fault(context)) {
// TODO: We should do the same for other platforms
jl_gc_notify_thread_yield(ct->ptls, context);
jl_set_gc_and_wait();
// Do not raise sigint on worker thread
if (jl_atomic_load_relaxed(&ct->tid) != 0)
Expand Down

0 comments on commit 8dc5535

Please sign in to comment.