module Control.Concurrent.HierarchyInternal where
import Control.Concurrent.Lifted (ThreadId, forkWithUnmask,
killThread, myThreadId)
import Control.Concurrent.MVar.Lifted (MVar, newEmptyMVar, newMVar,
putMVar, readMVar)
import Control.Concurrent.STM.TVar (TVar, modifyTVar', newTVarIO,
readTVarIO)
import Control.Exception.Lifted (AsyncException (ThreadKilled),
catch, finally, mask_)
import Control.Monad.Base (MonadBase, liftBase)
import Control.Monad.STM (atomically)
import Control.Monad.Trans.Control (MonadBaseControl)
import Data.Foldable (forM_)
import Data.Map.Strict (Map, delete, elems, empty,
insert, keys)
newtype FinishMarker = FinishMarker (MVar ()) deriving (Eq)
newtype ThreadMap = ThreadMap (TVar (Map ThreadId FinishMarker))
newThreadMap :: MonadBase IO m => m ThreadMap
newThreadMap = liftBase $ ThreadMap <$> newTVarIO empty
newChild
:: MonadBaseControl IO m
=> ThreadMap
-> (ThreadMap -> m ())
-> m ThreadId
newChild brothers@(ThreadMap bMap) action = do
finishMarker <- FinishMarker <$> newEmptyMVar
children <- newThreadMap
mask_ $ do
child <- forkWithUnmask $ \unmask ->
unmask (action children) `finally` cleanup finishMarker brothers children
liftBase . atomically $ modifyTVar' bMap (insert child finishMarker)
return child
killThreadHierarchy
:: MonadBaseControl IO m
=> ThreadMap
-> m ()
killThreadHierarchy (ThreadMap children) = do
currentChildren <- liftBase $ readTVarIO children
mapM_ killThread $ keys currentChildren
remainingChildren <- liftBase $ readTVarIO children
mapM_ (\(FinishMarker marker) -> readMVar marker) $ elems remainingChildren
killThreadHierarchyInternal
:: MonadBaseControl IO m
=> ThreadMap
-> m ()
killThreadHierarchyInternal (ThreadMap children) = do
currentChildren <- liftBase $ readTVarIO children
forM_ (keys currentChildren) $ \child ->
killThread child `catch` (\ThreadKilled -> killThread child)
remainingChildren <- liftBase $ readTVarIO children
forM_ (elems remainingChildren) $ \(FinishMarker marker) ->
readMVar marker `catch` (\ThreadKilled -> readMVar marker)
cleanup :: MonadBaseControl IO m => FinishMarker -> ThreadMap -> ThreadMap -> m ()
cleanup (FinishMarker marker) (ThreadMap brotherMap) children = do
killThreadHierarchyInternal children
myThread <- myThreadId
liftBase . atomically $ modifyTVar' brotherMap (delete myThread)
putMVar marker ()