Simon Jakobi pushed to branch wip/sjakobi/nonmoving-gc-timeout at Glasgow Haskell Compiler / GHC Commits: 45952452 by Simon Jakobi at 2026-07-22T23:36:29+02:00 rts: Reinitialize nonmoving GC worker after fork The persistent nonmoving GC worker is not copied into the child of a forkProcess call. Consequently, child shutdown waits indefinitely for a worker which does not exist. Wait for concurrent marking to become idle before forking. Avoid the capability/GC lock-order inversion and recreate the worker in the child. Make T12903 wait for the child so that the testsuite observes this bug. Assisted-by: gpt-5.6-sol via Codex CLI - - - - - 4 changed files: - rts/Schedule.c - rts/sm/NonMoving.c - rts/sm/NonMoving.h - testsuite/tests/rts/T12903.hs Changes: ===================================== rts/Schedule.c ===================================== @@ -2051,7 +2051,26 @@ forkProcess(HsStablePtr *entry waitForCapability(&cap, task); #if defined(THREADED_RTS) - stopAllCapabilities(&cap, task); + while (true) { + stopAllCapabilities(&cap, task); + + // The nonmoving collector's worker is not copied by fork(). Only + // proceed when it is idle, keeping its lock held to prevent another + // collection from starting before the fork. We must not wait for an + // active mark while holding capabilities since the mark may need to + // synchronize with the mutator before it can finish. + if (nonmovingBlockConcurrentMark(false)) { + break; + } + + releaseAllCapabilities(n_capabilities, NULL, task); + CHECK(nonmovingBlockConcurrentMark(true)); + nonmovingUnblockConcurrentMark(); + cap = NULL; + waitForCapability(&cap, task); + } +#else + CHECK(nonmovingBlockConcurrentMark(false)); #endif // no funny business: hold locks while we fork, otherwise if some @@ -2089,6 +2108,8 @@ forkProcess(HsStablePtr *entry if (pid) { // parent + nonmovingUnblockConcurrentMark(); + RELEASE_LOCK(&sched_mutex); RELEASE_LOCK(&sm_mutex); RELEASE_LOCK(&stable_ptr_mutex); @@ -2213,6 +2234,10 @@ forkProcess(HsStablePtr *entry generations[g].threads = END_TSO_QUEUE; } + // The persistent nonmoving collector worker is an OS thread and was + // not copied by fork(). Recreate it before running the child action. + nonmovingInitAfterFork(); + // The timer thread is not present in the child process, so we need // to initialise the timer again. initTimer(); ===================================== rts/sm/NonMoving.c ===================================== @@ -754,6 +754,12 @@ void nonmovingInit(void) nonmovingMarkInit(); } +void nonmovingInitAfterFork(void) +{ + if (! RtsFlags.GcFlags.useNonmoving) return; + nonmovingInitConcurrentWorker(); +} + void nonmovingExit(void) { if (! RtsFlags.GcFlags.useNonmoving) return; ===================================== rts/sm/NonMoving.h ===================================== @@ -150,6 +150,7 @@ extern struct NonmovingHeap nonmovingHeap; extern memcount nonmoving_segment_live_words; void nonmovingInit(void); +void nonmovingInitAfterFork(void); void nonmovingExit(void); bool nonmovingConcurrentMarkIsRunning(void); ===================================== testsuite/tests/rts/T12903.hs ===================================== @@ -1,5 +1,6 @@ import Control.Concurrent import Control.Exception +import System.Exit import System.IO import System.Posix import System.Posix.IO @@ -19,3 +20,5 @@ main = do "registered" <- hGetLine hdl signalProcess sigINT pid putStrLn =<< hGetLine hdl + Just (Exited ExitSuccess) <- getProcessStatus True False pid + return () View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/4595245280273f0cfef9dc3c8cbe85a8... -- View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/4595245280273f0cfef9dc3c8cbe85a8... You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
participants (1)
-
Simon Jakobi (@sjakobi)