{-# LANGUAGE DoAndIfThenElse #-}
module Control.Concurrent.Barrier
( barrier
, latchBarrier
) where
import Control.Monad (forM_)
import Control.Concurrent.MVar
barrier' :: (Int -> Int) -> Int -> IO (IO ())
barrier' reset count = do
b <- newMVar (count, [])
return $ do
w <- modifyMVar b $ \(c, waiting) ->
case c of
0 -> return ((0, []), Nothing)
1 -> do forM_ waiting $ flip putMVar ()
return ((reset count, []), Nothing)
_ -> do w <- newEmptyMVar
return ((c-1, w:waiting), Just w)
maybe (return ()) takeMVar w
-- | Self-resetting barrier. 'barrier' blocks until a specified
-- number of threads have reached it, and then they are all allowed to
-- run. The barrier is then reset so that a further 'count' threads
-- can block on it. Typical usage is:
--
-- > do b <- barrier 3
-- > forkIO $ b >> putStrLn "1" -- blocked
-- > forkIO $ b >> putStrLn "2" -- blocked
-- > forkIO $ b >> putStrLn "3" -- all three threads run
barrier :: Int -- ^ count - number of threads required before barrier is opened
-> IO (IO ())
barrier = barrier' id
-- | Latching barrier. This is the same as 'barrier', except once the
-- barrier has opened (the requisite number of threads has reached
-- it), it remains open, allowing all subsequent threads through
-- unblocked.
latchBarrier :: Int -- ^ count - number of threads required before barrier is opened
-> IO (IO ())
latchBarrier = barrier' (const 0)