lvish-1.1.1.1: Control/LVish/Logical.hs
{-# 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
-- <DUPLICATED CODE>
-- 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 ()
-- </DUPLICATED CODE>
--------------------------------------------------------------------------------
-- 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