module Wobsurv.Util.MasterThread where
import BasePrelude hiding (forkFinally)
import Control.Monad.Trans.Reader
import qualified BasePrelude
import qualified STMContainers.Set as Set
import qualified Wobsurv.Util.PartialHandler as H
type MasterThread =
ReaderT Context IO
type Context =
(Set.Set ThreadId)
type MT =
MasterThread
run :: MT a -> IO a
run mt =
do
context <- atomically $ Set.new
catch (runReaderT mt context) $ \(e :: SomeException) -> do
traverse_ killThread =<< do
atomically $ Set.foldM (\l -> return . (: l)) [] context
atomically $ Set.null context >>= bool retry (return ())
throwIO e
forkFinally :: MT () -> IO () -> MT ThreadId
forkFinally main finalizer =
ReaderT $ \context -> do
thread <- myThreadId
slaveContext <- atomically $ Set.new
let
onDeath r =
do
do
r' <- try $ finalizer
forM_ (left r <|> left r') $
H.toTotal $ H.onThreadKilled (return ()) <> H.rethrowTo thread
do
traverse_ killThread =<< do
atomically $ Set.foldM (\l -> return . (: l)) [] slaveContext
slaveThread <- myThreadId
atomically $ do
Set.null slaveContext >>= \case
True -> Set.delete slaveThread context
False -> retry
where
left = either Just (const Nothing)
slaveThread <- BasePrelude.forkFinally (runReaderT main slaveContext) onDeath
atomically $ Set.insert slaveThread context
return slaveThread
fork :: MT () -> MT ThreadId
fork main =
forkFinally main (return ())
runWithoutForking :: MT a -> IO a
runWithoutForking mt =
runReaderT mt (error "Attempt to fork when run with 'runWithoutForking'")