{-# LANGUAGE DeriveDataTypeable #-}
module Test.Tasty.Parallel (ActionStatus(..), Action(..), runInParallel) where
import Control.Monad
import Control.Concurrent
import Control.Concurrent.Async
import Control.Concurrent.STM
import Foreign.StablePtr
data ActionStatus
= ActionReady
| ActionSkip
| ActionWait
deriving ActionStatus -> ActionStatus -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: ActionStatus -> ActionStatus -> Bool
$c/= :: ActionStatus -> ActionStatus -> Bool
== :: ActionStatus -> ActionStatus -> Bool
$c== :: ActionStatus -> ActionStatus -> Bool
Eq
data Action = Action
{ Action -> STM ActionStatus
actionStatus :: STM ActionStatus
, Action -> IO ()
actionRun :: IO ()
, Action -> STM ()
actionSkip :: STM ()
}
runInParallel
:: Int
-> [Action]
-> IO (IO ())
runInParallel :: Int -> [Action] -> IO (IO ())
runInParallel Int
nthreads [Action]
actions = do
ThreadId
callingThread <- IO ThreadId
myThreadId
StablePtr ThreadId
_ <- forall a. a -> IO (StablePtr a)
newStablePtr ThreadId
callingThread
TMVar [Action]
actionsVar <- forall a. STM a -> IO a
atomically forall a b. (a -> b) -> a -> b
$ forall a. a -> STM (TMVar a)
newTMVar [Action]
actions
[Async ()]
pids <- forall (m :: * -> *) a. Applicative m => Int -> m a -> m [a]
replicateM Int
nthreads (forall a. IO a -> IO (Async a)
async forall a b. (a -> b) -> a -> b
$ TMVar [Action] -> IO ()
work TMVar [Action]
actionsVar)
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ do
[Action]
_ <- forall a. STM a -> IO a
atomically forall a b. (a -> b) -> a -> b
$ forall a. TMVar a -> a -> STM a
swapTMVar TMVar [Action]
actionsVar []
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ forall a. Async a -> IO ()
cancel [Async ()]
pids
work :: TMVar [Action] -> IO ()
work :: TMVar [Action] -> IO ()
work TMVar [Action]
actionsVar = IO ()
go
where
go :: IO ()
go = do
forall (m :: * -> *) a. Monad m => m (m a) -> m a
join forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. STM a -> IO a
atomically forall a b. (a -> b) -> a -> b
$ do
Maybe (Action, [Action])
mb_ready <- [Action] -> STM (Maybe (Action, [Action]))
findBool forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< forall a. TMVar a -> STM a
takeTMVar TMVar [Action]
actionsVar
case Maybe (Action, [Action])
mb_ready of
Maybe (Action, [Action])
Nothing -> do
forall a. TMVar a -> a -> STM ()
putTMVar TMVar [Action]
actionsVar []
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) a. Monad m => a -> m a
return ()
Just (Action
this, [Action]
rest) -> do
forall a. TMVar a -> a -> STM ()
putTMVar TMVar [Action]
actionsVar [Action]
rest
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ Action -> IO ()
actionRun Action
this forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> IO ()
go
findBool :: [Action] -> STM (Maybe (Action, [Action]))
findBool :: [Action] -> STM (Maybe (Action, [Action]))
findBool = [Action] -> [Action] -> STM (Maybe (Action, [Action]))
go []
where
go :: [Action] -> [Action] -> STM (Maybe (Action, [Action]))
go [] [] =
forall (m :: * -> *) a. Monad m => a -> m a
return forall a. Maybe a
Nothing
go [Action]
_ [] =
forall a. STM a
retry
go [Action]
past (Action
this : [Action]
rest) = do
ActionStatus
status <- Action -> STM ActionStatus
actionStatus Action
this
case ActionStatus
status of
ActionStatus
ActionReady -> forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall a. a -> Maybe a
Just (Action
this, forall a. [a] -> [a]
reverse [Action]
past forall a. [a] -> [a] -> [a]
++ [Action]
rest)
ActionStatus
ActionWait -> [Action] -> [Action] -> STM (Maybe (Action, [Action]))
go (Action
this forall a. a -> [a] -> [a]
: [Action]
past) [Action]
rest
ActionStatus
ActionSkip -> do
Action -> STM ()
actionSkip Action
this
[Action] -> [Action] -> STM (Maybe (Action, [Action]))
go [Action]
past [Action]
rest