|
|
@ -1,91 +1,59 @@ |
|
|
|
module ThreadPoolTimed where |
|
|
|
{-# OPTIONS -Wall #-} |
|
|
|
module ThreadPoolTimed(threadPoolTimed) where |
|
|
|
|
|
|
|
import Control.DeepSeq (NFData, deepseq) |
|
|
|
import Control.Concurrent.STM.TChan |
|
|
|
import Control.Concurrent.STM.TVar |
|
|
|
import Control.Monad.STM |
|
|
|
import Control.Concurrent |
|
|
|
import Control.Monad |
|
|
|
|
|
|
|
import Data.Maybe |
|
|
|
|
|
|
|
type NWorkers = Int |
|
|
|
type Sec = Int |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
threadPoolTimed :: NWorkers -> Sec -> (a -> Either a b) -> [a] -> IO ([a],[b]) |
|
|
|
threadPoolTimed nworkers sec f toCompute = do let initialLength = length toCompute :: Int |
|
|
|
toComputeSize <- newTVarIO initialLength |
|
|
|
toComputeQ <- newTChanIO :: IO (TChan a) |
|
|
|
completedQ <- newTChanIO :: IO (TChan b) |
|
|
|
unCompletedQ <- newTChanIO :: IO (TChan a) |
|
|
|
isTPoolAboutToFinish <- newTVarIO False |
|
|
|
|
|
|
|
workers <- forkNTimes nworkers (workerFunction f (toComputeSize, |
|
|
|
toComputeQ, |
|
|
|
completedQ, |
|
|
|
unCompletedQ, |
|
|
|
isTPoolAboutToFinish)) |
|
|
|
|
|
|
|
|
|
|
|
secWait sec |
|
|
|
|
|
|
|
pushDataToWorkers toCompute toComputeQ |
|
|
|
|
|
|
|
sendStopMessage isTPoolAboutToFinish |
|
|
|
threadPoolTimed :: (NFData a) => Int -> Int -> (a -> a) -> [a] -> IO [a] |
|
|
|
threadPoolTimed nworkers sec f toCompute = do toComputeQ <- newTChanIO :: IO (TChan a) |
|
|
|
stopVar <- newTVarIO False |
|
|
|
putStrLn "DATA TO WORKERS" |
|
|
|
pushDataToWorkers toCompute toComputeQ |
|
|
|
putStrLn "WORKERS STARTED" |
|
|
|
workers <- forkNTimes nworkers (workerFunction f (toComputeQ, |
|
|
|
stopVar)) |
|
|
|
putStrLn "CLOCK STARTED" |
|
|
|
secWait sec |
|
|
|
putStrLn "STOP MESSAGE SENT" |
|
|
|
sendStopMessage stopVar |
|
|
|
putStrLn "WAITING FOR COMPLETITION" |
|
|
|
waitWorkerCompletition workers |
|
|
|
computedSize <- atomically $ readTVar toComputeSize |
|
|
|
retrieveData initialLength computedSize unCompletedQ completedQ |
|
|
|
putStrLn "RETRIEVE DATA" |
|
|
|
retrieveAllData toComputeQ |
|
|
|
|
|
|
|
where |
|
|
|
secWait = threadDelay . (1000000 * ) |
|
|
|
sendStopMessage isTPoolAboutToFinish = atomically $ writeTVar isTPoolAboutToFinish True |
|
|
|
sendStopMessage stopMessageC = atomically $ writeTVar stopMessageC True |
|
|
|
|
|
|
|
forkNTimes :: NWorkers -> (MVar Bool -> IO ()) -> IO [MVar Bool] |
|
|
|
forkNTimes nworkers f = do workers <- replicateM nworkers (newMVar False) |
|
|
|
forkNTimes :: Int -> (TVar Bool -> IO ()) -> IO [TVar Bool] |
|
|
|
forkNTimes nworkers f = do workers <- replicateM nworkers (newTVarIO False) |
|
|
|
mapM_ (forkIO . f) workers |
|
|
|
return workers |
|
|
|
|
|
|
|
|
|
|
|
workerFunction :: (a -> Either a b) -> (TVar Int, TChan a, TChan b, TChan a, TVar Bool) -> MVar Bool -> IO () |
|
|
|
workerFunction f workerData completedVar = do extractedData <- dataToCompute |
|
|
|
if (isNothing extractedData) |
|
|
|
then putMVar completedVar True |
|
|
|
else do let newData = (f . fromJust) extractedData |
|
|
|
pushNewData newData |
|
|
|
workerFunction f workerData completedVar |
|
|
|
workerFunction :: (NFData a) => (a -> a) -> (TChan a, TVar Bool) -> TVar Bool -> IO () |
|
|
|
workerFunction f (toCompute, stopVar) threadEndedVar = do (stopMessageArrived, computationData) <- retrieveData |
|
|
|
if stopMessageArrived |
|
|
|
then atomically $ do writeTVar threadEndedVar True |
|
|
|
else do executeStep computationData |
|
|
|
workerFunction f (toCompute, stopVar) threadEndedVar |
|
|
|
where |
|
|
|
(computeSize, toComputeQ, finishedDataQ, unfinishedDataQ, isCompAboutToFinish) = workerData |
|
|
|
dataToCompute = atomically $ orElse nullData getData |
|
|
|
|
|
|
|
nullData = do r <- checkFinished |
|
|
|
if r |
|
|
|
then return Nothing |
|
|
|
else retry |
|
|
|
|
|
|
|
checkFinished = do computesz <- readTVar computeSize |
|
|
|
finished <- readTVar isCompAboutToFinish |
|
|
|
return (finished || (computesz == 0)) |
|
|
|
|
|
|
|
getData = do d <- tryReadTChan toComputeQ |
|
|
|
if isNothing d |
|
|
|
then retry |
|
|
|
else return d |
|
|
|
|
|
|
|
|
|
|
|
pushNewData nData = atomically $ do finished <- checkFinished |
|
|
|
if finished |
|
|
|
then pushLastData nData |
|
|
|
else pushPartialData nData |
|
|
|
|
|
|
|
pushPartialData (Left toBeCompleted) = writeTChan toComputeQ toBeCompleted |
|
|
|
pushPartialData (Right completed) = do writeTChan finishedDataQ completed |
|
|
|
modifyTVar computeSize (\x -> x - 1) |
|
|
|
|
|
|
|
pushLastData (Left toBeCompleted) = writeTChan unfinishedDataQ toBeCompleted |
|
|
|
pushLastData (Right completed) = writeTChan finishedDataQ completed |
|
|
|
|
|
|
|
|
|
|
|
retrieveData = atomically $ do stopMessage <- readTVar stopVar |
|
|
|
if stopMessage |
|
|
|
then return (True, Nothing) |
|
|
|
else do compValue <- readTChan toCompute |
|
|
|
return (False, Just compValue) |
|
|
|
|
|
|
|
executeStep computationData = do let newData = ((f . fromJust) computationData) |
|
|
|
newData `deepseq` atomically $ writeTChan toCompute newData |
|
|
|
|
|
|
|
|
|
|
|
pushDataToWorkers :: [a] -> TChan a -> IO () |
|
|
|
pushDataToWorkers toCompute toComputeQ = atomically $ mapM_ (\el -> writeTChan toComputeQ el) toCompute |
|
|
|
|
|
|
@ -94,17 +62,26 @@ pushDataToWorkers toCompute toComputeQ = atomically $ mapM_ (\el -> writeTChan t |
|
|
|
--retrieveData :: TChan a -> TChan b -> [(a,b)] |
|
|
|
--retrieveData unCompletedQ completedQ |
|
|
|
|
|
|
|
waitWorkerCompletition :: [MVar Bool] -> IO () |
|
|
|
waitWorkerCompletition workers = do workersStatus <- mapM readMVar workers |
|
|
|
if and workersStatus |
|
|
|
then return () |
|
|
|
else do threadDelay 1000 |
|
|
|
waitWorkerCompletition workers |
|
|
|
|
|
|
|
retrieveData :: Int -> Int -> TChan a -> TChan b -> IO ([a],[b]) |
|
|
|
retrieveData initialSize notEvaluated completedQ unCompletedQ = atomically $ do listCompleted <- flushChan evaluated completedQ |
|
|
|
listUnCompleted <- flushChan notEvaluated unCompletedQ |
|
|
|
return (listCompleted,listUnCompleted) |
|
|
|
where |
|
|
|
evaluated = initialSize - notEvaluated |
|
|
|
flushChan n c = replicateM n (readTChan c) |
|
|
|
waitWorkerCompletition :: [TVar Bool] -> IO () |
|
|
|
waitWorkerCompletition workers = atomically $ do workersStatus <- mapM readTVar workers |
|
|
|
if and workersStatus |
|
|
|
then return () |
|
|
|
else retry |
|
|
|
|
|
|
|
|
|
|
|
retrieveAllData :: TChan a -> IO [a] |
|
|
|
|
|
|
|
retrieveAllData queue = atomically $ retrieveAllDataSTM [] |
|
|
|
where retrieveAllDataSTM acc = do isEmpty <- isEmptyTChan queue |
|
|
|
if isEmpty |
|
|
|
then return acc |
|
|
|
else do elemToAcc <- readTChan queue |
|
|
|
retrieveAllDataSTM (elemToAcc:acc) |
|
|
|
|
|
|
|
-- retrieveAllData queue acc = do voidQueue <- atomically $ isEmptyTChan queue |
|
|
|
-- putStrLn $ (show voidQueue) |
|
|
|
-- if voidQueue |
|
|
|
-- then return acc |
|
|
|
-- else do elemToAcc <- atomically $ readTChan queue |
|
|
|
-- retrieveAllData queue (elemToAcc:acc) |
|
|
|
|