{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE ImportQualifiedPost #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StrictData #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
{-# OPTIONS_HADDOCK prune #-}
module Core.Program.Threads
    ( 
      createScope
    , forkThread
    , forkThread_
    , linkThread
    , waitThread
    , waitThread_
    , waitThread'
    , waitThreads'
    , cancelThread
      
    , concurrentThreads
    , concurrentThreads_
    , raceThreads
    , raceThreads_
    , timeoutThread
      
    , Thread
    , unThread
    , Terminator (..)
    , Timeout (..)
    ) where
import Control.Concurrent (ThreadId, forkIO, killThread)
import Control.Concurrent.MVar (MVar, newEmptyMVar, newMVar, putMVar, readMVar, tryPutMVar)
import Control.Concurrent.STM (atomically)
import Control.Concurrent.STM.TVar (modifyTVar', newTVarIO, readTVarIO)
import Control.Exception.Safe qualified as Safe (catch, finally, onException, throw)
import Control.Monad
    ( forM
    , forM_
    , void
    )
import Control.Monad.Reader.Class (MonadReader (ask))
import Core.Data.Structures
import Core.Program.Context
import Core.Program.Exceptions
import Core.Program.Execute
import Core.Program.Logging
import Core.System.Base
import Core.Text.Rope
data Thread α = Thread
    { forall α. Thread α -> ThreadId
threadPointerOf :: ThreadId
    , forall α. Thread α -> MVar (Either SomeException α)
threadOutcomeOf :: MVar (Either SomeException α)
    }
unThread :: Thread α -> ThreadId
unThread :: forall α. Thread α -> ThreadId
unThread = forall α. Thread α -> ThreadId
threadPointerOf
createScope :: Program τ α -> Program τ α
createScope :: forall τ α. Program τ α -> Program τ α
createScope Program τ α
program = do
    Context τ
context <- forall r (m :: * -> *). MonadReader r m => m r
ask
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ do
        TVar (Set ThreadId)
scope <- forall a. a -> IO (TVar a)
newTVarIO forall ε. Key ε => Set ε
emptySet
        let context' :: Context τ
context' =
                Context τ
context
                    { $sel:currentScopeFrom:Context :: TVar (Set ThreadId)
currentScopeFrom = TVar (Set ThreadId)
scope
                    }
        forall (m :: * -> *) a b. MonadMask m => m a -> m b -> m a
Safe.finally
            ( do
                forall τ α. Context τ -> Program τ α -> IO α
subProgram Context τ
context' Program τ α
program
            )
            ( do
                Set ThreadId
pointers <- forall a. TVar a -> IO a
readTVarIO TVar (Set ThreadId)
scope
                forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
t a -> (a -> m b) -> m ()
forM_ Set ThreadId
pointers ThreadId -> IO ()
killThread
            )
forkThread :: Program τ α -> Program τ (Thread α)
forkThread :: forall τ α. Program τ α -> Program τ (Thread α)
forkThread Program τ α
program = do
    Context τ
context <- forall r (m :: * -> *). MonadReader r m => m r
ask
    let i :: MVar Time
i = forall τ. Context τ -> MVar Time
startTimeFrom Context τ
context
    let scope :: TVar (Set ThreadId)
scope = forall τ. Context τ -> TVar (Set ThreadId)
currentScopeFrom Context τ
context
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ do
        
        
        
        Time
start <- forall a. MVar a -> IO a
readMVar MVar Time
i
        MVar Time
i' <- forall a. a -> IO (MVar a)
newMVar Time
start
        let context' :: Context τ
context' =
                Context τ
context
                    { $sel:startTimeFrom:Context :: MVar Time
startTimeFrom = MVar Time
i'
                    }
        
        MVar (Either SomeException α)
outcome <- forall a. IO (MVar a)
newEmptyMVar
        ThreadId
pointer <- IO () -> IO ThreadId
forkIO forall a b. (a -> b) -> a -> b
$ do
            forall (m :: * -> *) e a.
(MonadCatch m, Exception e) =>
m a -> (e -> m a) -> m a
Safe.catch
                ( do
                    α
actual <- forall τ α. Context τ -> Program τ α -> IO α
subProgram Context τ
context' Program τ α
program
                    forall a. MVar a -> a -> IO ()
putMVar MVar (Either SomeException α)
outcome (forall a b. b -> Either a b
Right α
actual)
                )
                ( \(SomeException
e :: SomeException) -> do
                    let text :: Rope
text = forall α. Textual α => α -> Rope
intoRope (forall e. Exception e => e -> String
displayException SomeException
e)
                    forall τ α. Context τ -> Program τ α -> IO α
subProgram Context τ
context' forall a b. (a -> b) -> a -> b
$ do
                        forall τ. Rope -> Program τ ()
internal Rope
"Uncaught exception ending thread"
                        forall τ. Rope -> Program τ ()
internal (Rope
"e = " forall a. Semigroup a => a -> a -> a
<> Rope
text)
                    forall a. MVar a -> a -> IO ()
putMVar MVar (Either SomeException α)
outcome (forall a b. a -> Either a b
Left SomeException
e)
                )
        forall a. STM a -> IO a
atomically forall a b. (a -> b) -> a -> b
$ do
            forall a. TVar a -> (a -> a) -> STM ()
modifyTVar' TVar (Set ThreadId)
scope (\Set ThreadId
pointers -> forall ε. Key ε => ε -> Set ε -> Set ε
insertElement ThreadId
pointer Set ThreadId
pointers)
        forall (m :: * -> *) a. Monad m => a -> m a
return
            ( Thread
                { threadPointerOf :: ThreadId
threadPointerOf = ThreadId
pointer
                , threadOutcomeOf :: MVar (Either SomeException α)
threadOutcomeOf = MVar (Either SomeException α)
outcome
                }
            )
forkThread_ :: Program τ α -> Program τ ()
forkThread_ :: forall τ α. Program τ α -> Program τ ()
forkThread_ = forall (f :: * -> *) a. Functor f => f a -> f ()
void forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall τ α. Program τ α -> Program τ (Thread α)
forkThread
waitThread :: Thread α -> Program τ α
waitThread :: forall α τ. Thread α -> Program τ α
waitThread Thread α
thread = do
    Either SomeException α
result <- forall α τ. Thread α -> Program τ (Either SomeException α)
waitThread' Thread α
thread
    case Either SomeException α
result of
        Left SomeException
problem -> forall (m :: * -> *) e a. (MonadThrow m, Exception e) => e -> m a
Safe.throw SomeException
problem
        Right α
actual -> forall (f :: * -> *) a. Applicative f => a -> f a
pure α
actual
waitThread_ :: Thread α -> Program τ ()
waitThread_ :: forall α τ. Thread α -> Program τ ()
waitThread_ Thread α
thread = forall (f :: * -> *) a. Functor f => f a -> f ()
void (forall α τ. Thread α -> Program τ α
waitThread Thread α
thread)
waitThread' :: Thread α -> Program τ (Either SomeException α)
waitThread' :: forall α τ. Thread α -> Program τ (Either SomeException α)
waitThread' Thread α
thread = do
    Context τ
context <- forall r (m :: * -> *). MonadReader r m => m r
ask
    let scope :: TVar (Set ThreadId)
scope = forall τ. Context τ -> TVar (Set ThreadId)
currentScopeFrom Context τ
context
    let outcome :: MVar (Either SomeException α)
outcome = forall α. Thread α -> MVar (Either SomeException α)
threadOutcomeOf Thread α
thread
    let pointer :: ThreadId
pointer = forall α. Thread α -> ThreadId
threadPointerOf Thread α
thread
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ do
        forall (m :: * -> *) a b. MonadMask m => m a -> m b -> m a
Safe.onException
            ( do
                Either SomeException α
result <- forall a. MVar a -> IO a
readMVar MVar (Either SomeException α)
outcome 
                forall a. STM a -> IO a
atomically forall a b. (a -> b) -> a -> b
$ do
                    forall a. TVar a -> (a -> a) -> STM ()
modifyTVar' TVar (Set ThreadId)
scope (\Set ThreadId
pointers -> forall ε. Key ε => ε -> Set ε -> Set ε
removeElement ThreadId
pointer Set ThreadId
pointers)
                forall (f :: * -> *) a. Applicative f => a -> f a
pure Either SomeException α
result
            )
            ( do
                ThreadId -> IO ()
killThread ThreadId
pointer
                forall a. STM a -> IO a
atomically forall a b. (a -> b) -> a -> b
$ do
                    forall a. TVar a -> (a -> a) -> STM ()
modifyTVar' TVar (Set ThreadId)
scope (\Set ThreadId
pointers -> forall ε. Key ε => ε -> Set ε -> Set ε
removeElement ThreadId
pointer Set ThreadId
pointers)
            )
waitThreads' :: [Thread α] -> Program τ [Either SomeException α]
waitThreads' :: forall α τ. [Thread α] -> Program τ [Either SomeException α]
waitThreads' [Thread α]
threads = do
    Context τ
context <- forall r (m :: * -> *). MonadReader r m => m r
ask
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ do
        forall (m :: * -> *) a b. MonadMask m => m a -> m b -> m a
Safe.onException
            ( do
                forall τ α. Context τ -> Program τ α -> IO α
subProgram Context τ
context forall a b. (a -> b) -> a -> b
$ do
                    forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
t a -> (a -> m b) -> m (t b)
forM [Thread α]
threads forall α τ. Thread α -> Program τ (Either SomeException α)
waitThread'
            )
            ( do
                
                
                
                
                
                
                let scope :: TVar (Set ThreadId)
scope = forall τ. Context τ -> TVar (Set ThreadId)
currentScopeFrom Context τ
context
                forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
t a -> (a -> m b) -> m ()
forM_ [Thread α]
threads forall a b. (a -> b) -> a -> b
$ \Thread α
thread -> do
                    let pointer :: ThreadId
pointer = forall α. Thread α -> ThreadId
threadPointerOf Thread α
thread
                    ThreadId -> IO ()
killThread ThreadId
pointer
                    forall a. STM a -> IO a
atomically forall a b. (a -> b) -> a -> b
$ do
                        forall a. TVar a -> (a -> a) -> STM ()
modifyTVar' TVar (Set ThreadId)
scope (\Set ThreadId
pointers -> forall ε. Key ε => ε -> Set ε -> Set ε
removeElement ThreadId
pointer Set ThreadId
pointers)
            )
cancelThread :: Thread α -> Program τ ()
cancelThread :: forall α τ. Thread α -> Program τ ()
cancelThread Thread α
thread = do
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ do
        
        
        
        
        
        
        
        let outcome :: MVar (Either SomeException α)
outcome = forall α. Thread α -> MVar (Either SomeException α)
threadOutcomeOf Thread α
thread
        Bool
result <- forall a. MVar a -> a -> IO Bool
tryPutMVar MVar (Either SomeException α)
outcome (forall a b. a -> Either a b
Left (forall e. Exception e => e -> SomeException
toException Terminator
ThreadCancelled))
        case Bool
result of
            Bool
False -> do
                forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
            Bool
True -> do
                ThreadId -> IO ()
killThread (forall α. Thread α -> ThreadId
threadPointerOf Thread α
thread)
data Terminator = ThreadCancelled
    deriving (Int -> Terminator -> ShowS
[Terminator] -> ShowS
Terminator -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Terminator] -> ShowS
$cshowList :: [Terminator] -> ShowS
show :: Terminator -> String
$cshow :: Terminator -> String
showsPrec :: Int -> Terminator -> ShowS
$cshowsPrec :: Int -> Terminator -> ShowS
Show)
instance Exception Terminator
concurrentThreads :: Program τ α -> Program τ β -> Program τ (α, β)
concurrentThreads :: forall τ α β. Program τ α -> Program τ β -> Program τ (α, β)
concurrentThreads Program τ α
one Program τ β
two = do
    forall τ α. Program τ α -> Program τ α
createScope forall a b. (a -> b) -> a -> b
$ do
        Thread α
a1 <- forall τ α. Program τ α -> Program τ (Thread α)
forkThread Program τ α
one
        Thread β
a2 <- forall τ α. Program τ α -> Program τ (Thread α)
forkThread Program τ β
two
        α
result1 <- forall α τ. Thread α -> Program τ α
waitThread Thread α
a1
        β
result2 <- forall α τ. Thread α -> Program τ α
waitThread Thread β
a2
        forall (f :: * -> *) a. Applicative f => a -> f a
pure (α
result1, β
result2)
concurrentThreads_ :: Program τ α -> Program τ β -> Program τ ()
concurrentThreads_ :: forall τ α β. Program τ α -> Program τ β -> Program τ ()
concurrentThreads_ Program τ α
one Program τ β
two = forall (f :: * -> *) a. Functor f => f a -> f ()
void (forall τ α β. Program τ α -> Program τ β -> Program τ (α, β)
concurrentThreads Program τ α
one Program τ β
two)
raceThreads :: Program τ α -> Program τ β -> Program τ (Either α β)
raceThreads :: forall τ α β. Program τ α -> Program τ β -> Program τ (Either α β)
raceThreads Program τ α
one Program τ β
two = do
    forall τ α. Program τ α -> Program τ α
createScope forall a b. (a -> b) -> a -> b
$ do
        MVar (Either () ())
outcome <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ do
            forall a. IO (MVar a)
newEmptyMVar
        Thread α
t1 <- forall τ α. Program τ α -> Program τ (Thread α)
forkThread forall a b. (a -> b) -> a -> b
$ do
            forall τ α γ. Program τ α -> Program τ γ -> Program τ α
finally
                ( do
                    Program τ α
one
                )
                ( do
                    forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ do
                        forall a. MVar a -> a -> IO ()
putMVar MVar (Either () ())
outcome (forall a b. a -> Either a b
Left ())
                )
        Thread β
t2 <- forall τ α. Program τ α -> Program τ (Thread α)
forkThread forall a b. (a -> b) -> a -> b
$ do
            forall τ α γ. Program τ α -> Program τ γ -> Program τ α
finally
                ( do
                    Program τ β
two
                )
                ( do
                    forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ do
                        forall a. MVar a -> a -> IO ()
putMVar MVar (Either () ())
outcome (forall a b. b -> Either a b
Right ())
                )
        Either () ()
result <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ do
            forall a. MVar a -> IO a
readMVar MVar (Either () ())
outcome
        case Either () ()
result of
            Left ()
_ -> do
                α
result1 <- forall α τ. Thread α -> Program τ α
waitThread Thread α
t1
                forall (f :: * -> *) a. Applicative f => a -> f a
pure (forall a b. a -> Either a b
Left α
result1)
            Right ()
_ -> do
                β
result2 <- forall α τ. Thread α -> Program τ α
waitThread Thread β
t2
                forall (f :: * -> *) a. Applicative f => a -> f a
pure (forall a b. b -> Either a b
Right β
result2)
raceThreads_ :: Program τ α -> Program τ β -> Program τ ()
raceThreads_ :: forall τ α β. Program τ α -> Program τ β -> Program τ ()
raceThreads_ Program τ α
one Program τ β
two = forall (f :: * -> *) a. Functor f => f a -> f ()
void (forall τ α β. Program τ α -> Program τ β -> Program τ (Either α β)
raceThreads Program τ α
one Program τ β
two)
linkThread :: Thread α -> Program τ ()
linkThread :: forall α τ. Thread α -> Program τ ()
linkThread Thread α
_ = forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
{-# DEPRECATED linkThread "Exceptions are bidirectional so linkThread no longer needed" #-}
data Timeout = Timeout deriving (Int -> Timeout -> ShowS
[Timeout] -> ShowS
Timeout -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Timeout] -> ShowS
$cshowList :: [Timeout] -> ShowS
show :: Timeout -> String
$cshow :: Timeout -> String
showsPrec :: Int -> Timeout -> ShowS
$cshowsPrec :: Int -> Timeout -> ShowS
Show)
instance Exception Timeout
timeoutThread :: Rational -> Program τ α -> Program τ α
timeoutThread :: forall τ α. Rational -> Program τ α -> Program τ α
timeoutThread Rational
seconds Program τ α
program = do
    Either Timeout α
result <-
        forall τ α β. Program τ α -> Program τ β -> Program τ (Either α β)
raceThreads
            ( do
                forall τ. Rational -> Program τ ()
sleepThread Rational
seconds
                forall (f :: * -> *) a. Applicative f => a -> f a
pure Timeout
Timeout
            )
            ( do
                Program τ α
program
            )
    case Either Timeout α
result of
        Left Timeout
e -> forall ε τ α. Exception ε => ε -> Program τ α
throw Timeout
e
        Right α
a -> forall (f :: * -> *) a. Applicative f => a -> f a
pure α
a