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.
Trac metadata
Trac field | Value |
---|---|
Version | 7.4.1 |
Type | Bug |
TypeOfFailure | OtherFailure |
Priority | normal |
Resolution | Unresolved |
Component | libraries/base |
Test case | |
Differential revisions | |
BlockedBy | |
Related | |
Blocking | |
CC | |
Operating system | |
Architecture |