{-# LANGUAGE BangPatterns #-} -- | Not exported directly. Reexported by "Control.LVish". module Control.LVish.Logical (asyncAnd, asyncOr, andMap, orMap) where import Control.LVish.Basics import Control.LVish.Internal (Par(WrapPar), unsafeDet) import Control.LVish.SchedIdempotent (liftIO, HandlerPool) import Data.LVar.IVar as IV import qualified Data.Atomics.Counter as C -------------------------------------------------------------------------------- -- | A parallel @And@ operation that can return early---whenever a False appears on either branch. asyncAnd :: Maybe HandlerPool -> (Par d s Bool) -> (Par d s Bool) -> (Bool -> Par d s ()) -> Par d s () asyncAnd hp leftM rightM kont = do -- Atomic counter, if we are the second True we write the result: cnt <- io$ C.newCounter 0 -- TODO we could share this for 3+-way and. let launch m = forkHP hp $ do b <- m case b of True -> do n <- io$ C.incrCounter 1 cnt if n==2 then kont True else return () False -> -- We COULD assume idempotency and execute kont False twice, -- but since we have the counter anyway let us dedup: do n <- io$ C.incrCounter 100 cnt if n < 200 -- Zero ops or one True. then kont False else return () launch leftM launch rightM return () -- OR this could expose: -- asyncAnd :: Maybe HandlerPool -> (Par d s Bool) -> (Par d s Bool) -> Par d s Bool -- -- I think this is one of those situations where (efficiently) abstracting is more -- complicated than permitting a code clone. -- | Analagous operation for @Or@. asyncOr :: Maybe HandlerPool -> (Par d s Bool) -> (Par d s Bool) -> (Bool -> Par d s ()) -> Par d s () asyncOr hp leftM rightM kont = do -- Atomic counter, if we`re the second True we write the result: cnt <- io$ C.newCounter 0 -- TODO we could share this for 3+-way and. let launch m = forkHP hp $ do b <- m case b of False -> do n <- io$ C.incrCounter 1 cnt if n==2 then kont False else return () True -> -- We COULD assume idempotency and execute kont False twice, -- but since we have the counter anyway let`s dedup: do n <- io$ C.incrCounter 100 cnt if n < 200 -- Zero ops or one True. then kont True else return () launch leftM launch rightM return () -- -------------------------------------------------------------------------------- -- Lift them to lists: -------------------------------------------------------------------------------- {-# INLINE andMap #-} andMap :: Maybe HandlerPool -> (a -> Par d s Bool) -> [a] -> Par d s Bool andMap = makeMapper asyncAnd {-# INLINE orMap #-} orMap :: Maybe HandlerPool -> (a -> Par d s Bool) -> [a] -> Par d s Bool orMap = makeMapper asyncOr {-# INLINE makeMapper #-} makeMapper :: (Maybe HandlerPool -> (Par d s Bool) -> (Par d s Bool) -> (Bool -> Par d s ()) -> Par d s ()) -> Maybe HandlerPool -> (a -> Par d s Bool) -> [a] -> Par d s Bool makeMapper asyncOp hp fn ls = aloop ls where aloop [] = return True aloop [x] = fn x aloop ls2 = do let (x,y) = fastChop ls2 tmp <- IV.new -- A place for the intermediate result asyncOp hp (aloop x) (aloop y) (IV.put tmp) -- writeIt IV.get tmp -- IM.getKey thekey table -------------------------------------------------------------------------------- -- Utilities: -------------------------------------------------------------------------------- fastChop :: [a] -> ([a],[a]) fastChop ls = loop [] ls ls where loop !acc !rst1 !rst2 = case rst2 of [] -> (acc,rst1) [_] -> (acc,rst1) -- Each time around we chop one from rst1 and two from rst2: _:_:rst2' -> let (hd:rst1') = rst1 in loop (hd:acc) rst1' rst2' io :: IO a -> Par d s a io = WrapPar . liftIO