module Ki.Internal.Scope
( Scope,
scoped,
awaitAll,
fork,
forkWith,
forkWith_,
fork_,
forkTry,
forkTryWith,
)
where
import Control.Concurrent (ThreadId, myThreadId, throwTo)
import Control.Concurrent.MVar (MVar, newEmptyMVar, tryPutMVar, tryTakeMVar)
import Control.Exception
( Exception (fromException, toException),
MaskingState (..),
SomeAsyncException,
SomeException,
asyncExceptionFromException,
asyncExceptionToException,
throwIO,
try,
uninterruptibleMask,
pattern ErrorCall,
)
import Control.Monad (guard, when)
import Data.Foldable (for_)
import Data.Functor (void)
import Data.IntMap (IntMap)
import qualified Data.IntMap.Lazy as IntMap.Lazy
import Data.Void (Void, absurd)
import GHC.Conc
( STM,
TVar,
atomically,
enableAllocationLimit,
labelThread,
newTVarIO,
readTVar,
retry,
setAllocationCounter,
throwSTM,
writeTVar,
)
import GHC.Conc.Sync (readTVarIO)
import GHC.IO (unsafeUnmask)
import IntSupply (IntSupply)
import qualified IntSupply
import Ki.Internal.ByteCount (byteCountToInt64)
import Ki.Internal.IO
( IOResult (..),
UnexceptionalIO (..),
assertM,
exceptionIs,
interruptiblyMasked,
unexceptionalTry,
unexceptionalTryEither,
uninterruptiblyMasked,
)
import Ki.Internal.NonblockingSTM
import Ki.Internal.Propagating (Tid, peelOffPropagating, propagate)
import Ki.Internal.Thread (Thread, makeThread)
import Ki.Internal.ThreadAffinity (forkWithAffinity)
import Ki.Internal.ThreadOptions (ThreadOptions (..), defaultThreadOptions)
data Scope = Scope
{
Scope -> MVar SomeException
childExceptionVar :: {-# UNPACK #-} !(MVar SomeException),
Scope -> TVar (IntMap ThreadId)
childrenVar :: {-# UNPACK #-} !(TVar (IntMap ThreadId)),
Scope -> IntSupply
nextChildIdSupply :: {-# UNPACK #-} !IntSupply,
Scope -> ThreadId
parentThreadId :: {-# UNPACK #-} !ThreadId,
Scope -> TVar ScopeStatus
statusVar :: {-# UNPACK #-} !(TVar ScopeStatus)
}
type ScopeStatus = Int
pattern Open :: Int
pattern $mOpen :: forall {r}. ScopeStatus -> ((# #) -> r) -> ((# #) -> r) -> r
Open <- ((>= 0) -> True)
pattern Closing :: Int
pattern $mClosing :: forall {r}. ScopeStatus -> ((# #) -> r) -> ((# #) -> r) -> r
$bClosing :: ScopeStatus
Closing = -1
pattern Closed :: Int
pattern $mClosed :: forall {r}. ScopeStatus -> ((# #) -> r) -> ((# #) -> r) -> r
$bClosed :: ScopeStatus
Closed = -2
{-# COMPLETE Open, Closing, Closed #-}
data ScopeClosing
= ScopeClosing
instance Show ScopeClosing where
show :: ScopeClosing -> String
show ScopeClosing
_ = String
"<<internal ki exception: scope closing>>"
instance Exception ScopeClosing where
toException :: ScopeClosing -> SomeException
toException = ScopeClosing -> SomeException
forall e. Exception e => e -> SomeException
asyncExceptionToException
fromException :: SomeException -> Maybe ScopeClosing
fromException = SomeException -> Maybe ScopeClosing
forall e. Exception e => SomeException -> Maybe e
asyncExceptionFromException
scoped :: (Scope -> IO a) -> IO a
scoped :: forall a. (Scope -> IO a) -> IO a
scoped Scope -> IO a
action = do
scope :: Scope
scope@Scope {MVar SomeException
$sel:childExceptionVar:Scope :: Scope -> MVar SomeException
childExceptionVar :: MVar SomeException
childExceptionVar, TVar (IntMap ThreadId)
$sel:childrenVar:Scope :: Scope -> TVar (IntMap ThreadId)
childrenVar :: TVar (IntMap ThreadId)
childrenVar, TVar ScopeStatus
$sel:statusVar:Scope :: Scope -> TVar ScopeStatus
statusVar :: TVar ScopeStatus
statusVar} <- IO Scope
allocateScope
((forall a. IO a -> IO a) -> IO a) -> IO a
forall b. ((forall a. IO a -> IO a) -> IO b) -> IO b
uninterruptibleMask \forall a. IO a -> IO a
restore -> do
Either SomeException a
result <- IO a -> IO (Either SomeException a)
forall e a. Exception e => IO a -> IO (Either e a)
try (IO a -> IO a
forall a. IO a -> IO a
restore (Scope -> IO a
action Scope
scope))
!IntMap ThreadId
runningChildren <- do
STM (IntMap ThreadId) -> IO (IntMap ThreadId)
forall a. STM a -> IO a
atomically do
ScopeStatus
starting <- TVar ScopeStatus -> STM ScopeStatus
forall a. TVar a -> STM a
readTVar TVar ScopeStatus
statusVar
Bool -> STM ()
forall (m :: * -> *). Applicative m => Bool -> m ()
assertM (ScopeStatus
starting ScopeStatus -> ScopeStatus -> Bool
forall a. Ord a => a -> a -> Bool
>= ScopeStatus
0)
Bool -> STM ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (ScopeStatus
starting ScopeStatus -> ScopeStatus -> Bool
forall a. Eq a => a -> a -> Bool
== ScopeStatus
0)
TVar ScopeStatus -> ScopeStatus -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar ScopeStatus
statusVar ScopeStatus
Closing
TVar (IntMap ThreadId) -> STM (IntMap ThreadId)
forall a. TVar a -> STM a
readTVar TVar (IntMap ThreadId)
childrenVar
IntMap ThreadId -> (ThreadId -> IO ()) -> IO ()
forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
t a -> (a -> f b) -> f ()
for_ IntMap ThreadId
runningChildren \ThreadId
child -> ThreadId -> ScopeClosing -> IO ()
forall e. Exception e => ThreadId -> e -> IO ()
throwTo ThreadId
child ScopeClosing
ScopeClosing
STM () -> IO ()
forall a. STM a -> IO a
atomically do
IntMap ThreadId
children <- TVar (IntMap ThreadId) -> STM (IntMap ThreadId)
forall a. TVar a -> STM a
readTVar TVar (IntMap ThreadId)
childrenVar
Bool -> STM ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (IntMap ThreadId -> Bool
forall a. IntMap a -> Bool
IntMap.Lazy.null IntMap ThreadId
children)
TVar ScopeStatus -> ScopeStatus -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar ScopeStatus
statusVar ScopeStatus
Closed
case Either SomeException a
result of
Left SomeException
exception -> SomeException -> IO a
forall e a. Exception e => e -> IO a
throwIO (SomeException -> SomeException
peelOffPropagating SomeException
exception)
Right a
value ->
MVar SomeException -> IO (Maybe SomeException)
forall a. MVar a -> IO (Maybe a)
tryTakeMVar MVar SomeException
childExceptionVar IO (Maybe SomeException) -> (Maybe SomeException -> IO a) -> IO a
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
Maybe SomeException
Nothing -> a -> IO a
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure a
value
Just SomeException
exception -> SomeException -> IO a
forall e a. Exception e => e -> IO a
throwIO SomeException
exception
allocateScope :: IO Scope
allocateScope :: IO Scope
allocateScope = do
MVar SomeException
childExceptionVar <- IO (MVar SomeException)
forall a. IO (MVar a)
newEmptyMVar
TVar (IntMap ThreadId)
childrenVar <- IntMap ThreadId -> IO (TVar (IntMap ThreadId))
forall a. a -> IO (TVar a)
newTVarIO IntMap ThreadId
forall a. IntMap a
IntMap.Lazy.empty
IntSupply
nextChildIdSupply <- IO IntSupply
IntSupply.new
ThreadId
parentThreadId <- IO ThreadId
myThreadId
TVar ScopeStatus
statusVar <- ScopeStatus -> IO (TVar ScopeStatus)
forall a. a -> IO (TVar a)
newTVarIO ScopeStatus
0
Scope -> IO Scope
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Scope {MVar SomeException
$sel:childExceptionVar:Scope :: MVar SomeException
childExceptionVar :: MVar SomeException
childExceptionVar, TVar (IntMap ThreadId)
$sel:childrenVar:Scope :: TVar (IntMap ThreadId)
childrenVar :: TVar (IntMap ThreadId)
childrenVar, IntSupply
$sel:nextChildIdSupply:Scope :: IntSupply
nextChildIdSupply :: IntSupply
nextChildIdSupply, ThreadId
$sel:parentThreadId:Scope :: ThreadId
parentThreadId :: ThreadId
parentThreadId, TVar ScopeStatus
$sel:statusVar:Scope :: TVar ScopeStatus
statusVar :: TVar ScopeStatus
statusVar}
spawn :: Scope -> ThreadOptions -> (Tid -> (forall x. IO x -> IO x) -> UnexceptionalIO ()) -> IO ChildIds
spawn :: Scope
-> ThreadOptions
-> (ScopeStatus -> (forall a. IO a -> IO a) -> UnexceptionalIO ())
-> IO ChildIds
spawn scope :: Scope
scope@Scope {TVar (IntMap ThreadId)
$sel:childrenVar:Scope :: Scope -> TVar (IntMap ThreadId)
childrenVar :: TVar (IntMap ThreadId)
childrenVar, TVar ScopeStatus
$sel:statusVar:Scope :: Scope -> TVar ScopeStatus
statusVar :: TVar ScopeStatus
statusVar} ThreadOptions
options ScopeStatus -> (forall a. IO a -> IO a) -> UnexceptionalIO ()
action = do
IO ChildIds -> IO ChildIds
forall a. IO a -> IO a
interruptiblyMasked do
NonblockingSTM () -> IO ()
forall a. NonblockingSTM a -> IO a
nonblockingAtomically do
ScopeStatus
status <- TVar ScopeStatus -> NonblockingSTM ScopeStatus
forall a. TVar a -> NonblockingSTM a
nonblockingReadTVar TVar ScopeStatus
statusVar
Bool -> NonblockingSTM ()
forall (m :: * -> *). Applicative m => Bool -> m ()
assertM (ScopeStatus
status ScopeStatus -> ScopeStatus -> Bool
forall a. Ord a => a -> a -> Bool
>= -ScopeStatus
2)
case ScopeStatus
status of
ScopeStatus
Open -> TVar ScopeStatus -> ScopeStatus -> NonblockingSTM ()
forall a. TVar a -> a -> NonblockingSTM ()
nonblockingWriteTVar' TVar ScopeStatus
statusVar (ScopeStatus
status ScopeStatus -> ScopeStatus -> ScopeStatus
forall a. Num a => a -> a -> a
+ ScopeStatus
1)
ScopeStatus
Closing -> ScopeClosing -> NonblockingSTM ()
forall e x. Exception e => e -> NonblockingSTM x
nonblockingThrowSTM ScopeClosing
ScopeClosing
ScopeStatus
Closed -> ErrorCall -> NonblockingSTM ()
forall e x. Exception e => e -> NonblockingSTM x
nonblockingThrowSTM (String -> ErrorCall
ErrorCall String
"ki: scope closed")
ChildIds
childIds <- Scope
-> ThreadOptions
-> (ScopeStatus -> (forall a. IO a -> IO a) -> UnexceptionalIO ())
-> IO ChildIds
spawnChild Scope
scope ThreadOptions
options ScopeStatus -> (forall a. IO a -> IO a) -> UnexceptionalIO ()
action
NonblockingSTM () -> IO ()
forall a. NonblockingSTM a -> IO a
nonblockingAtomically do
ScopeStatus
starting <- TVar ScopeStatus -> NonblockingSTM ScopeStatus
forall a. TVar a -> NonblockingSTM a
nonblockingReadTVar TVar ScopeStatus
statusVar
Bool -> NonblockingSTM ()
forall (m :: * -> *). Applicative m => Bool -> m ()
assertM (ScopeStatus
starting ScopeStatus -> ScopeStatus -> Bool
forall a. Ord a => a -> a -> Bool
>= ScopeStatus
1)
TVar ScopeStatus -> ScopeStatus -> NonblockingSTM ()
forall a. TVar a -> a -> NonblockingSTM ()
nonblockingWriteTVar' TVar ScopeStatus
statusVar (ScopeStatus
starting ScopeStatus -> ScopeStatus -> ScopeStatus
forall a. Num a => a -> a -> a
- ScopeStatus
1)
TVar (IntMap ThreadId) -> ChildIds -> NonblockingSTM ()
recordChild TVar (IntMap ThreadId)
childrenVar ChildIds
childIds
ChildIds -> IO ChildIds
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ChildIds
childIds
data ChildIds
= ChildIds
{-# UNPACK #-} !Tid
{-# UNPACK #-} !ThreadId
spawnChild :: Scope -> ThreadOptions -> (Tid -> (forall x. IO x -> IO x) -> UnexceptionalIO ()) -> IO ChildIds
spawnChild :: Scope
-> ThreadOptions
-> (ScopeStatus -> (forall a. IO a -> IO a) -> UnexceptionalIO ())
-> IO ChildIds
spawnChild Scope
scope ThreadOptions
options ScopeStatus -> (forall a. IO a -> IO a) -> UnexceptionalIO ()
action = do
ScopeStatus
childId <- IntSupply -> IO ScopeStatus
IntSupply.next IntSupply
nextChildIdSupply
ThreadId
childThreadId <-
ThreadAffinity -> IO () -> IO ThreadId
forkWithAffinity ThreadAffinity
affinity do
Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Bool -> Bool
not (String -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null String
label)) do
ThreadId
childThreadId <- IO ThreadId
myThreadId
ThreadId -> String -> IO ()
labelThread ThreadId
childThreadId String
label
Maybe ByteCount -> (ByteCount -> IO ()) -> IO ()
forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
t a -> (a -> f b) -> f ()
for_ Maybe ByteCount
allocationLimit \ByteCount
bytes -> do
Int64 -> IO ()
setAllocationCounter (ByteCount -> Int64
byteCountToInt64 ByteCount
bytes)
IO ()
enableAllocationLimit
let
atRequestedMaskingState :: IO a -> IO a
atRequestedMaskingState :: forall a. IO a -> IO a
atRequestedMaskingState =
case MaskingState
requestedChildMaskingState of
MaskingState
Unmasked -> IO a -> IO a
forall a. IO a -> IO a
unsafeUnmask
MaskingState
MaskedInterruptible -> IO a -> IO a
forall a. a -> a
id
MaskingState
MaskedUninterruptible -> IO a -> IO a
forall a. IO a -> IO a
uninterruptiblyMasked
UnexceptionalIO () -> IO ()
forall a. UnexceptionalIO a -> IO a
runUnexceptionalIO (ScopeStatus -> (forall a. IO a -> IO a) -> UnexceptionalIO ()
action ScopeStatus
childId IO x -> IO x
forall a. IO a -> IO a
atRequestedMaskingState)
NonblockingSTM () -> IO ()
forall a. NonblockingSTM a -> IO a
nonblockingAtomically (TVar (IntMap ThreadId) -> ScopeStatus -> NonblockingSTM ()
unrecordChild TVar (IntMap ThreadId)
childrenVar ScopeStatus
childId)
ChildIds -> IO ChildIds
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (ScopeStatus -> ThreadId -> ChildIds
ChildIds ScopeStatus
childId ThreadId
childThreadId)
where
Scope {TVar (IntMap ThreadId)
$sel:childrenVar:Scope :: Scope -> TVar (IntMap ThreadId)
childrenVar :: TVar (IntMap ThreadId)
childrenVar, IntSupply
$sel:nextChildIdSupply:Scope :: Scope -> IntSupply
nextChildIdSupply :: IntSupply
nextChildIdSupply} = Scope
scope
ThreadOptions {ThreadAffinity
affinity :: ThreadAffinity
$sel:affinity:ThreadOptions :: ThreadOptions -> ThreadAffinity
affinity, Maybe ByteCount
allocationLimit :: Maybe ByteCount
$sel:allocationLimit:ThreadOptions :: ThreadOptions -> Maybe ByteCount
allocationLimit, String
label :: String
$sel:label:ThreadOptions :: ThreadOptions -> String
label, $sel:maskingState:ThreadOptions :: ThreadOptions -> MaskingState
maskingState = MaskingState
requestedChildMaskingState} = ThreadOptions
options
{-# INLINE spawnChild #-}
recordChild :: TVar (IntMap ThreadId) -> ChildIds -> NonblockingSTM ()
recordChild :: TVar (IntMap ThreadId) -> ChildIds -> NonblockingSTM ()
recordChild TVar (IntMap ThreadId)
childrenVar (ChildIds ScopeStatus
childId ThreadId
childThreadId) = do
IntMap ThreadId
children <- TVar (IntMap ThreadId) -> NonblockingSTM (IntMap ThreadId)
forall a. TVar a -> NonblockingSTM a
nonblockingReadTVar TVar (IntMap ThreadId)
childrenVar
TVar (IntMap ThreadId) -> IntMap ThreadId -> NonblockingSTM ()
forall a. TVar a -> a -> NonblockingSTM ()
nonblockingWriteTVar' TVar (IntMap ThreadId)
childrenVar ((Maybe ThreadId -> Maybe ThreadId)
-> ScopeStatus -> IntMap ThreadId -> IntMap ThreadId
forall a.
(Maybe a -> Maybe a) -> ScopeStatus -> IntMap a -> IntMap a
IntMap.Lazy.alter (Maybe ThreadId
-> (ThreadId -> Maybe ThreadId) -> Maybe ThreadId -> Maybe ThreadId
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (ThreadId -> Maybe ThreadId
forall a. a -> Maybe a
Just ThreadId
childThreadId) (Maybe ThreadId -> ThreadId -> Maybe ThreadId
forall a b. a -> b -> a
const Maybe ThreadId
forall a. Maybe a
Nothing)) ScopeStatus
childId IntMap ThreadId
children)
unrecordChild :: TVar (IntMap ThreadId) -> Tid -> NonblockingSTM ()
unrecordChild :: TVar (IntMap ThreadId) -> ScopeStatus -> NonblockingSTM ()
unrecordChild TVar (IntMap ThreadId)
childrenVar ScopeStatus
childId = do
IntMap ThreadId
children <- TVar (IntMap ThreadId) -> NonblockingSTM (IntMap ThreadId)
forall a. TVar a -> NonblockingSTM a
nonblockingReadTVar TVar (IntMap ThreadId)
childrenVar
TVar (IntMap ThreadId) -> IntMap ThreadId -> NonblockingSTM ()
forall a. TVar a -> a -> NonblockingSTM ()
nonblockingWriteTVar' TVar (IntMap ThreadId)
childrenVar ((Maybe ThreadId -> Maybe ThreadId)
-> ScopeStatus -> IntMap ThreadId -> IntMap ThreadId
forall a.
(Maybe a -> Maybe a) -> ScopeStatus -> IntMap a -> IntMap a
IntMap.Lazy.alter (Maybe ThreadId
-> (ThreadId -> Maybe ThreadId) -> Maybe ThreadId -> Maybe ThreadId
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (ThreadId -> Maybe ThreadId
forall a. a -> Maybe a
Just ThreadId
forall a. HasCallStack => a
undefined) (Maybe ThreadId -> ThreadId -> Maybe ThreadId
forall a b. a -> b -> a
const Maybe ThreadId
forall a. Maybe a
Nothing)) ScopeStatus
childId IntMap ThreadId
children)
awaitAll :: Scope -> STM ()
awaitAll :: Scope -> STM ()
awaitAll Scope {TVar (IntMap ThreadId)
$sel:childrenVar:Scope :: Scope -> TVar (IntMap ThreadId)
childrenVar :: TVar (IntMap ThreadId)
childrenVar, TVar ScopeStatus
$sel:statusVar:Scope :: Scope -> TVar ScopeStatus
statusVar :: TVar ScopeStatus
statusVar} = do
IntMap ThreadId
children <- TVar (IntMap ThreadId) -> STM (IntMap ThreadId)
forall a. TVar a -> STM a
readTVar TVar (IntMap ThreadId)
childrenVar
Bool -> STM ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (IntMap ThreadId -> Bool
forall a. IntMap a -> Bool
IntMap.Lazy.null IntMap ThreadId
children)
ScopeStatus
status <- TVar ScopeStatus -> STM ScopeStatus
forall a. TVar a -> STM a
readTVar TVar ScopeStatus
statusVar
Bool -> STM ()
forall (m :: * -> *). Applicative m => Bool -> m ()
assertM (ScopeStatus
status ScopeStatus -> ScopeStatus -> Bool
forall a. Ord a => a -> a -> Bool
>= -ScopeStatus
2)
case ScopeStatus
status of
ScopeStatus
Open -> Bool -> STM ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (ScopeStatus
status ScopeStatus -> ScopeStatus -> Bool
forall a. Eq a => a -> a -> Bool
== ScopeStatus
0)
ScopeStatus
Closing -> STM ()
forall a. STM a
retry
ScopeStatus
Closed -> () -> STM ()
forall a. a -> STM a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
fork :: Scope -> IO a -> IO (Thread a)
fork :: forall a. Scope -> IO a -> IO (Thread a)
fork Scope
scope =
Scope -> ThreadOptions -> IO a -> IO (Thread a)
forall a. Scope -> ThreadOptions -> IO a -> IO (Thread a)
forkWith Scope
scope ThreadOptions
defaultThreadOptions
fork_ :: Scope -> IO Void -> IO ()
fork_ :: Scope -> IO Void -> IO ()
fork_ Scope
scope =
Scope -> ThreadOptions -> IO Void -> IO ()
forkWith_ Scope
scope ThreadOptions
defaultThreadOptions
forkWith :: Scope -> ThreadOptions -> IO a -> IO (Thread a)
forkWith :: forall a. Scope -> ThreadOptions -> IO a -> IO (Thread a)
forkWith Scope
scope ThreadOptions
opts IO a
action = do
TVar (Result a)
resultVar <- Result a -> IO (TVar (Result a))
forall a. a -> IO (TVar a)
newTVarIO Result a
forall a. Result a
NoResultYet
let done :: Result a -> UnexceptionalIO ()
done Result a
result = IO () -> UnexceptionalIO ()
forall a. IO a -> UnexceptionalIO a
UnexceptionalIO (STM () -> IO ()
forall a. STM a -> IO a
atomically (TVar (Result a) -> Result a -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar (Result a)
resultVar Result a
result))
ChildIds ScopeStatus
_ ThreadId
childThreadId <-
Scope
-> ThreadOptions
-> (ScopeStatus -> (forall a. IO a -> IO a) -> UnexceptionalIO ())
-> IO ChildIds
spawn Scope
scope ThreadOptions
opts \ScopeStatus
childId forall a. IO a -> IO a
masking -> do
IO a -> UnexceptionalIO (IOResult a)
forall a. IO a -> UnexceptionalIO (IOResult a)
unexceptionalTry (IO a -> IO a
forall a. IO a -> IO a
masking IO a
action) UnexceptionalIO (IOResult a)
-> (IOResult a -> UnexceptionalIO ()) -> UnexceptionalIO ()
forall a b.
UnexceptionalIO a -> (a -> UnexceptionalIO b) -> UnexceptionalIO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
Failure SomeException
exception -> do
Bool -> UnexceptionalIO () -> UnexceptionalIO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Bool -> Bool
not (forall e. Exception e => SomeException -> Bool
exceptionIs @ScopeClosing SomeException
exception)) do
Scope -> ScopeStatus -> SomeException -> UnexceptionalIO ()
propagateException Scope
scope ScopeStatus
childId SomeException
exception
Result a -> UnexceptionalIO ()
done (SomeException -> Result a
forall a. SomeException -> Result a
BadResult SomeException
exception)
Success a
value -> Result a -> UnexceptionalIO ()
done (a -> Result a
forall a. a -> Result a
GoodResult a
value)
let doAwait :: STM a
doAwait =
TVar (Result a) -> STM (Result a)
forall a. TVar a -> STM a
readTVar TVar (Result a)
resultVar STM (Result a) -> (Result a -> STM a) -> STM a
forall a b. STM a -> (a -> STM b) -> STM b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
Result a
NoResultYet -> STM a
forall a. STM a
retry
BadResult SomeException
exception -> SomeException -> STM a
forall e a. Exception e => e -> STM a
throwSTM SomeException
exception
GoodResult a
value -> a -> STM a
forall a. a -> STM a
forall (f :: * -> *) a. Applicative f => a -> f a
pure a
value
Thread a -> IO (Thread a)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (ThreadId -> STM a -> Thread a
forall a. ThreadId -> STM a -> Thread a
makeThread ThreadId
childThreadId STM a
doAwait)
forkWith_ :: Scope -> ThreadOptions -> IO Void -> IO ()
forkWith_ :: Scope -> ThreadOptions -> IO Void -> IO ()
forkWith_ Scope
scope ThreadOptions
opts IO Void
action = do
ChildIds
_childThreadId <-
Scope
-> ThreadOptions
-> (ScopeStatus -> (forall a. IO a -> IO a) -> UnexceptionalIO ())
-> IO ChildIds
spawn Scope
scope ThreadOptions
opts \ScopeStatus
childId forall a. IO a -> IO a
masking ->
(SomeException -> UnexceptionalIO ())
-> (Void -> UnexceptionalIO ()) -> IO Void -> UnexceptionalIO ()
forall a b.
(SomeException -> UnexceptionalIO b)
-> (a -> UnexceptionalIO b) -> IO a -> UnexceptionalIO b
unexceptionalTryEither
( \SomeException
exception ->
Bool -> UnexceptionalIO () -> UnexceptionalIO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Bool -> Bool
not (forall e. Exception e => SomeException -> Bool
exceptionIs @ScopeClosing SomeException
exception)) do
Scope -> ScopeStatus -> SomeException -> UnexceptionalIO ()
propagateException Scope
scope ScopeStatus
childId SomeException
exception
)
Void -> UnexceptionalIO ()
forall a. Void -> a
absurd
(IO Void -> IO Void
forall a. IO a -> IO a
masking IO Void
action)
() -> IO ()
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
forkTry :: forall e a. (Exception e) => Scope -> IO a -> IO (Thread (Either e a))
forkTry :: forall e a.
Exception e =>
Scope -> IO a -> IO (Thread (Either e a))
forkTry Scope
scope =
Scope -> ThreadOptions -> IO a -> IO (Thread (Either e a))
forall e a.
Exception e =>
Scope -> ThreadOptions -> IO a -> IO (Thread (Either e a))
forkTryWith Scope
scope ThreadOptions
defaultThreadOptions
data Result a
= NoResultYet
| BadResult !SomeException
| GoodResult a
forkTryWith :: forall e a. (Exception e) => Scope -> ThreadOptions -> IO a -> IO (Thread (Either e a))
forkTryWith :: forall e a.
Exception e =>
Scope -> ThreadOptions -> IO a -> IO (Thread (Either e a))
forkTryWith Scope
scope ThreadOptions
opts IO a
action = do
TVar (Result a)
resultVar <- Result a -> IO (TVar (Result a))
forall a. a -> IO (TVar a)
newTVarIO Result a
forall a. Result a
NoResultYet
let done :: Result a -> UnexceptionalIO ()
done Result a
result = IO () -> UnexceptionalIO ()
forall a. IO a -> UnexceptionalIO a
UnexceptionalIO (STM () -> IO ()
forall a. STM a -> IO a
atomically (TVar (Result a) -> Result a -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar (Result a)
resultVar Result a
result))
ChildIds ScopeStatus
_ ThreadId
childThreadId <-
Scope
-> ThreadOptions
-> (ScopeStatus -> (forall a. IO a -> IO a) -> UnexceptionalIO ())
-> IO ChildIds
spawn Scope
scope ThreadOptions
opts \ScopeStatus
childId forall a. IO a -> IO a
masking -> do
IOResult a
result <- IO a -> UnexceptionalIO (IOResult a)
forall a. IO a -> UnexceptionalIO (IOResult a)
unexceptionalTry (IO a -> IO a
forall a. IO a -> IO a
masking IO a
action)
case IOResult a
result of
Failure SomeException
exception -> do
let shouldPropagate :: Bool
shouldPropagate =
if forall e. Exception e => SomeException -> Bool
exceptionIs @e SomeException
exception
then forall e. Exception e => SomeException -> Bool
exceptionIs @SomeAsyncException SomeException
exception
else Bool -> Bool
not (forall e. Exception e => SomeException -> Bool
exceptionIs @ScopeClosing SomeException
exception)
Bool -> UnexceptionalIO () -> UnexceptionalIO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when Bool
shouldPropagate (Scope -> ScopeStatus -> SomeException -> UnexceptionalIO ()
propagateException Scope
scope ScopeStatus
childId SomeException
exception)
Result a -> UnexceptionalIO ()
done (SomeException -> Result a
forall a. SomeException -> Result a
BadResult SomeException
exception)
Success a
value -> Result a -> UnexceptionalIO ()
done (a -> Result a
forall a. a -> Result a
GoodResult a
value)
let doAwait :: STM (Either e a)
doAwait =
TVar (Result a) -> STM (Result a)
forall a. TVar a -> STM a
readTVar TVar (Result a)
resultVar STM (Result a)
-> (Result a -> STM (Either e a)) -> STM (Either e a)
forall a b. STM a -> (a -> STM b) -> STM b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
Result a
NoResultYet -> STM (Either e a)
forall a. STM a
retry
BadResult SomeException
exception ->
case forall e. Exception e => SomeException -> Maybe e
fromException @e SomeException
exception of
Maybe e
Nothing -> SomeException -> STM (Either e a)
forall e a. Exception e => e -> STM a
throwSTM SomeException
exception
Just e
expectedException -> Either e a -> STM (Either e a)
forall a. a -> STM a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (e -> Either e a
forall a b. a -> Either a b
Left e
expectedException)
GoodResult a
value -> Either e a -> STM (Either e a)
forall a. a -> STM a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (a -> Either e a
forall a b. b -> Either a b
Right a
value)
Thread (Either e a) -> IO (Thread (Either e a))
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (ThreadId -> STM (Either e a) -> Thread (Either e a)
forall a. ThreadId -> STM a -> Thread a
makeThread ThreadId
childThreadId STM (Either e a)
doAwait)
propagateException :: Scope -> Tid -> SomeException -> UnexceptionalIO ()
propagateException :: Scope -> ScopeStatus -> SomeException -> UnexceptionalIO ()
propagateException Scope {MVar SomeException
$sel:childExceptionVar:Scope :: Scope -> MVar SomeException
childExceptionVar :: MVar SomeException
childExceptionVar, ThreadId
$sel:parentThreadId:Scope :: Scope -> ThreadId
parentThreadId :: ThreadId
parentThreadId, TVar ScopeStatus
$sel:statusVar:Scope :: Scope -> TVar ScopeStatus
statusVar :: TVar ScopeStatus
statusVar} ScopeStatus
childId SomeException
exception =
IO ScopeStatus -> UnexceptionalIO ScopeStatus
forall a. IO a -> UnexceptionalIO a
UnexceptionalIO (TVar ScopeStatus -> IO ScopeStatus
forall a. TVar a -> IO a
readTVarIO TVar ScopeStatus
statusVar) UnexceptionalIO ScopeStatus
-> (ScopeStatus -> UnexceptionalIO ()) -> UnexceptionalIO ()
forall a b.
UnexceptionalIO a -> (a -> UnexceptionalIO b) -> UnexceptionalIO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
ScopeStatus
Closing -> UnexceptionalIO ()
tryPutChildExceptionVar
ScopeStatus
status -> do
Bool -> UnexceptionalIO ()
forall (m :: * -> *). Applicative m => Bool -> m ()
assertM (ScopeStatus
status ScopeStatus -> ScopeStatus -> Bool
forall a. Ord a => a -> a -> Bool
>= ScopeStatus
0)
UnexceptionalIO ()
loop
where
loop :: UnexceptionalIO ()
loop :: UnexceptionalIO ()
loop =
IO () -> UnexceptionalIO (IOResult ())
forall a. IO a -> UnexceptionalIO (IOResult a)
unexceptionalTry (SomeException -> ScopeStatus -> ThreadId -> IO ()
propagate SomeException
exception ScopeStatus
childId ThreadId
parentThreadId) UnexceptionalIO (IOResult ())
-> (IOResult () -> UnexceptionalIO ()) -> UnexceptionalIO ()
forall a b.
UnexceptionalIO a -> (a -> UnexceptionalIO b) -> UnexceptionalIO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
Failure SomeException
secondException
| forall e. Exception e => SomeException -> Bool
exceptionIs @ScopeClosing SomeException
secondException -> UnexceptionalIO ()
tryPutChildExceptionVar
| Bool
otherwise -> UnexceptionalIO ()
loop
Success ()
_ -> () -> UnexceptionalIO ()
forall a. a -> UnexceptionalIO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
tryPutChildExceptionVar :: UnexceptionalIO ()
tryPutChildExceptionVar :: UnexceptionalIO ()
tryPutChildExceptionVar =
IO () -> UnexceptionalIO ()
forall a. IO a -> UnexceptionalIO a
UnexceptionalIO (IO Bool -> IO ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (MVar SomeException -> SomeException -> IO Bool
forall a. MVar a -> a -> IO Bool
tryPutMVar MVar SomeException
childExceptionVar SomeException
exception))