Ticket #6126 (closed bug: fixed)
Fix risk of dead-lock in documentation of Control.Concurrent
The current documentation of Control.Concurrent proposes a function to wait for a child thread to complete:
myForkIO :: IO () -> IO (MVar ()) myForkIO io = do mvar <- newEmptyMVar forkIO (io `finally` putMVar mvar ()) return mvar
This function has the risk of causing a dead-lock. If an asynchronous exception if thrown before the putMVar exception handler is installed the mvar will remain empty. This causes a dead-lock when the mvar is taken.
The attached patch fixes this problem by correctly masking asynchronous exceptions before forking. Also, instead of returning the mvar it returns a computation that waits for the child thread to complete. This is safer than returning the mvar itself since a user can't accidentally put the mvar (which will dead-lock) or take the mvar (which when executed twice will dead-lock).
The attached patch additionally rewrites the function to wait for a group of threads to complete. Instead of keeping a list of MVars, I use a counter (stored in a TVar) that counts the number of running threads. The counter is incremented when a thread if forked and decremented when it completes. Waiting for the group of threads to complete is accomplished by checking if the counter has reached 0 and if not retry the transaction.