{-# LANGUAGE ExistentialQuantification #-}
module Development.Shake.Internal.Core.Rendezvous(
Waiting, newWaiting, afterWaiting,
Answer(..), Compute(..),
rendezvous
) where
import Control.Monad
import Data.IORef.Extra
import Data.Primitive.Array
import Development.Shake.Internal.Errors
data Answer a c
= Abort a
| Continue c
data Compute a
= Now a
| Later (Waiting a)
partitionAnswer :: [Answer a c] -> ([a], [c])
partitionAnswer = foldr f ([],[])
where f (Abort a) ~(as,cs) = (a:as,cs)
f (Continue c) ~(as,cs) = (as,c:cs)
partitionCompute :: [Compute a] -> ([a], [Waiting a])
partitionCompute = foldr f ([],[])
where f (Now x) ~(xs,ws) = (x:xs,ws)
f (Later w) ~(xs,ws) = (xs,w:ws)
data Waiting a = forall b . Waiting (b -> a) (IORef (b -> IO ()))
instance Functor Waiting where
fmap f (Waiting op ref) = Waiting (f . op) ref
instance Show (Waiting a) where
show _ = "Waiting"
newWaiting :: IO (Waiting a, a -> IO ())
newWaiting = do
ref <- newIORef $ \_ -> return ()
let run x = ($ x) =<< readIORef ref
return (Waiting id ref, run)
afterWaiting :: Waiting a -> (a -> IO ()) -> IO ()
afterWaiting (Waiting op ref) act = modifyIORef' ref (\a s -> a s >> act (op s))
rendezvous :: [Compute (Answer a c)] -> IO (Compute (Either a [c]))
rendezvous xs = do
let (now, later) = partitionCompute xs
let (abort, continue) = partitionAnswer now
if not $ null abort then
return $ Now $ Left $ head abort
else if null later then
return $ Now $ Right continue
else do
(waiting, run) <- newWaiting
let n = length xs
result <- newArray n $ errorInternal "rendezvous"
todo <- newIORef $ length later
forM_ (zip [0..] xs) $ \(i,x) -> case x of
Now (Continue c) -> writeArray result i c
Later w -> afterWaiting w $ \v -> do
t <- readIORef todo
case v of
_ | t == 0 -> return ()
Abort a -> do
writeIORef todo 0
run $ Left a
Continue c -> do
writeArray result i c
writeIORef' todo $ t-1
when (t == 1) $ do
rs <- unsafeFreezeArray result
run $ Right $ map (indexArray rs) [0..n-1]
return $ Later waiting