Ticket #1391 (closed merge: fixed)
forkProcess() in Schedule.c with -threaded should initialize mutexes in child process (POSIX)
|Reported by:||thorkilnaur||Owned by:||igloo|
|Operating System:||MacOS X||Architecture:||powerpc|
|Type of failure:||Difficulty:||Unknown|
|Test Case:||forkprocess01(ghci)||Blocked By:|
forkProcess() in Schedule.c implements System.Posix.Process.forkProcess essentially by fork()'ing. In http://www.gnu.org/software/libc/manual/html_node/Threads-and-Fork.html we read that
It's not intuitively obvious what should happen when a multi-threaded POSIX process calls fork. ... fork duplicates the whole memory space, including mutexes in their current locking state, but only the calling thread: other threads are not running in the child process. The mutexes are not usable after the fork and must be initialized with pthread_mutex_init in the child process.
Although a lot of things happen in forkProcess() in Schedule.c in the child process after fork() returns, this initialization of mutexes and related delicate matters are not done.
On my PPC Mac OS X 10.4.9, I have observed this to eventually result in the child process getting a SIGSEGV (signal 11; segmentation fault) when run via ghc --interactive. The failing test case forkprocess01(ghci) for PPC Mac OS X is an example of this.
The case of forkprocess01 failing when run via ghci --interactive comes about because ghc itself is linked with -threaded. With a ghc linked without -threaded, the segmentation fault does not happen.
When executing forkprocess01 compiled with --make -threaded, the error again cannot be reproduced. But this seems to be because forkprocess01 itself does not use multiple threads. With a slightly extended version of forkprocess01 called forkprocess03:
-- forkprocess03.hs: -- Test that we can call exitFailure in a forked process, and have it -- communicated properly to the parent. -- Do this (forkprocess01) within a forkIO'ed child process. import System.Exit import System.Posix.Process import Control.Concurrent main0 = do p <- forkProcess $ exitWith (ExitFailure 72) r <- getProcessStatus True False p print r main = do p <- forkIO $ main0 threadDelay 10000000 print p
the erroneous reaction can be observed when compiled with -threaded using a fairly recent ghc HEAD:
$ /Users/thorkilnaur/tn/GHCDarcsRepository/ghc-HEAD-for-HughesPJ-wrong-fill-indent-20070506_1304/ghc/compiler/stage2/ghc-inplace --version The Glorious Glasgow Haskell Compilation System, version 6.7.20070513 $ touch forkprocess03.hs $ /Users/thorkilnaur/tn/GHCDarcsRepository/ghc-HEAD-for-HughesPJ-wrong-fill-indent-20070506_1304/ghc/compiler/stage2/ghc-inplace --make forkprocess03 -threaded [1 of 1] Compiling Main ( forkprocess03.hs, forkprocess03.o ) Linking forkprocess03 ... $ ./forkprocess03 Just (Terminated 11) ThreadId 4 $
Whereas without -threaded, the program seems to run fine:
$ touch forkprocess03.hs $ /Users/thorkilnaur/tn/GHCDarcsRepository/ghc-HEAD-for-HughesPJ-wrong-fill-indent-20070506_1304/ghc/compiler/stage2/ghc-inplace --make forkprocess03 [1 of 1] Compiling Main ( forkprocess03.hs, forkprocess03.o ) Linking forkprocess03 ... $ ./forkprocess03 Just (Exited (ExitFailure 72)) ThreadId 2 $
The repair, however, is not particularly obvious. The above reference suggests using pthread_atfork to set up handlers to lock all mutexes before fork()'ing and subsequently unlock them in the parent and initializing them in the child. But even if this is chosen as the way forward, additional matters need to be clarified, to ensure that such handling plays well with the rest of the threaded runtime system and also retains the Windows variant of things. Also, if anything is done about this, #1185 should probably considered as well.