module Test.Framework.Runners.ThreadPool (
executeOnPool
) where
import Control.Concurrent
( getChanContents,
newChan,
readChan,
writeChan,
writeList2Chan,
forkIO,
myThreadId,
Chan )
import Control.Monad ( forM_ )
import qualified Data.IntMap as IM
import Foreign.StablePtr ( newStablePtr )
data WorkerEvent token a = WorkerTermination
| WorkerItem token a
executeOnPool :: Int
-> [IO a]
-> IO [a]
executeOnPool :: forall a. Int -> [IO a] -> IO [a]
executeOnPool Int
n [IO a]
actions = do
input_chan <- IO (Chan (WorkerEvent Int (IO a)))
forall a. IO (Chan a)
newChan
output_chan <- newChan
_ <- forkIO $ writeList2Chan input_chan (zipWith WorkerItem [0..] actions ++ replicate n WorkerTermination)
forM_ [1..n] (const $ forkIO $ poolWorker input_chan output_chan)
_stablePtr <- myThreadId >>= newStablePtr
fmap (reorderFrom 0 . takeWhileWorkersExist n) $ getChanContents output_chan
poolWorker :: Chan (WorkerEvent token (IO a)) -> Chan (WorkerEvent token a) -> IO ()
poolWorker :: forall token a.
Chan (WorkerEvent token (IO a))
-> Chan (WorkerEvent token a) -> IO ()
poolWorker Chan (WorkerEvent token (IO a))
input_chan Chan (WorkerEvent token a)
output_chan = do
action_item <- Chan (WorkerEvent token (IO a)) -> IO (WorkerEvent token (IO a))
forall a. Chan a -> IO a
readChan Chan (WorkerEvent token (IO a))
input_chan
case action_item of
WorkerEvent token (IO a)
WorkerTermination -> Chan (WorkerEvent token a) -> WorkerEvent token a -> IO ()
forall a. Chan a -> a -> IO ()
writeChan Chan (WorkerEvent token a)
output_chan WorkerEvent token a
forall token a. WorkerEvent token a
WorkerTermination
WorkerItem token
token IO a
action -> do
result <- IO a
action
writeChan output_chan (WorkerItem token result)
poolWorker input_chan output_chan
takeWhileWorkersExist :: Int -> [WorkerEvent token a] -> [(token, a)]
takeWhileWorkersExist :: forall token a. Int -> [WorkerEvent token a] -> [(token, a)]
takeWhileWorkersExist Int
worker_count [WorkerEvent token a]
events
| Int
worker_count Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
0 = []
| Bool
otherwise = case [WorkerEvent token a]
events of
(WorkerEvent token a
WorkerTermination:[WorkerEvent token a]
events') -> Int -> [WorkerEvent token a] -> [(token, a)]
forall token a. Int -> [WorkerEvent token a] -> [(token, a)]
takeWhileWorkersExist (Int
worker_count Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) [WorkerEvent token a]
events'
(WorkerItem token
token a
x:[WorkerEvent token a]
events') -> (token
token, a
x) (token, a) -> [(token, a)] -> [(token, a)]
forall a. a -> [a] -> [a]
: Int -> [WorkerEvent token a] -> [(token, a)]
forall token a. Int -> [WorkerEvent token a] -> [(token, a)]
takeWhileWorkersExist Int
worker_count [WorkerEvent token a]
events'
[] -> []
reorderFrom :: Int -> [(Int, a)] -> [a]
reorderFrom :: forall a. Int -> [(Int, a)] -> [a]
reorderFrom Int
from [(Int, a)]
initial_things = Int -> [(Int, a)] -> IntMap a -> Bool -> [a]
forall {a}. Int -> [(Int, a)] -> IntMap a -> Bool -> [a]
go Int
from [(Int, a)]
initial_things IntMap a
forall a. IntMap a
IM.empty Bool
False
where go :: Int -> [(Int, a)] -> IntMap a -> Bool -> [a]
go Int
next [] IntMap a
buf Bool
_
| IntMap a -> Bool
forall a. IntMap a -> Bool
IM.null IntMap a
buf = []
| Bool
otherwise = Int -> [(Int, a)] -> IntMap a -> Bool -> [a]
go Int
next (IntMap a -> [(Int, a)]
forall a. IntMap a -> [(Int, a)]
IM.toList IntMap a
buf) IntMap a
forall a. IntMap a
IM.empty Bool
False
go Int
next all_things :: [(Int, a)]
all_things@((Int
token, a
x):[(Int, a)]
things) IntMap a
buf Bool
buf_useful
| Int
token Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
next
= a
x a -> [a] -> [a]
forall a. a -> [a] -> [a]
: Int -> [(Int, a)] -> IntMap a -> Bool -> [a]
go (Int
next Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) [(Int, a)]
things IntMap a
buf Bool
True
| Bool
buf_useful
, (Just a
x', IntMap a
buf') <- (Int -> a -> Maybe a) -> Int -> IntMap a -> (Maybe a, IntMap a)
forall a.
(Int -> a -> Maybe a) -> Int -> IntMap a -> (Maybe a, IntMap a)
IM.updateLookupWithKey ((a -> Maybe a) -> Int -> a -> Maybe a
forall a b. a -> b -> a
const ((a -> Maybe a) -> Int -> a -> Maybe a)
-> (a -> Maybe a) -> Int -> a -> Maybe a
forall a b. (a -> b) -> a -> b
$ Maybe a -> a -> Maybe a
forall a b. a -> b -> a
const Maybe a
forall a. Maybe a
Nothing) Int
next IntMap a
buf
= a
x' a -> [a] -> [a]
forall a. a -> [a] -> [a]
: Int -> [(Int, a)] -> IntMap a -> Bool -> [a]
go (Int
next Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) [(Int, a)]
all_things IntMap a
buf' Bool
True
| Bool
otherwise
= Int -> [(Int, a)] -> IntMap a -> Bool -> [a]
go Int
next [(Int, a)]
things (Int -> a -> IntMap a -> IntMap a
forall a. Int -> a -> IntMap a -> IntMap a
IM.insert Int
token a
x IntMap a
buf) Bool
False