Skip to content

Commit

Permalink
macOS: extend the workaround to cover the dyld/exc_server deadlock is…
Browse files Browse the repository at this point in the history
…sue, since 12.1

Later, we should probably switch to using mach_exc_server generated from
`mig mach_exc.defs`.
  • Loading branch information
vtjnash committed Jan 12, 2022
1 parent 2939272 commit 267b124
Showing 1 changed file with 16 additions and 9 deletions.
25 changes: 16 additions & 9 deletions src/signals-mach.c
Original file line number Diff line number Diff line change
Expand Up @@ -526,27 +526,30 @@ static kern_return_t profiler_segv_handler
}
#endif

static int jl_lock_profile_mach(void)
// WARNING: we are unable to handle sigsegv while the dlsymlock is held
static int jl_lock_profile_mach(int dlsymlock)
{
jl_lock_profile();
// workaround for old keymgr bugs
void *unused = NULL;
int keymgr_locked = _keymgr_get_and_lock_processwide_ptr_2(KEYMGR_GCC3_DW2_OBJ_LIST, &unused) == 0;
if (_dyld_atfork_prepare != NULL && _dyld_atfork_parent != NULL)
// workaround for new dlsym4 bugs (API and bugs introduced in macOS 12.1)
if (dlsymlock && _dyld_atfork_prepare != NULL && _dyld_atfork_parent != NULL)
_dyld_atfork_prepare();
return keymgr_locked;
}

static void jl_unlock_profile_mach(int keymgr_locked)
static void jl_unlock_profile_mach(int dlsymlock, int keymgr_locked)
{
if (_dyld_atfork_prepare != NULL && _dyld_atfork_parent != NULL)
_dyld_atfork_parent();
if (dlsymlock && _dyld_atfork_prepare != NULL && _dyld_atfork_parent != NULL) \
_dyld_atfork_parent(); \
if (keymgr_locked)
_keymgr_unlock_processwide_ptr(KEYMGR_GCC3_DW2_OBJ_LIST);
jl_unlock_profile();
}

#define jl_lock_profile() int keymgr_locked = jl_lock_profile_mach()
#define jl_unlock_profile() jl_unlock_profile_mach(keymgr_locked)
#define jl_lock_profile() int keymgr_locked = jl_lock_profile_mach(1)
#define jl_unlock_profile() jl_unlock_profile_mach(1, keymgr_locked)

void *mach_profile_listener(void *arg)
{
Expand All @@ -564,7 +567,7 @@ void *mach_profile_listener(void *arg)
HANDLE_MACH_ERROR("mach_msg", ret);
// sample each thread, round-robin style in reverse order
// (so that thread zero gets notified last)
jl_lock_profile();
int keymgr_locked = jl_lock_profile_mach(0);
jl_shuffle_int_array_inplace(profile_round_robin_thread_order, jl_n_threads, &profile_cong_rng_seed);
for (int idx = jl_n_threads; idx-- > 0; ) {
// Stop the threads in the random round-robin order.
Expand All @@ -575,9 +578,13 @@ void *mach_profile_listener(void *arg)
break;
}

if (_dyld_atfork_prepare != NULL && _dyld_atfork_parent != NULL)
_dyld_atfork_prepare(); // briefly acquire the dlsym lock
host_thread_state_t state;
jl_thread_suspend_and_get_state2(i, &state);
unw_context_t *uc = (unw_context_t*)&state;
if (_dyld_atfork_prepare != NULL && _dyld_atfork_parent != NULL)
_dyld_atfork_parent(); // quickly release the dlsym lock

if (running) {
#ifdef LLVMLIBUNWIND
Expand Down Expand Up @@ -634,7 +641,7 @@ void *mach_profile_listener(void *arg)
// We're done! Resume the thread.
jl_thread_resume(i, 0);
}
jl_unlock_profile();
jl_unlock_profile_mach(0, keymgr_locked);
if (running) {
// Reset the alarm
kern_return_t ret = clock_alarm(clk, TIME_RELATIVE, timerprof, profile_port);
Expand Down

0 comments on commit 267b124

Please sign in to comment.