From 476c16d2324c5e8dc27c324e2062fbe8e6686de4 Mon Sep 17 00:00:00 2001 From: Iain Buclaw Date: Thu, 11 Sep 2025 18:33:17 +0200 Subject: [PATCH] druntime: Move unblockGCSignals to core.internal.gc.os Limit the number of platforms that this is done on. A inspection of some libc implementations of fork has identified the main culprits, don't need to apply this to any others. MacOS testsuite also regressed as a result on calling this code, it's not clear why, but the backtrace is: ``` * thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0) * frame #0: 0x00007ff81abe6ee3 libsystem_platform.dylib`_os_unfair_lock_recursive_abort + 23 frame #1: 0x00007ff81abe12da libsystem_platform.dylib`_os_unfair_lock_lock_slow + 247 frame #2: 0x00007ff81abccd44 libsystem_pthread.dylib`_pthread_atfork_prepare_handlers + 48 frame #3: 0x00007ff825dc2705 libSystem.B.dylib`libSystem_atfork_prepare + 25 frame #4: 0x00007ff81aac17e1 libsystem_c.dylib`fork + 24 frame #5: 0x0000000101f730ee test_runner`core.internal.backtrace.dwarf.resolveAddressesWithAtos(Location[]) + 210 ``` --- .../core/internal/gc/impl/conservative/gc.d | 24 ++----------- druntime/src/core/internal/gc/os.d | 34 +++++++++++++++++++ 2 files changed, 36 insertions(+), 22 deletions(-) diff --git a/druntime/src/core/internal/gc/impl/conservative/gc.d b/druntime/src/core/internal/gc/impl/conservative/gc.d index 591e0dc9dc87..322486e2082b 100644 --- a/druntime/src/core/internal/gc/impl/conservative/gc.d +++ b/druntime/src/core/internal/gc/impl/conservative/gc.d @@ -3495,30 +3495,10 @@ Lmark: __gshared bool fork_needs_lock = true; // racing condition with cocurrent calls of fork? - // The GC signals might be blocked by `fork` when the atfork prepare - // handler is invoked. This guards us from the scenario where we are - // waiting for a GC action in another thread to complete, and that thread - // decides to call thread_suspendAll, then we must be able to response to - // that request, otherwise we end up in a deadlock situation. - private static void unblockGCSignals() nothrow @nogc - { - import core.sys.posix.signal : pthread_sigmask, sigaddset, sigemptyset, sigset_t, SIG_UNBLOCK; - - int suspendSignal = void, resumeSignal = void; - thread_getGCSignals(suspendSignal, resumeSignal); - - sigset_t set; - sigemptyset(&set); - sigaddset(&set, suspendSignal); - sigaddset(&set, resumeSignal); - - auto sigmask = pthread_sigmask(SIG_UNBLOCK, &set, null); - assert(sigmask == 0, "failed to unblock GC signals"); - } - extern(C) static void _d_gcx_atfork_prepare() { - unblockGCSignals(); + static if (__traits(compiles, os_unblock_gc_signals)) + os_unblock_gc_signals(); if (instance && fork_needs_lock) ConservativeGC.lockNR(); diff --git a/druntime/src/core/internal/gc/os.d b/druntime/src/core/internal/gc/os.d index 291aee535bc6..a301664d34be 100644 --- a/druntime/src/core/internal/gc/os.d +++ b/druntime/src/core/internal/gc/os.d @@ -85,6 +85,13 @@ else version (Posix) return ChildStatus.done; } + version (DragonFlyBSD) + version = GCSignalsUnblock; + version (FreeBSD) + version = GCSignalsUnblock; + version (Solaris) + version = GCSignalsUnblock; + //version = GC_Use_Alloc_MMap; } else @@ -315,3 +322,30 @@ else version (Posix) return pageSize * pages; } } + +/** + The GC signals might be blocked by `fork` when the atfork prepare + handler is invoked. This guards us from the scenario where we are + waiting for a GC action in another thread to complete, and that thread + decides to call thread_suspendAll, then we must be able to response to + that request, otherwise we end up in a deadlock situation. + */ +version (GCSignalsUnblock) +{ + void os_unblock_gc_signals() nothrow @nogc + { + import core.sys.posix.signal : pthread_sigmask, sigaddset, sigemptyset, sigset_t, SIG_UNBLOCK; + import core.thread : thread_getGCSignals; + + int suspendSignal = void, resumeSignal = void; + thread_getGCSignals(suspendSignal, resumeSignal); + + sigset_t set; + sigemptyset(&set); + sigaddset(&set, suspendSignal); + sigaddset(&set, resumeSignal); + + auto sigmask = pthread_sigmask(SIG_UNBLOCK, &set, null); + assert(sigmask == 0, "failed to unblock GC signals"); + } +}