module SlaveThread
(
fork,
forkFinally,
)
where
import BasePrelude hiding (forkFinally)
import Control.Monad.Trans.Reader
import Control.Monad.Morph
import qualified STMContainers.Multimap as Multimap
import qualified PartialHandler
import qualified ListT
slaves :: Multimap.Multimap ThreadId ThreadId
slaves =
unsafePerformIO $ Multimap.newIO
fork :: IO a -> IO ThreadId
fork main =
forkFinally (return ()) main
forkFinally :: IO a -> IO b -> IO ThreadId
forkFinally finalizer computation =
do
masterThread <- myThreadId
semaphore <- newEmptyMVar
slaveThread <-
mask $ \restore -> forkIO $ do
slaveThread <- myThreadId
atomically $ Multimap.insert slaveThread masterThread slaves
putMVar semaphore ()
r <- try $ restore computation
killSlaves slaveThread
waitForSlavesToDie slaveThread
forM_ (left r) $
PartialHandler.totalizeRethrowingTo_ masterThread $
PartialHandler.onThreadKilled (return ())
try finalizer >>= \r ->
forM_ (left r) $ PartialHandler.totalizeRethrowingTo_ masterThread $ mempty
atomically $ Multimap.delete slaveThread masterThread slaves
takeMVar semaphore
return slaveThread
where
left = either Just (const Nothing)
killSlaves :: ThreadId -> IO ()
killSlaves thread =
ListT.traverse_ killThread $ hoist atomically $ Multimap.streamByKey thread slaves
waitForSlavesToDie :: ThreadId -> IO ()
waitForSlavesToDie thread =
atomically $ do
null <- ListT.null $ Multimap.streamByKey thread slaves
if null
then return ()
else retry