{-# LANGUAGE ScopedTypeVariables #-} module Control.Concurrent.Parallel3(parallel_) where import GHC.Conc import Control.Concurrent.Chan import Control.Concurrent.MVar import Control.Concurrent import Control.Monad import Control.Exception as E import System.IO.Unsafe -- initialise on the main thread, and keep mainThread :: ThreadId mainThread = unsafePerformIO $ myThreadId -- True = kill the thread after it finishes {-# NOINLINE queue #-} queue :: Chan (IO a, Bool) queue = seq mainThread $ unsafePerformIO $ do chan <- newChan replicateM_ (numCapabilities-1) $ addWorker return chan addWorker :: IO () addWorker = do forkIO $ f `E.catch` \(e :: SomeException) -> throwTo mainThread $ ErrorCall "Control.Concurrent.Parallel: parallel thread died." return () where f :: IO () f = do (act, kill) <- readChan queue act unless kill f -- | Run the list of computations in parallel -- Rule: No thread should get pre-empted (although not a guarantee) -- On return all actions have been performed parallel_ :: [IO a] -> IO () parallel_ xs | numCapabilities <= 1 = sequence_ xs parallel_ [] = return () parallel_ [x] = x >> return () parallel_ (x1:xs) = do sem <- newQSem $ 1 - length xs forM_ xs $ \x -> writeChan queue (x >> signalQSem sem, False) x1 addWorker waitQSem sem writeChan queue (signalQSem sem, True) waitQSem sem