module Control.Concurrent.CHP.Event where
import Control.Concurrent.STM
import Control.Monad
import Data.List
import Data.Maybe
import Data.Unique
import Control.Concurrent.CHP.Poison
import Control.Concurrent.CHP.ProcessId
newtype Event = Event (
Unique,
TVar (WithPoison
(Int,
[OfferSet])
))
type OfferSet = (TVar (Maybe [Int])
, [Int]
, ProcessId
, [Event])
newEvent :: Int -> IO Event
newEvent n
= do u <- newUnique
atomically $ do tv <- newTVar (NoPoison (n, []))
return $ Event (u, tv)
enrollEvent :: Event -> STM (WithPoison ())
enrollEvent (Event (_, tv))
= do x <- readTVar tv
case x of
PoisonItem -> return PoisonItem
NoPoison (count, offers) ->
do writeTVar tv $ NoPoison (count + 1, offers)
return $ NoPoison ()
resignEvent :: Event -> STM (WithPoison ())
resignEvent (Event (_, tv))
= do x <- readTVar tv
case x of
PoisonItem -> return PoisonItem
NoPoison (count, offers) ->
do writeTVar tv $ NoPoison (count 1, offers)
if (count 1 == length offers)
then completeEvent False tv
else return $ NoPoison ()
retractOffers :: [(TVar (Maybe [Int]), [Event])] -> STM ()
retractOffers = mapM_ retractAll
where
retractAll :: (TVar (Maybe [Int]), [Event]) -> STM ()
retractAll (tvid, evts) = mapM_ retract evts
where
retract :: Event -> STM ()
retract (Event (_,tv))
= do x <- readTVar tv
case x of
PoisonItem -> return ()
NoPoison (enrolled, offers) ->
let reducedOffers = filter (\(tvx,_,_,_) -> tvx /= tvid) offers in
writeTVar tv $ NoPoison (enrolled, reducedOffers)
completeEvent :: Bool -> TVar (WithPoison (Int, [OfferSet])) -> STM (WithPoison ())
completeEvent addPoison tv
= do x <- readTVar tv
case (x, addPoison) of
(PoisonItem, _) -> return PoisonItem
(NoPoison (_, offers), False) ->
do retractOffers $ [(tvw, events) | (tvw,_,_,events) <- offers]
sequence_ [writeTVar tvw (Just wx) | (tvw, wx, _,_) <- offers]
return $ NoPoison ()
(NoPoison (_, offers), True) ->
do retractOffers $ [(tvw, events) | (tvw,_,_,events) <- offers]
sequence_ [writeTVar tvw (Just $ wx ++ [0]) | (tvw, wx, _, _) <- offers]
writeTVar tv PoisonItem
return PoisonItem
enableEvents :: forall a. TVar (Maybe [Int]) -> ProcessId -> [(a, [Int], STM
(), Event)] -> Bool -> STM (Maybe (a, [ProcessId], [Int]))
enableEvents tvNotify pid events canCommitToWait
= do x <- checkAll
case (x, canCommitToWait) of
(Just (labels, pids, ns, act, Event (_,tv)), _) ->
act >> completeEvent False tv >>= \b ->
return $ Just (labels, pids, case b of {PoisonItem -> ns ++ [0]; _ -> ns})
(Nothing, False) -> return Nothing
(Nothing, True) ->
do sequence_ [do NoPoison (count, otherOffers) <- readTVar ab
writeTVar ab $ NoPoison (count,(tvNotify, ns, pid,
map fourth events):otherOffers)
| (_, ns, _, Event (_,ab)) <- events]
return Nothing
where
fourth (_,_,_,c) = c
checkAll :: STM (Maybe (a, [ProcessId], [Int], STM (), Event))
checkAll = do xs <- sequence [do x <- readTVar tv
return (x, evt)
| evt@(_,_,_,Event (_,tv)) <- events]
return $ fmap get $ find (ready . fst) xs
where
get :: (WithPoison (Int, [OfferSet]), (a, [Int], STM (), Event))
-> (a, [ProcessId], [Int], STM (), Event)
get (PoisonItem,(l,ns,act,e)) = (l,[pid],ns,act,e)
get (NoPoison (_,offers),(l,ns,act,e)) = (l,pid : [p | (_,_,p,_) <- offers],ns,act,e)
ready :: WithPoison (Int, [OfferSet]) -> Bool
ready PoisonItem = True
ready (NoPoison (count, offers)) = count == 1 + length offers
disableEvents :: TVar (Maybe [Int]) -> [Event] -> STM (Maybe [Int])
disableEvents tv events
= do x <- readTVar tv
when (isNothing x) $ retractOffers [(tv, events)]
return x
checkEventForPoison :: Event -> STM (WithPoison ())
checkEventForPoison (Event (_, tv))
= do x <- readTVar tv
case x of
PoisonItem -> return PoisonItem
_ -> return (NoPoison ())
poisonEvent :: Event -> STM ()
poisonEvent (Event (_,tv)) = completeEvent True tv >> return ()