packages feed

chp 1.2.0 → 1.3.0

raw patch · 18 files changed

+547/−393 lines, 18 filesdep ~basePVP ok

version bump matches the API change (PVP)

Dependency ranges changed: base

API changes (from Hackage documentation)

- Control.Concurrent.CHP.Monad: instance (MonadError e m) => MonadError e (LoopWhileT m)
- Control.Concurrent.CHP.Traces.CSP: instance Show CSPTrace
- Control.Concurrent.CHP.Traces.Structural: instance Show StructuralTrace
- Control.Concurrent.CHP.Traces.TraceOff: instance Show TraceOff
- Control.Concurrent.CHP.Traces.VCR: instance Show VCRTrace
+ Control.Concurrent.CHP.Monad: embedCHP :: CHP a -> CHP (IO (Maybe a))
+ Control.Concurrent.CHP.Monad: embedCHP1 :: (a -> CHP b) -> CHP (a -> IO (Maybe b))
+ Control.Concurrent.CHP.Monad: embedCHP1_ :: (a -> CHP b) -> CHP (a -> IO ())
+ Control.Concurrent.CHP.Monad: embedCHP_ :: CHP a -> CHP (IO ())
+ Control.Concurrent.CHP.Traces: labelAll :: (Trace t, Ord u) => t u -> t String
+ Control.Concurrent.CHP.Traces: recordedIndivEventLabel :: RecordedIndivEvent u -> u
+ Control.Concurrent.CHP.Traces: recordedIndivEventSeq :: RecordedIndivEvent u -> Integer
+ Control.Concurrent.CHP.Traces.CSP: instance (Ord u) => Show (CSPTrace u)
+ Control.Concurrent.CHP.Traces.Structural: instance (Ord u) => Show (StructuralTrace u)
+ Control.Concurrent.CHP.Traces.Structural: instance (Read a) => Read (EventHierarchy a)
+ Control.Concurrent.CHP.Traces.Structural: instance (Show a) => Show (EventHierarchy a)
+ Control.Concurrent.CHP.Traces.Structural: instance Foldable EventHierarchy
+ Control.Concurrent.CHP.Traces.Structural: instance Traversable EventHierarchy
+ Control.Concurrent.CHP.Traces.TraceOff: instance Show (TraceOff a)
+ Control.Concurrent.CHP.Traces.VCR: instance (Ord u) => Show (VCRTrace u)
- Control.Concurrent.CHP.Traces: BarrierSyncIndiv :: Unique -> RecordedIndivEvent
+ Control.Concurrent.CHP.Traces: BarrierSyncIndiv :: u -> Integer -> RecordedIndivEvent u
- Control.Concurrent.CHP.Traces: ChannelRead :: Unique -> RecordedIndivEvent
+ Control.Concurrent.CHP.Traces: ChannelRead :: u -> Integer -> RecordedIndivEvent u
- Control.Concurrent.CHP.Traces: ChannelWrite :: Unique -> RecordedIndivEvent
+ Control.Concurrent.CHP.Traces: ChannelWrite :: u -> Integer -> RecordedIndivEvent u
- Control.Concurrent.CHP.Traces: ClockSyncIndiv :: Unique -> String -> RecordedIndivEvent
+ Control.Concurrent.CHP.Traces: ClockSyncIndiv :: u -> Integer -> String -> RecordedIndivEvent u
- Control.Concurrent.CHP.Traces: class (Show t) => Trace t
+ Control.Concurrent.CHP.Traces: class Trace t
- Control.Concurrent.CHP.Traces: data RecordedIndivEvent
+ Control.Concurrent.CHP.Traces: data RecordedIndivEvent u
- Control.Concurrent.CHP.Traces: emptyTrace :: (Trace t) => t
+ Control.Concurrent.CHP.Traces: emptyTrace :: (Trace t) => t u
- Control.Concurrent.CHP.Traces: prettyPrint :: (Trace t) => t -> Doc
+ Control.Concurrent.CHP.Traces: prettyPrint :: (Trace t, Ord u) => t u -> Doc
- Control.Concurrent.CHP.Traces: runCHPAndTrace :: (Trace t) => CHP a -> IO (Maybe a, t)
+ Control.Concurrent.CHP.Traces: runCHPAndTrace :: (Trace t) => CHP a -> IO (Maybe a, t Unique)
- Control.Concurrent.CHP.Traces: type ChannelLabels = Map Unique String
+ Control.Concurrent.CHP.Traces: type ChannelLabels u = Map u String
- Control.Concurrent.CHP.Traces: type RecordedEvent = (RecordedEventType, Unique)
+ Control.Concurrent.CHP.Traces: type RecordedEvent u = (RecordedEventType, u)
- Control.Concurrent.CHP.Traces.CSP: CSPTrace :: (ChannelLabels, [RecordedEvent]) -> CSPTrace
+ Control.Concurrent.CHP.Traces.CSP: CSPTrace :: (ChannelLabels u, [RecordedEvent u]) -> CSPTrace u
- Control.Concurrent.CHP.Traces.CSP: newtype CSPTrace
+ Control.Concurrent.CHP.Traces.CSP: newtype CSPTrace u
- Control.Concurrent.CHP.Traces.CSP: runCHP_CSPTrace :: CHP a -> IO (Maybe a, CSPTrace)
+ Control.Concurrent.CHP.Traces.CSP: runCHP_CSPTrace :: CHP a -> IO (Maybe a, CSPTrace Unique)
- Control.Concurrent.CHP.Traces.Structural: StructuralTrace :: (ChannelLabels, Maybe (EventHierarchy RecordedIndivEvent)) -> StructuralTrace
+ Control.Concurrent.CHP.Traces.Structural: StructuralTrace :: (ChannelLabels u, Maybe (EventHierarchy (RecordedIndivEvent u))) -> StructuralTrace u
- Control.Concurrent.CHP.Traces.Structural: newtype StructuralTrace
+ Control.Concurrent.CHP.Traces.Structural: newtype StructuralTrace u
- Control.Concurrent.CHP.Traces.Structural: runCHP_StructuralTrace :: CHP a -> IO (Maybe a, StructuralTrace)
+ Control.Concurrent.CHP.Traces.Structural: runCHP_StructuralTrace :: CHP a -> IO (Maybe a, StructuralTrace Unique)
- Control.Concurrent.CHP.Traces.TraceOff: data TraceOff
+ Control.Concurrent.CHP.Traces.TraceOff: data TraceOff a
- Control.Concurrent.CHP.Traces.VCR: VCRTrace :: (ChannelLabels, [Set RecordedEvent]) -> VCRTrace
+ Control.Concurrent.CHP.Traces.VCR: VCRTrace :: (ChannelLabels u, [Set (RecordedEvent u)]) -> VCRTrace u
- Control.Concurrent.CHP.Traces.VCR: newtype VCRTrace
+ Control.Concurrent.CHP.Traces.VCR: newtype VCRTrace u
- Control.Concurrent.CHP.Traces.VCR: runCHP_VCRTrace :: CHP a -> IO (Maybe a, VCRTrace)
+ Control.Concurrent.CHP.Traces.VCR: runCHP_VCRTrace :: CHP a -> IO (Maybe a, VCRTrace Unique)

Files

Control/Concurrent/CHP/Alt.hs view
@@ -107,12 +107,15 @@ import Control.Monad.State import Control.Monad.Trans import Data.List+import qualified Data.Map as Map import Data.Maybe+import Data.Unique import System.IO  import Control.Concurrent.CHP.Base import Control.Concurrent.CHP.Event import Control.Concurrent.CHP.Guard+import Control.Concurrent.CHP.Monad import Control.Concurrent.CHP.Parallel import Control.Concurrent.CHP.Poison import Control.Concurrent.CHP.Traces.Base@@ -166,7 +169,7 @@ -- Whichever channel is chosen by both processes will not satisfy the priority -- at one end (if such priority between channels was supported). priAlt :: [CHP a] -> CHP a-priAlt items = (liftPoison $ priAlt' $ map wrapPoison items) >>= checkPoison+priAlt items = unwrapPoison $ priAlt' $ map wrapPoison items  -- | A useful operator to perform an 'alt'.  This operator is associative, -- and has arbitrary priority.  When you have lots of guards, it is probably easier@@ -281,26 +284,37 @@ -- behaviour as x. -- -- Added in version 1.1.0-every :: [CHP a] -> CHP [a]-every [] = liftPoison $ AltableT (SkipGuard [], return []) (return [])-every xs = liftPoison (AltableT (foldl1 merge $ map blankEvent gs, getEventPoison True) (return-  $ NoPoison False)) >>= checkPoison >>= \b -> if b then runParallel (map (unwrapPoison . liftTrace) bodies) else alt [every xs]+every :: forall a. [CHP a] -> CHP [a]+every [] = skip >> return []+every xs = liftPoison (AltableT (liftM ((:[]) . flip (,) (return True)) $ gs >>= foldM1 merge) (return+   False)) >>= \b -> if b then runParallel (map (unwrapPoison . liftTrace) bodies) else alt [every xs]   where-    (gs, bodies) = unzip $ map (pullOutAltable . wrapPoison) xs+    both :: Either String [(Guard, TraceT IO (WithPoison a))]+    both = mapM (checkSingle . pullOutAltable . wrapPoison) xs+    gs :: Either String [Guard]+    gs = liftM (map fst) both+    bodies = map snd $ fromRight both+    fromRight (Right x) = x+    fromRight _ = error "Reached unreachable code in every; bodies executed after bad guard" -    blankEvent :: Guard -> Guard-    blankEvent (EventGuard _ rec act es) = EventGuard [] rec act es-    blankEvent g = g+    checkSingle (Left err) = Left err+    checkSingle (Right []) = Left "Bad guard in every"+    checkSingle (Right [x]) = Right x+    checkSingle (Right _) = Left "Alt inside every" -    merge :: Guard -> Guard -> Guard-    merge (SkipGuard _) g = g-    merge g (SkipGuard _) = g-    merge StopGuard _ = StopGuard-    merge _ StopGuard = StopGuard-    merge (EventGuard _ recx actx esx) (EventGuard _ recy acty esy)-      = EventGuard [] (recx ++ recy) (actx >> acty) (esx ++ esy)-    merge _ _ = BadGuard "merging unsupported guards"+    merge :: Guard -> Guard -> Either String Guard+    merge SkipGuard g = return g+    merge g SkipGuard = return g+    merge StopGuard _ = return StopGuard+    merge _ StopGuard = return StopGuard+    merge (EventGuard recx actx esx) (EventGuard recy acty esy)+      = return $ EventGuard (\n -> recx n ++ recy n) (actx >> acty) (esx ++ esy)+    merge _ _ = badGuard "merging unsupported guards" +    foldM1 :: Monad m => (b -> b -> m b) -> [b] -> m b+    foldM1 f (y:ys) = foldM f y ys+    foldM1 _ _ = error "Reached unreachable code in every; guards empty in non-empty case"+ -- | A useful operator that acts like 'every'.  The operator is associative and -- commutative (see 'every' for notes on idempotence).  When you have lots of things -- to join with this operator, it's probably easier to use the 'every' function.@@ -336,110 +350,52 @@ -- I had some memory efficiency problems so I went with the state-monad-based -- approach instead. -priAlt' :: forall a. [CHP' a] -> CHP' a+priAlt' :: forall a. [CHP' (WithPoison a)] -> CHP' (WithPoison a) priAlt' items   -- Our guard is a nested guard of all our sub-guards.   -- Our action-if-not-guard is to do the selection ourselves.-  -- Our body is to read the numbered list, strip one off and follow the path,-  -- ignoring the action-if-not-guard of the chosen body-  = AltableT (NestedGuards $ wrappedGuards-               ,executeNumberedBody)-             (selectFromGuards >> executeNumberedBody)+  = AltableT flattenedGuards selectFromGuards   where-    wrappedGuards :: [Guard]-    wrappedGuards = map wrap flattenedGuards-      where-        wrap :: (Int, Guard) -> Guard-        wrap (n, SkipGuard ns) = SkipGuard $ n : ns-        wrap (n, EventGuard ns e act ab) = EventGuard (n:ns) e act ab-        wrap (n, TimeoutGuard g) = TimeoutGuard $-          do g' <- g-             return $ do ns <- g'-                         return (n : ns)-        wrap (_, g@(BadGuard _)) = g-        wrap (_, _) = BadGuard "wrapped"--    -- Polls the available guards, but ignores timeout guards and alting barrier-    -- guards-    checkNormalGuards :: STM (Maybe Int)-    checkNormalGuards = foldl1 orElse $-                          (map checkGuard flattenedGuards) ++ [return Nothing]-      where-        checkGuard :: (Int, Guard) -> STM (Maybe Int)-        checkGuard (n, BadGuard _) = return $ Just n-        checkGuard (n, SkipGuard {}) = return $ Just n-        checkGuard (_, _) = retry--    -- Waits for one of the normal (non-alting barrier) guards to be ready,-    -- or the given transaction to complete-    waitNormalGuards :: STM [Int] -> IO (Bool, [Int])-    waitNormalGuards extra-      = do guards <- mapM enable wrappedGuards-           atomically $ foldl1 orElse (wrap True extra : map (wrap False) guards)-      where-        enable :: Guard -> IO (STM [Int])-        enable (BadGuard _) = return $ return []-        enable (SkipGuard ns) = return $ return ns-        enable (TimeoutGuard g) = g-        enable _ = return retry -- This effectively ignores other guards--        wrap :: Bool -> STM [Int] -> STM (Bool, [Int])-        wrap b m = do x <- m-                      return (b, x)--     -- The list of guards without any NestedGuards or StopGuards:-    flattenedGuards :: [(Int, Guard)]-    flattenedGuards = (flatten $ zip [0..] $ map (fst . pullOutAltable) items)+    flattenedGuards :: Either String [(Guard, TraceT IO (WithPoison a))]+    flattenedGuards = liftM (filter (not . isStopGuard . fst)) altStuff       where-        flatten :: [(Int, Guard)] -> [(Int,Guard)]-        flatten [] = []-        flatten ((n,x):xs) = case x of-          NestedGuards gs -> flatten $ zip (repeat n) gs ++ xs-          StopGuard -> flatten xs-          g -> (n, g) : flatten xs+        altStuff :: Either String [(Guard, TraceT IO (WithPoison a))]+        altStuff = liftM concat $ mapM pullOutAltable items      -- The alting barrier guards:-    eventGuards :: [([RecordedIndivEvent], [Int], STM (), [Event])]-    eventGuards = [(rec,ns,act,ab) | EventGuard ns rec act ab <- wrappedGuards]--    -- We must use isPrefixOf, because things are added in the case of poison-    findEventAssoc :: [Int] -> [RecordedIndivEvent]-    findEventAssoc x = case filter (\(_,y,_,_) -> y `isPrefixOf` x) eventGuards of-      [(rec,_,_,_)] -> rec-      _ -> error "Could not find associated event in alt, internal logic error" -                   -        -    --    -- Stores a list of ints in the state-    storeChoice :: [Int] -> TraceT IO ()-    storeChoice ns = modify (\(_, es) -> (ns, es))--    isBadGuard :: Guard -> Bool-    isBadGuard (BadGuard _) = True-    isBadGuard _ = False+    eventGuards :: [Guard] -> [((Unique -> Integer) -> [RecordedIndivEvent Unique], Int, STM (), [Event])]+    eventGuards guards = [(rec,n,act,ab) | (n, EventGuard rec act ab) <- zip [0..] guards] -    -- Performs the select operation on all the guards.  The choice is stored-    -- in the state ready to execute the bodies-    selectFromGuards :: TraceT IO ()-    selectFromGuards-      | null eventGuards-         = do (_,ns) <- liftIO $ waitNormalGuards retry-              storeChoice ns-      | any isBadGuard wrappedGuards-         = let str = head [s | BadGuard s <- wrappedGuards]-               err = "ALTing not supported on given guard: " ++ str+    -- Performs the select operation on all the guards, and then executes the body+    selectFromGuards :: TraceT IO (WithPoison a)+    selectFromGuards = case flattenedGuards of+      Left str ->+           let err = "ALTing not supported on given guard: " ++ str            in liftIO $ do hPutStrLn stderr err                           ioError $ userError err-      | otherwise-         = do earliestReady <- liftIO $ atomically checkNormalGuards++      Right both+        | null (eventGuards $ map fst both) ->+           join $ liftM snd $ liftIO $ waitNormalGuards both retry+        | otherwise ->+           do let (guards, bodies) = unzip both+                  earliestReady = findIndex isSkipGuard guards+                  recordAndRun _ (Signal PoisonItem)+                    = return PoisonItem+                  recordAndRun m (Signal (NoPoison n))+                    = let (EventGuard rec _ _, body) = both !! n+                      in recordEvent (rec $ makeLookup m) >> body+                  justRun (Signal PoisonItem) = return PoisonItem+                  justRun (Signal (NoPoison n)) = bodies !! n+                  guardsAndSignal :: [(Guard, (SignalValue, Map.Map Unique Integer))]+                  guardsAndSignal = zip guards (zip (map (Signal . NoPoison) [0..]) (repeat Map.empty))               tv <- liftIO . atomically $ newTVar Nothing               pid <- getProcessId-              (_, tr) <- get+              tr <- get               mn <- liftIO . atomically $ do                       ret <- enableEvents tv pid-                        (maybe id take earliestReady [(x,y,z) | (_,x,y,z)<-eventGuards])+                        (maybe id take earliestReady [(Signal $ NoPoison x,y,z) | (_,x,y,z)<-eventGuards guards])                         (isNothing earliestReady)                       maybe (return ()) (\(_,es) -> recordEventLast (nub es) tr) ret                       return ret@@ -447,47 +403,50 @@                 -- An event -- and we were the last person to arrive:                 -- The event must have been higher priority than any other                 -- ready guards-                (Just (ns, _), _) ->-                    do recordEvent $ findEventAssoc ns-                       storeChoice ns+                (Just ((n, m), _), _) -> recordAndRun m n                 -- No events were ready, but there was an available normal                 -- guards.  Re-run the normal guards; at least one will be ready                 (Nothing, Just _) ->-                  do (_, ns) <- liftIO $ waitNormalGuards retry-                     storeChoice ns+                  join $ liftM snd $ liftIO $ waitNormalGuards both retry                 -- No events ready, no other guards ready either                 -- Events will have been enabled; wait for everything:                 (Nothing, Nothing) ->-                    do (wasAltingBarrier, ns) <- liftIO $ waitNormalGuards $ waitAlting tv+                    do (wasAltingBarrier, (n, m)) <- liftIO $ waitNormalGuards+                         guardsAndSignal $ waitAlting tv                        if wasAltingBarrier-                         then recordEvent (findEventAssoc ns) >> storeChoice ns -- It was a barrier, all done+                         then recordAndRun m n -- It was a barrier, all done                          else                             -- Another guard fired, but we must check in case                             -- we have meanwhile been committed to taking an                             -- event:-                            do mn' <- liftIO . atomically $ disableEvents tv (concatMap fourth eventGuards)+                            do mn' <- liftIO . atomically $ disableEvents tv (concatMap fourth+                                 $ eventGuards guards)                                case mn' of                                  -- An event overrides our non-event choice:-                                 Just bns -> recordEvent (findEventAssoc bns) >> storeChoice bns+                                 Just (x, m') -> recordAndRun m' x                                  -- Go with the original option, no events                                  -- became ready:-                                 Nothing -> storeChoice ns+                                 Nothing -> justRun n       where-        waitAlting :: TVar (Maybe [Int]) -> STM [Int]+        waitAlting :: SignalVar -> STM (SignalValue, Map.Map Unique Integer)         waitAlting tv = do b <- readTVar tv                            case b of                              Nothing -> retry                              Just ns -> return ns         fourth (_,_,_,c) = c -    executeNumberedBody :: TraceT IO a-    executeNumberedBody-      = do st <- get-           case st of-             ((g:gs), es) ->-               do put (gs, es)-                  snd $ pullOutAltable (items !! g)-             ([], _) -> liftIO $-               do hPutStrLn stderr "ALTing not supported on given guard (no index)"-                  ioError $ userError "ALTing not supported on given guard (no index)"+        makeLookup :: Map.Map Unique Integer -> Unique -> Integer+        makeLookup m u = fromMaybe (error "CHP: Unique not found in alt") $ Map.lookup u m ++-- Waits for one of the normal (non-alting barrier) guards to be ready,+-- or the given transaction to complete+waitNormalGuards :: [(Guard, b)] -> STM b -> IO (Bool, b)+waitNormalGuards guards extra+  = do enabled <- mapM enable guards+       atomically $ foldl1 orElse (liftM ((,) True) extra : enabled)+  where+    enable :: (Guard, b) -> IO (STM (Bool, b))+    enable (SkipGuard, x) = return $ return (False, x)+    enable (TimeoutGuard g, x) = liftM (>> return (False, x)) g+    enable _ = return retry -- This effectively ignores other guards
Control/Concurrent/CHP/Barriers.hs view
@@ -91,13 +91,13 @@ -- | Synchronises on the given barrier.  You must be enrolled on a barrier in order -- to synchronise on it.  Returns the new phase, following the synchronisation. syncBarrier :: Enrolled PhasedBarrier phase -> CHP phase-syncBarrier = syncBarrierWith (Just . BarrierSyncIndiv)+syncBarrier = syncBarrierWith (indivRecJust BarrierSyncIndiv)      -- | Finds out the current phase a barrier is on. currentPhase :: Enrolled PhasedBarrier phase -> CHP phase currentPhase (Enrolled (Barrier (_, tv, _))) = liftIO $ atomically $ readTVar tv -repeatUntil :: (Monad m, Eq a) => (a -> Bool) -> m a -> m ()+repeatUntil :: (Monad m) => (a -> Bool) -> m a -> m () repeatUntil target comp = do x <- comp                              unless (target x) $ repeatUntil target comp 
Control/Concurrent/CHP/Base.hs view
@@ -33,20 +33,23 @@ -- monads, and poison.  Not publicly visible. module Control.Concurrent.CHP.Base where +import Control.Applicative+import Control.Arrow import Control.Concurrent.STM-#if __GLASGOW_HASKELL__ >= 609+-- #if __GLASGOW_HASKELL__ >= 609 -- I can't figure out the new Exception system in GHC 6.10 and how I catch all -- exceptions (see GHC bug #2655), so I'm just going to use the old system:-import qualified Control.OldException as C-#else-import qualified Control.Exception as C-#endif+--import qualified Control.OldException as C+-- #else+--import qualified Control.Exception as C+-- #endif import Control.Monad.Error import Control.Monad.Reader import Control.Monad.State import Control.Monad.Writer import Control.Monad.Trans import qualified Data.Map as Map+import Data.Unique import qualified Text.PrettyPrint.HughesPJ  import Control.Concurrent.CHP.Guard@@ -72,11 +75,15 @@ -- 'Control.Concurrent.CHP.Monad.runCHP_' to execute programs in this -- monad. newtype CHP a = PoisonT (ErrorT PoisonError CHP' a)-  deriving (Monad, MonadIO)+  deriving (Functor, Monad, MonadIO) +instance Applicative CHP where+  pure = return+  (<*>) = ap+ data CHP' a = AltableTRet a | AltableT {   -- The guard, and body to execute after the guard-  getAltable :: (Guard, TraceT IO a),+  getAltable :: Either String [(Guard, TraceT IO a)],   -- The body to execute without a guard   getStandard :: TraceT IO a } @@ -94,17 +101,22 @@ -- but because its type is only determined by its return type, you may wish -- to use the already-typed functions offered in each trace module -- see the -- modules in "Control.Concurrent.CHP.Traces".-class Show t => Trace t where+--+-- The trace type involved became parameterised in version 1.3.0.+class Trace t where   -- | Runs the given CHP program, and returns its return value and the trace.   --  The return value is a Maybe type because the process may have exited   -- due to uncaught poison.  In that case Nothing is return as the result.-  runCHPAndTrace :: CHP a -> IO (Maybe a, t)+  runCHPAndTrace :: CHP a -> IO (Maybe a, t Unique)   -- | The empty trace.-  emptyTrace :: t+  emptyTrace :: t u   -- | Pretty-prints the given trace using the "Text.PrettyPrint.HughesPJ"   -- module.-  prettyPrint :: t -> Text.PrettyPrint.HughesPJ.Doc+  prettyPrint :: Ord u => t u -> Text.PrettyPrint.HughesPJ.Doc +  -- | Added in version 1.3.0.+  labelAll :: Ord u => t u -> t String+ -- | A class indicating that something is poisonable. class Poisonable c where   -- | Poisons the given item.@@ -124,13 +136,13 @@   AltableTRet x -> return x   AltableT _ st -> st -pullOutAltable :: CHP' a -> (Guard, TraceT IO a)+pullOutAltable :: CHP' a -> Either String [(Guard, TraceT IO a)] pullOutAltable m = case m of-  AltableTRet x -> (badGuard "return", return x)+  AltableTRet _ -> badGuard "return"   AltableT alt _ -> alt  liftTrace :: TraceT IO a -> CHP' a-liftTrace m = AltableT (badGuard "lifted action", m) m+liftTrace m = AltableT (badGuard "lifted action") m  wrapPoison :: CHP a -> CHP' (WithPoison a) wrapPoison (PoisonT m) = (liftM $ either (const PoisonItem) NoPoison) $@@ -184,37 +196,28 @@ liftSTM = liftIO . atomically  getProcessId :: TraceT IO ProcessId-getProcessId = do (_, x) <- get+getProcessId = do x <- get                   case x of                     Trace (pid,_,_) -> return pid                     NoTrace -> return emptyProcessId  runCHPProgramWith :: TraceStore -> (TraceStore -> t) -> CHP a -> IO (Maybe a, t) runCHPProgramWith start f (PoisonT p)-  = do (x, (_, t)) <- runStateT (liftM (either (const Nothing) Just) $ pullOutStandard $ runErrorT p) ([], start)+  = do (x, t) <- runStateT (liftM (either (const Nothing) Just) $ pullOutStandard $ runErrorT p) start        return (x, f t) -runCHPProgramWith' :: SubTraceStore -> (ChannelLabels -> SubTraceStore -> IO t) -> CHP a -> IO (Maybe a, t)+runCHPProgramWith' :: SubTraceStore -> (ChannelLabels Unique -> SubTraceStore -> IO t) -> CHP a -> IO (Maybe a, t) runCHPProgramWith' subStart f p   = do tv <- atomically $ newTVar Map.empty        (x, Trace (_,_,t)) <- runCHPProgramWith                                 (Trace (rootProcessId, tv, subStart))                                 id p-                             `C.catch` const (return (Nothing,-                               Trace (undefined, undefined, subStart)))+--                             `C.catch` const (return (Nothing,+--                               Trace (undefined, undefined, subStart)))        l <- atomically $ readTVar tv        t' <- f l t        return (x, t') -getEventPoison :: a -> TraceT IO (WithPoison a)-getEventPoison x-        = do (ns,y) <- get-             case ns of-               [] -> return $ NoPoison x-               _ -> do put ([],y)-                       return PoisonItem-- -- ========== -- Instances:  -- ==========@@ -243,10 +246,10 @@   -- f :: a -> AltableT g m b   m >>= f = case m of              AltableTRet x -> f x-             AltableT (grd, altBody) nonAlt ->-              let altBody' = altBody >>= pullOutStandard . f+             AltableT altBody nonAlt ->+              let altBody' = liftM (map $ second (>>= pullOutStandard . f)) altBody                   nonAlt' = nonAlt >>= pullOutStandard . f-              in AltableT (grd, altBody') nonAlt'+              in AltableT altBody' nonAlt'   return x = AltableTRet x  instance MonadIO CHP' where
Control/Concurrent/CHP/BroadcastChannels.hs view
@@ -119,24 +119,24 @@  instance WriteableChannel BroadcastChanout where   extWriteChannel (BO (BC (b, tv))) m-    = do syncBarrierWith (Just . ChannelWrite)+    = do syncBarrierWith (indivRecJust ChannelWrite)            $ Enrolled b          m >>= liftIO . atomically . writeTVar tv-         syncBarrierWith (const Nothing)+         syncBarrierWith (const $ const Nothing)            $ Enrolled b-         syncBarrierWith (const Nothing)+         syncBarrierWith (const $ const Nothing)            $ Enrolled b          return ()  instance ReadableChannel (Enrolled BroadcastChanin) where   extReadChannel (Enrolled (BI (BC (b, tv)))) f-    = do syncBarrierWith (Just . ChannelRead)+    = do syncBarrierWith (indivRecJust ChannelRead)            $ Enrolled b-         syncBarrierWith (const Nothing)+         syncBarrierWith (const $ const Nothing)            $ Enrolled b          x <- liftIO (atomically $ readTVar tv)          y <- f x-         syncBarrierWith (const Nothing)+         syncBarrierWith (const $ const Nothing)            $ Enrolled b          return y @@ -204,25 +204,25 @@  instance WriteableChannel (Enrolled ReduceChanout) where   extWriteChannel (Enrolled (GO (GC (b, tv, (f,_))))) m-    = do syncBarrierWith (Just . ChannelWrite)+    = do syncBarrierWith (indivRecJust ChannelWrite)            $ Enrolled b          m >>= liftIO . atomically . \x -> readTVar tv >>= writeTVar tv . f x-         syncBarrierWith (const Nothing)+         syncBarrierWith (const $ const Nothing)            $ Enrolled b-         syncBarrierWith (const Nothing)+         syncBarrierWith (const $ const Nothing)            $ Enrolled b          return ()  instance ReadableChannel ReduceChanin where   extReadChannel (GI (GC (b, tv, (_, empty)))) f-    = do syncBarrierWith (Just . ChannelRead)+    = do syncBarrierWith (indivRecJust ChannelRead)            $ Enrolled b-         syncBarrierWith (const Nothing)+         syncBarrierWith (const $ const Nothing)            $ Enrolled b          x <- liftIO (atomically $ readTVar tv)          y <- f x          liftIO (atomically $ writeTVar tv empty)-         syncBarrierWith (const Nothing)+         syncBarrierWith (const $ const Nothing)            $ Enrolled b          return y 
Control/Concurrent/CHP/CSP.hs view
@@ -48,20 +48,18 @@ import Control.Concurrent.CHP.Enroll import Control.Concurrent.CHP.Guard import Control.Concurrent.CHP.Mutex-import Control.Concurrent.CHP.Poison import Control.Concurrent.CHP.Traces.Base  -- First engages in event, then executes the body.  The returned value is suitable -- for use in an alt-buildOnEventPoison :: (Unique -> Maybe RecordedIndivEvent) -> Event.Event -> STM () -> CHP a -> CHP a+buildOnEventPoison :: (Unique -> (Unique -> Integer) -> Maybe (RecordedIndivEvent Unique)) -> Event.Event -> STM () -> CHP a -> CHP a buildOnEventPoison rec e act body-  = liftPoison (AltableT (theGuard, getEventPoison True)-                   (return $ NoPoison False))-    >>= checkPoison >>= \b -> if b then body else-      alt [liftPoison $ AltableT (theGuard, getEventPoison ()) (return $ NoPoison())] >>=-        checkPoison >> body+  = liftPoison (AltableT (Right [(theGuard, return True)])+                   (return False))+    >>= \b -> if b then body else+      alt [liftPoison $ AltableT (Right [(theGuard, return ())]) (return ())] >> body     where-      theGuard = EventGuard [] (maybeToList $ rec $ Event.getEventUnique e) act [e]+      theGuard = EventGuard (maybeToList . rec (Event.getEventUnique e)) act [e]  scopeBlock :: CHP a -> (a -> CHP b) -> IO () -> CHP b scopeBlock start body errorEnd@@ -75,7 +73,7 @@  -- | Synchronises on the given barrier.  You must be enrolled on a barrier in order -- to synchronise on it.  Returns the new phase, following the synchronisation.-syncBarrierWith :: (Unique -> Maybe RecordedIndivEvent)+syncBarrierWith :: (Unique -> (Unique -> Integer) -> Maybe (RecordedIndivEvent Unique))   -> Enrolled PhasedBarrier phase -> CHP phase syncBarrierWith rec (Enrolled (Barrier (e,tv, fph)))     = buildOnEventPoison rec e incPhase@@ -115,13 +113,13 @@     = do liftSTM (Event.enrollEvent e) >>= checkPoison          x <- f $ Enrolled b          liftSTM (Event.resignEvent e) >>= checkPoison >>= (\es ->-           do (_,tr) <- liftPoison $ liftTrace get+           do tr <- liftPoison $ liftTrace get               when (not $ null es) $ liftSTM $ recordEventLast (nub es) tr)          return x    resign (Enrolled (Barrier (e, _, _))) m     = do liftSTM (Event.resignEvent e) >>= checkPoison >>= (\es ->-           do (_,tr) <- liftPoison $ liftTrace get+           do tr <- liftPoison $ liftTrace get               when (not $ null es) $ liftSTM $ recordEventLast (nub es) tr)          x <- m          liftSTM (Event.enrollEvent e) >>= checkPoison
Control/Concurrent/CHP/Channels.hs view
@@ -322,7 +322,7 @@ instance ReadableChannel Chanin where   readChannel (Chanin c)     = let (e, m) = startReadChannelC c in-      buildOnEventPoison (Just . ChannelRead) e (return ()) (liftSTM $+      buildOnEventPoison (indivRecJust ChannelRead) e (return ()) (liftSTM $         do x <- m            endReadChannelC c            return x) >>= checkPoison@@ -330,7 +330,7 @@   extReadChannel (Chanin c) body     = let (e, m) = startReadChannelC c in       scopeBlock-        (buildOnEventPoison (Just . ChannelRead) e (return ()) (liftSTM m) >>= checkPoison)+        (buildOnEventPoison (indivRecJust ChannelRead) e (return ()) (liftSTM m) >>= checkPoison)         (\val -> do x <- body val                     liftSTM $ endReadChannelC c                     return x)@@ -339,7 +339,7 @@ instance WriteableChannel Chanout where   writeChannel (Chanout c) x     = let (e, m) = startWriteChannelC c in-        buildOnEventPoison (Just . ChannelWrite) e (return ())+        buildOnEventPoison (indivRecJust ChannelWrite) e (return ())           (liftM2 (++)             (liftSTM $ sequence [m, sendWriteChannelC c x])             (liftSTM $ sequence [endWriteChannelC c]))@@ -347,7 +347,7 @@   extWriteChannel (Chanout c) body     = let (e, m) = startWriteChannelC c in       scopeBlock-        (buildOnEventPoison (Just . ChannelWrite)+        (buildOnEventPoison (indivRecJust ChannelWrite)           e (return ()) (liftSTM m) >>= checkPoison)         (const $ sequence [body >>= liftSTM . sendWriteChannelC c                           ,liftSTM (endWriteChannelC c)]
Control/Concurrent/CHP/Clocks.hs view
@@ -105,8 +105,10 @@ -- This is also true if not every process enrolled on a barrier wants to take action -- every phase -- a clock allows those processes to remain sleeping, rather than -- wake up only to sleep again,+--  -- * Barriers support choice, but clocks do not.  This means clocks are both -- less powerful, but also faster than barriers.+--  -- * Barriers choose their next phase using their incrementing function.  Clocks -- are more flexible, in that their next phase is chosen solely by looking at the -- requests from the various processes.  Hence why Double is a suitable type for@@ -120,11 +122,11 @@ import Control.Monad hiding (mapM, mapM_) import Control.Monad.State (get) import Control.Monad.Trans+import Data.Foldable (mapM_) -- Needed for testing: --import Data.Maybe import qualified Data.Sequence as Seq import qualified Data.Set as Set-import Data.Traversable import Data.Unique import Prelude hiding (mapM, mapM_) @@ -218,31 +220,30 @@ poisonTVar :: TVar (WithPoison a) -> STM () poisonTVar = flip writeTVar PoisonItem --- Provides mapM_ for Traversable:-mapM_ :: (Traversable t, Monad m) => (a -> m b) -> t a -> m ()-mapM_ f x = mapM f x >> return ()+type TimeVar time = TVar (WithPoison (Maybe (time, Integer)))  data TimerData time   = TimerData {       curTime :: time+     ,seqTime :: Integer      ,enrolledOnTimer :: Int      -- A slightly more efficient way of knowing current offers:      ,offeredOnTimer :: Int       -- Offers are held, sorted by time.  We rely on the fact that for all x,       -- Nothing < Just x-     ,timerOffersNext :: Maybe ([ProcessId], TVar (WithPoison (Maybe time)))-     ,timerOffersBefore :: [([ProcessId], (time, TVar (WithPoison (Maybe time))))]-     ,timerOffersAfter :: [([ProcessId], (time, TVar (WithPoison (Maybe time))))]-     ,timerEventPool :: Seq.Seq (TVar (WithPoison (Maybe time)))+     ,timerOffersNext :: Maybe ([ProcessId], TimeVar time)+     ,timerOffersBefore :: [([ProcessId], (time, TimeVar time))]+     ,timerOffersAfter :: [([ProcessId], (time, TimeVar time))]+     ,timerEventPool :: Seq.Seq (TimeVar time)      } -- Uncomment these lines while testing: --  deriving (Eq, Show) --instance Show (TVar (WithPoison (Maybe a))) where show = const "<tv>"  emptyTimerData :: time -> TimerData time-emptyTimerData t = TimerData t 0 0 Nothing [] [] Seq.empty+emptyTimerData t = TimerData t 0 0 0 Nothing [] [] Seq.empty -enrollTimerData :: Maybe (TVar (WithPoison (Maybe time))) -> TimerData time -> TimerData time+enrollTimerData :: Maybe (TimeVar time) -> TimerData time -> TimerData time enrollTimerData me td   = td {enrolledOnTimer = enrolledOnTimer td + 1        -- It's important that the event goes on the front, so that we don't re-use@@ -273,7 +274,7 @@   offerTimerData :: forall time. Ord time => ProcessId -> Maybe time -> TimerData time-  -> STM (TimerData time, TVar (WithPoison (Maybe time)))+  -> STM (TimerData time, TimeVar time) offerTimerData pid Nothing td = case timerOffersNext td of   Nothing -> do     (e, pool) <- spareEvent $ timerEventPool td@@ -326,14 +327,14 @@          liftSTM (modifyTVar tv $ enrollTimerData $ Just ev)            >>= checkPoison          x <- f $ Enrolled tim-         ts <- liftPoison $ liftTrace $ liftM snd get+         ts <- liftPoison $ liftTrace $ get          liftSTM (modifyTVar' tv $ checkCompletion u sh ts . resignTimerData True)            >>= checkPoison          return x    -- For temporary resignations, we don't touch the event pool   resign (Enrolled (Clock (tv, u, sh))) m-    = do ts <- liftPoison $ liftTrace $ liftM snd get+    = do ts <- liftPoison $ liftTrace $ get          liftSTM (modifyTVar' tv (checkCompletion u sh ts . resignTimerData False))            >>= checkPoison          x <- m@@ -346,12 +347,13 @@   | offeredOnTimer td == enrolledOnTimer td =       case timerOffersAfter td of         ((pids, (newT, ev)):rest) -> do-          writeTVar ev $ NoPoison $ Just newT-          maybe (return ()) (flip writeTVar (NoPoison $ Just newT) . snd) (timerOffersNext td)+          writeTVar ev $ NoPoison $ Just (newT, seqTime td)+          maybe (return ()) (flip writeTVar (NoPoison $ Just (newT, seqTime td)) . snd) (timerOffersNext td)           recordEventLast [((Event.ClockSync $ sh newT,u)             , Set.fromList $ pids ++ maybe [] fst (timerOffersNext td))]             ts           return $ td { timerOffersAfter = rest+                      , seqTime = succ $ seqTime td                       , offeredOnTimer =                           offeredOnTimer td                             - (length pids + maybe 0 (length . fst) (timerOffersNext td))@@ -367,8 +369,8 @@                       }         [] -> case timerOffersBefore td of                 ((pids, (newT, ev)):rest) -> do-                  writeTVar ev $ NoPoison $ Just newT-                  maybe (return ()) (flip writeTVar (NoPoison $ Just newT) . snd) (timerOffersNext td)+                  writeTVar ev $ NoPoison $ Just (newT, seqTime td)+                  maybe (return ()) (flip writeTVar (NoPoison $ Just (newT, seqTime td)) . snd) (timerOffersNext td)                   return $                     td { timerOffersAfter = rest                        , timerOffersBefore = []@@ -376,6 +378,7 @@                            offeredOnTimer td                              - (length pids + maybe 0 (length . fst) (timerOffersNext td))                        , curTime = newT+                       , seqTime = succ $ seqTime td                        , timerOffersNext = Nothing                        -- The event will only be re-used once we get to the                        -- end of the list, and thus all the people are ready@@ -389,7 +392,7 @@   | otherwise = return td  waitClock :: Ord time =>-  ProcessId -> TraceStore -> Enrolled Clock time -> Maybe time -> STM (STM (WithPoison time))+  ProcessId -> TraceStore -> Enrolled Clock time -> Maybe time -> STM (STM (WithPoison (time, Integer))) waitClock pid ts (Enrolled (Clock (tv, u, sh))) ph   = do x <- readTVar tv        case x of@@ -419,11 +422,11 @@   getCurrentTime (Enrolled (Clock (tv, _, _)))     = liftSTM (liftM (fmap curTime) $ readTVar tv) >>= checkPoison   wait c@(Enrolled (Clock (_, u, sh))) mt-    = do ts <- liftPoison $ liftTrace $ liftM snd get+    = do ts <- liftPoison $ liftTrace $ get          pid <- liftPoison $ liftTrace $ getProcessId          waitAct <- liftSTM $ waitClock pid ts c mt-         t <- liftSTM waitAct >>= checkPoison-         liftPoison $ liftTrace $ recordEvent [ClockSyncIndiv u $ sh t]+         (t, s) <- liftSTM waitAct >>= checkPoison+         liftPoison $ liftTrace $ recordEvent [ClockSyncIndiv u s $ sh t]          return t  instance Ord time => Poisonable (Enrolled Clock time) where
Control/Concurrent/CHP/Event.hs view
@@ -28,7 +28,10 @@ -- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  --TODO document this (for internal purposes)-module Control.Concurrent.CHP.Event where+module Control.Concurrent.CHP.Event (RecordedEventType(..), Event, getEventUnique,+  SignalVar, SignalValue(..), enableEvents, disableEvents,+  newEvent, newEventUnique, enrollEvent, resignEvent, poisonEvent, checkEventForPoison,+  testAll) where  import Control.Concurrent.STM import Control.Monad@@ -37,7 +40,9 @@ import qualified Data.Map as Map import Data.Maybe import qualified Data.Set as Set+import qualified Data.Traversable as T import Data.Unique+import Prelude hiding (seq)   import Control.Concurrent.CHP.Poison@@ -55,6 +60,7 @@   RecordedEventType, -- Event type for trace recording   TVar (WithPoison     (Int, -- Enrolled count+     Integer, -- Event sequence count     [OfferSet]) -- A list of offer sets  )) @@ -71,15 +77,32 @@ getEventUnique :: Event -> Unique getEventUnique (Event (u,_,_)) = u -getEventTVar :: Event -> TVar (WithPoison (Int, [OfferSet]))+getEventTVar :: Event -> TVar (WithPoison (Int, Integer, [OfferSet])) getEventTVar (Event (_,_,tv)) = tv  getEventType :: Event -> RecordedEventType getEventType (Event (_,t,_)) = t -newtype OfferSet = OfferSet (TVar (Maybe [Int]) -- Variable to use to signal when committed+-- The value used to pass information to a waiting process once one of their events+-- has fired (and they have been committed to it).  The Int is an index into their+-- list of guards+newtype SignalValue = Signal (WithPoison Int)+  deriving (Eq, Show)++type SignalVar = TVar (Maybe (SignalValue, Map.Map Unique Integer))++addPoison :: SignalValue -> SignalValue+addPoison = const $ Signal PoisonItem++nullSignalValue :: SignalValue+nullSignalValue = Signal $ NoPoison (-1)++isNullSignal :: SignalValue -> Bool+isNullSignal (Signal n) = n == NoPoison (-1)++newtype OfferSet = OfferSet (SignalVar -- Variable to use to signal when committed                 , ProcessId -- Id of the process making the offer-                , [([Int], Map.Map Event ())]) -- Value to send when committed+                , [(SignalValue, Map.Map Event ())]) -- Value to send when committed                                     -- A list of all sets of events currently offered  instance Eq OfferSet where@@ -98,9 +121,42 @@ allEventsInOffer :: OfferSet -> Map.Map Event () allEventsInOffer (OfferSet (_, _, eventSets)) = unionAll (map snd eventSets) -search :: [OfferSet] -> Map.Map Event Bool -> Maybe (STM (), Map.Map Unique (RecordedEventType,-  Set.Set ProcessId), [(TVar (Maybe [Int]), [Event])])-search [] _ = Just (return (), Map.empty, [])+getAndIncCounter :: Event -> a -> STM (WithPoison Integer)+getAndIncCounter e _+  = do x <- readTVar (getEventTVar e)+       case x of+         PoisonItem -> return PoisonItem+         NoPoison (a, n, c) -> do writeTVar (getEventTVar e) $+                                    NoPoison (a, succ n, c)+                                  return $ NoPoison n++-- | search is /not/ used for discovering offers.  It is used for looking for possible+-- resolutions to a collection of offer sets.  It is pure; it performs no STM actions,+-- it just searches the offer-sets (which will have been discovered through STM)+-- for completions.+--+-- search performs a 2-dimensional traversal of the offers.  The search function+-- is called with a list of offer-sets.  For the offer-set at the head, it calls+-- tryAll.  tryAll searches through each offer in the offer-set, seeing if it can+-- be completed.  If it can, it calls search on the remaining offer-sets.  If this+-- fails, it reverts to trying the other offers in the list. The map of events passed through+-- relates to the previous things found in the search.+search :: [OfferSet]+          -- ^ The collection of all the related offer-sets+          -> Map.Map Event Bool+          -- ^ This contains the events already decided upon in the search.  If+          -- an event maps to True, it means it was chosen by an earlier part of+          -- the search, and thus future parts of the search /must/ have this event+          -- in the chosen offer (if the process offers it at all -- if it doesn't,+          -- it can be ignored).  If an event maps to False, it was already ruled+          -- out by not being chosen in another part of the search, and it cannot+          -- be chosen by any future parts of the search.  Should be empty when first called from the outside.+          -> Maybe ( [(SignalVar, SignalValue)]+                   , Map.Map Event (RecordedEventType, Set.Set ProcessId)+                   )+             -- ^ The list of tvars involved with the completion and the signal+             -- value for them, and the map with information about the completed events.+search [] _ = Just ([], Map.empty) search (offer@(OfferSet (tv, pid, eventSets)) : offers) eventMap       | Map.null mustChooseFromEventSets = tryAll eventSets       | otherwise = tryAll filteredEventSets@@ -122,16 +178,21 @@                 es             ] +        -- Folds across a map, seeing if the given predicate holds for all values+        -- in the map.         mapdotall :: Ord k => (a -> Bool) -> Map.Map k a -> Bool         mapdotall f = Map.fold (\x b -> f x && b) True -        -- All events in the maps in the first parameter will be mapped to True-        tryAll :: [([Int],Map.Map Event ())] ->-          Maybe (STM (), Map.Map Unique (RecordedEventType, Set.Set ProcessId),-            [(TVar (Maybe [Int]), [Event])])+        and' :: Ord k => Map.Map k Bool -> Bool+        and' = mapdotall id++        tryAll :: [(SignalValue, Map.Map Event ())]+          -> Maybe ( [(SignalVar, SignalValue)]+                   , Map.Map Event (RecordedEventType, Set.Set ProcessId)+                   )         tryAll [] = Nothing         tryAll ((ns, es):next)-          | not $ mapdotall id (eventMap `Map.intersection` es)+          | not $ and' (eventMap `Map.intersection` es)               -- Contains an already-rejected event (one that mapped to False), skip:               -- Need to reject the other events too though -- well, at least put               -- them in the appropriate map and pass them through.  They will@@ -140,10 +201,12 @@               = tryAll next           | otherwise = case search offers eventMap' of             Nothing -> tryAll next-            Just (act, resolved, retract) -> Just (if null ns then act else writeTVar tv (Just ns) >> act, foldl-              (\m e -> Map.insertWith add (getEventUnique e) (getEventType-                e, Set.singleton pid) m) resolved (Map.keys es), if null ns then retract else-                  (tv, Map.keys allEventsInOfferMappedToFalse) : retract)+            Just (act, resolved) -> Just+              (if isNullSignal ns then act else (tv, ns) : act+              , foldl (\m e -> Map.insertWith add e+                                 (getEventType e, Set.singleton pid) m)+                  resolved (Map.keys es)+              )                          where             -- All events that features in other offers by this process, but not@@ -157,8 +220,6 @@              add (tx, pidsx) (_, pidsy) = (tx, pidsx `Set.union` pidsy)             -data EventStatus = Fine | NotCompletable deriving (Eq, Show)- -- Given a list of offers that could possibly complete, check if any set -- of offers can.  If so, complete it (including all retractions and -- notifications for each process), otherwise leave things untouched.@@ -167,26 +228,28 @@ -- list of all offer-sets that need to be considered (they will have come from -- all events in a connected sub-graph), the map of relevant events to their status, -- and returns the map of event-identifiers that did complete.-resolveOffers :: Maybe (TVar (Maybe [Int])) -> [OfferSet] -> Set.Set Event -> STM (Map.Map Unique (RecordedEventType,-  Set.Set ProcessId))+resolveOffers :: Maybe SignalVar -> [OfferSet] -> Set.Set Event+  -> STM (Map.Map Unique (RecordedEventType, Set.Set ProcessId)) resolveOffers newTvid allOffers events   = do let (offers', _) = trim (allOffers, events)-           (act, ret, retract) = fromMaybe (return (), Map.empty, []) $ search (map addNullOffer-             $ sortOffers offers') Map.empty-       act+           (act, ret) = fromMaybe ([], Map.empty) $+             search (map addNullOffer $ sortOffers offers') Map.empty+       eventCounts <- T.sequence $ Map.mapWithKey getAndIncCounter ret+       let NoPoison uniqCounts = T.sequence $ Map.mapKeysMonotonic getEventUnique eventCounts+       mapM_ (\(tv, x) -> writeTVar tv (Just (x, uniqCounts))) act        -- do the retractions for all involved processes once the choice is made:        -- TODO optimise:-       retractOffers $ zip (map fst retract)+       retractOffers $ zip (map fst act)                            (repeat $ unionAll $ map allEventsInOffer allOffers)-       return ret+       return (Map.mapKeysMonotonic getEventUnique ret)   where     -- Don't add the null offer for the newest process, and null offer should be     -- added to the end:     addNullOffer :: OfferSet -> OfferSet     addNullOffer (OfferSet (tv,y,zs)) = OfferSet (tv,y,if Just tv == newTvid then zs else zs++nullOffer) -    nullOffer :: [([Int], Map.Map Event ())]-    nullOffer = [([],Map.empty)]+    nullOffer :: [(SignalValue, Map.Map Event ())]+    nullOffer = [(nullSignalValue,Map.empty)]      -- SMallest offers first to minimise backtracking:     sortOffers :: [OfferSet] -> [OfferSet]@@ -262,7 +325,7 @@           = do x <- readTVar tv                case x of                  PoisonItem -> act >> return PoisonItem-                 NoPoison (count, offers) ->+                 NoPoison (count, _, offers) ->                    let otherEvents = map allEventsInOffer offers in                    if length offers == count                      then -- It could be ready@@ -273,19 +336,29 @@                      else -- No way it could be ready, so ignore it:                        discoverRelatedOffersAll a next --- Given an optional waiting-tvar from the newest process to offer (if any), and--- an event, spiders out, discovers all the offers, then resolves them and returns--- a map containing all the completed events, mapping the identifier to the event--- type and the set of process identifiers that participated in the succesfully--- completed events.  The map will be empty if and only if no events were completed.-discoverAndResolve :: Either OfferSet Event -> STM (WithPoison (Map.Map Unique (RecordedEventType,Set.Set-  ProcessId)))+-- Given an event, spiders out, discovers all the offers, then resolves them+-- and returns a map containing all the completed events, mapping the+-- identifier to the event type and the set of process identifiers that+-- participated in the succesfully completed events.  The map will be empty if+-- and only if no events were completed.+discoverAndResolve :: Either OfferSet Event+                        -- ^ Either an OfferSet to spider out from, or a single+                        -- event.  The latter case is for when we are resigning+                        -- from an event and need to check if that completes anything.+                      -> STM (WithPoison (Map.Map Unique (RecordedEventType, Set.Set ProcessId)))+                        -- ^ Gives back either poison, or a map from event identifiers+                        -- to information about the completed event.  The map is+                        -- empty if no events were completed. discoverAndResolve offOrEvent   = do r <- discoverRelatedOffers $ case offOrEvent of               Left off@(OfferSet (tv, _, nes)) ->                 let retract = retractOffers [(tv, allEventsInOffer off)] in-                      concat [zip (repeat $ retract >> writeTVar tv (Just $ ns-                         ++ [0])) (Map.keys es) | (ns, es) <- nes]+                      concat [zip+                        -- This is the action to execute if an event is found to+                        -- be poisoned:+                        (repeat $ retract >> writeTVar tv (Just (addPoison ns, Map.empty)))+                        (Map.keys es)+                        | (ns, es) <- nes]               Right e -> [(return (), e)]        case r of          PoisonItem -> return PoisonItem@@ -298,7 +371,7 @@ newEvent :: RecordedEventType -> Int -> IO Event newEvent t n   = do u <- newUnique-       atomically $ do tv <- newTVar (NoPoison (n, []))+       atomically $ do tv <- newTVar (NoPoison (n, 0, []))                        return $ Event (u, t, tv)  newEventUnique :: IO Unique@@ -309,30 +382,30 @@   = do x <- readTVar $ getEventTVar e        case x of          PoisonItem -> return PoisonItem-         NoPoison (count, offers) ->-           do writeTVar (getEventTVar e) $ NoPoison (count + 1, offers)+         NoPoison (count, seq, offers) ->+           do writeTVar (getEventTVar e) $ NoPoison (count + 1, seq, offers)               return $ NoPoison ()  -- If the event completes, we return details related to it:-resignEvent :: Event -> STM (WithPoison [((RecordedEventType, Unique), Set.Set-    ProcessId)])+resignEvent :: Event -> STM (WithPoison [((RecordedEventType, Unique), Set.Set ProcessId)]) resignEvent e   = do x <- readTVar $ getEventTVar e        case x of          PoisonItem -> return PoisonItem-         NoPoison (count, offers) ->-           do writeTVar (getEventTVar e) $ NoPoison (count - 1, offers)+         NoPoison (count, seq, offers) ->+           do writeTVar (getEventTVar e) $ NoPoison (count - 1, seq, offers)               if (count - 1 == length offers)                 then liftM (fmap $ \mu -> [((r,u),pids) | (u,(r,pids)) <- Map.toList mu])                        $ discoverAndResolve $ Right e                 else return $ NoPoison []  -- Given the list of identifiers paired with all the events that that process might--- be engaged in, retracts all the offers during a transaction.-retractOffers :: [(TVar (Maybe [Int]), Map.Map Event ())] -> STM ()+-- be engaged in, retracts all the offers that are associated with the given TVar;+-- i.e. the TVar is used as an identifier for the process+retractOffers :: [(SignalVar, Map.Map Event ())] -> STM () retractOffers = mapM_ retractAll   where-    retractAll :: (TVar (Maybe [Int]), Map.Map Event ()) -> STM ()+    retractAll :: (SignalVar, Map.Map Event ()) -> STM ()     retractAll (tvid, evts) = mapM_ retract (Map.keys evts)       where         retract :: Event -> STM ()@@ -340,15 +413,17 @@           = do x <- readTVar $ getEventTVar e                case x of                  PoisonItem -> return ()-                 NoPoison (enrolled, offers) ->+                 NoPoison (enrolled, seq, offers) ->                    let reducedOffers = filter (\(OfferSet (tvx,_,_)) -> tvx /= tvid) offers in-                   writeTVar (getEventTVar e) $ NoPoison (enrolled, reducedOffers)+                   writeTVar (getEventTVar e) $ NoPoison (enrolled, seq, reducedOffers)  -- Simply adds the offers but doesn't check if that will complete an event: -- Returns PoisonItem if any of the events were poisoned makeOffers :: OfferSet -> STM (WithPoison ()) makeOffers offers   = do let allEvents = Map.keys $ allEventsInOffer offers+       -- No need for nub, as having it come from a map guarantees there are no+       -- duplicates in the list of events        liftM mergeWithPoison $ mapM makeOffer allEvents   where     makeOffer :: Event -> STM (WithPoison ())@@ -356,18 +431,38 @@       = do x <- readTVar $ getEventTVar e            case x of              PoisonItem -> return PoisonItem-             NoPoison (count, prevOffers) ->-               do writeTVar (getEventTVar e) $ NoPoison (count, offers : prevOffers)+             NoPoison (count, seq, prevOffers) ->+               do writeTVar (getEventTVar e) $ NoPoison (count, seq, offers : prevOffers)                   return $ NoPoison () --- Passed: True if allowed to commit to waiting--- Returns: True if committed, False otherwise-enableEvents :: TVar (Maybe [Int]) -> ProcessId -> [([Int], STM-  (), [Event])] -> Bool -> STM (Maybe ([Int], [((RecordedEventType, Unique), Set.Set-    ProcessId)]))+-- Returns Nothing if no events were ready.  Returns Just with the signal value+-- if an event was immediately available, followed by the information for each+-- event involved in the synchronisation.  If poison was encounted, this list will+-- be empty.+enableEvents :: SignalVar+                  -- ^ Variable used to signal the process once a choice is made+                -> ProcessId+                  -- ^ The id of the process making the choice+                -> [(SignalValue, STM (), [Event])]+                  -- ^ The list of options.  Each option has a signalvalue to return+                  -- if chosen, an STM action to execute at the same time as the+                  -- synchronisation, and a list of events (conjoined together).+                  --  So this list is the disjunction of conjunctions, with a little+                  -- more information.+                -> Bool+                  -- ^ True if it can commit to waiting.  If there is an event+                  -- combination ready during the transaction, it will chosen regardless+                  -- of the value of this flag.  However, if there no events ready,+                  -- passing True will leave the offers there, but False will retract+                  -- the offers.+                -> STM (Maybe ((SignalValue, Map.Map Unique Integer), [((RecordedEventType, Unique), Set.Set ProcessId)])) enableEvents tvNotify pid events canCommitToWait   = do let offer = OfferSet (tvNotify, pid, [(nid, Map.fromList (zip es (repeat ()))) | (nid, _, es) <- events])+       -- First add our offer to all the events:+       -- We don't check the result for poison, as discoverAndResolve will find+       -- it anyway        makeOffers offer+       -- Then spider out and see if anything can be resolved:        pmu <- discoverAndResolve (Left offer)        case (canCommitToWait, pmu) of          (_, PoisonItem) -> do Just chosen <- readTVar tvNotify@@ -385,7 +480,12 @@                    Just chosen <- readTVar tvNotify                    return $ Just (chosen, [((r,u),pids) | (u,(r,pids)) <- Map.toList mu]) -disableEvents :: TVar (Maybe [Int]) -> [Event] -> STM (Maybe [Int])+-- | Given the variable used to signal the process, and the list of events that+-- were involved in its offers, attempts to disable the events.  If the variable+-- has been signalled (i.e. has a Just value), that is returned and nothing is done, if the variable+-- has not been signalled (i.e. is Nothing), the events are disabled and Nothing+-- is returned.+disableEvents :: SignalVar -> [Event] -> STM (Maybe (SignalValue, Map.Map Unique Integer)) disableEvents tv events   = do x <- readTVar tv        -- Since the transaction will be atomic, we know@@ -406,16 +506,16 @@   = do x <- readTVar $ getEventTVar e        case x of          PoisonItem -> return ()-         NoPoison (_, offers) ->+         NoPoison (_, _, offers) ->            do retractOffers [(tvw, unionAll $ map snd events)                             | OfferSet (tvw, _, events) <- offers]-              sequence_ [writeTVar tvw (Just $ pickInts events ++ [0])+              sequence_ [writeTVar tvw (Just (addPoison $ pickInts events, Map.empty))                         | OfferSet (tvw, _, events) <- offers]               writeTVar (getEventTVar e) PoisonItem   where-    pickInts :: [([Int], Map.Map Event ())] -> [Int]+    pickInts :: [(SignalValue, Map.Map Event ())] -> SignalValue     pickInts es = case filter ((e `Map.member`) . snd) es of-      [] -> [] -- Should never happen+      [] -> nullSignalValue -- Should never happen       ((ns,_):_) -> ns  --TODO document how if it's poisoned, 0 will be appended to the list@@ -568,7 +668,7 @@                         " expected no poison but found it"                       (PoisonItem, NoPoison _) -> putStrLn $ testName ++                         " expected poison but found none"-                      (NoPoison expOff, NoPoison (_, actOff)) ->+                      (NoPoison expOff, NoPoison (_, _, actOff)) ->                         when (map (realOffers !!) expOff **/=** actOff) $                           putStrLn $ testName ++ " offers did not match"                 | (n, (_, expect)) <- zip [0..] eventCounts]@@ -595,7 +695,7 @@            realOffers <- sequence              [ do tv <- atomically $ newTVar Nothing                   let pid = testProcessId processN-                      offSub = [ ([processN, offerN],+                      offSub = [ (Signal $ NoPoison (processN + offerN),                                   Map.fromList [ (events !! indivEvent, ())                                   | indivEvent <- singleOffer])                                | (offerN, singleOffer) <- zip [0..] processOffers]@@ -603,12 +703,12 @@                   mapM_ (\e -> atomically $ do                     x <- readTVar (getEventTVar e)                     case x of-                      NoPoison (count, offs) ->-                        writeTVar (getEventTVar e) $ NoPoison (count, off : offs)+                      NoPoison (count, s, offs) ->+                        writeTVar (getEventTVar e) $ NoPoison (count, s, off : offs)                       PoisonItem -> return ()                     ) (Map.keys $ unionAll $ map snd offSub)                   return off-             | (processN, processOffers) <- zip [0..] offerSets]+             | (processN, processOffers) <- zip (map (*1000) [0..]) offerSets]            return (events, realOffers)  testResolve :: IO ()@@ -673,7 +773,7 @@            actualResult <- atomically $ discoverAndResolve $ Left $ head realOffers            let expectedResult = if poisoned then PoisonItem else NoPoison $                                 Map.fromList [ (getEventUnique e, (ChannelComm,-                                               Set.fromList $ map (testProcessId . fst) is))+                                               Set.fromList $ map (testProcessId . (*1000) . fst) is))                                              | (e, Left is) <- zip events (map snd eventCounts)]            when (expectedResult /= actualResult) $              putStrLn $ testName ++ " failed on direct result, expected: "@@ -684,9 +784,10 @@                do x <- atomically $ readTVar tv                   case x of                     Nothing -> putStrLn $ "Unexpected no-win for " ++ show (pn,en)-                    Just v -> when (v /= (if poisoned then (++[0]) else id) [pn, en]) $-                      putStrLn $ testName ++ " wrong choice: " ++ show v ++ " exp: " ++ show-                        [pn, en]+                    Just v -> when (fst v /= (if poisoned then addPoison else id)+                                             (Signal $ NoPoison ((pn*1000)+en))) $+                      putStrLn $ testName ++ " wrong choice: " ++ " exp: " ++ show+                        (pn+en)                   return pn              ) $ map snd eventCounts            -- test the others are unchanged@@ -695,7 +796,7 @@                             case x of                               Nothing -> return ()                               Just _ -> putStrLn $ testName ++ " Unexpected win for process: " ++-                                show n ++ " " ++ show x+                                show n                      | n <- [0 .. length offerSets - 1] \\ allFired]            -- check events are blanked afterwards:            sequence_ [ let e = events !! n@@ -703,10 +804,12 @@                              Left _ -> []                              Right ns -> map (realOffers !!) ns in do                          x <- atomically $ readTVar $ getEventTVar e-                         when (x /= NoPoison (count, expVal)) $-                           putStrLn $ testName ++ "Event " ++ show n +++                         case x of+                           NoPoison (c, _, e') | c == count && e' == expVal -> return ()+                           _ ->+                             putStrLn $ testName ++ "Event " ++ show n ++                              " not as expected after, exp: " ++ show (length expVal)-                             ++ " act: " ++ (let NoPoison (_,act) = x in show (length act))+                             ++ " act: " ++ (let NoPoison (_,_,act) = x in show (length act))                      | (n,(NoPoison count, st)) <- zip [0..] eventCounts]      showStuff = show . fmap (map (\(u,x) -> (hashUnique u, x)) . Map.toList)
Control/Concurrent/CHP/Guard.hs view
@@ -31,30 +31,37 @@  import Control.Concurrent.STM import Control.Monad.Trans+import Data.Unique import System.IO  import Control.Concurrent.CHP.Event import Control.Concurrent.CHP.Traces.Base  -- Setup (giving transaction)-data Guard = TimeoutGuard (IO (STM [Int]))-             | SkipGuard [Int]+data Guard = TimeoutGuard (IO (STM ()))+             | SkipGuard              | StopGuard-             | BadGuard String              -- The STM item is an action to take in the same transaction as              -- completing the event (before it is completed).-             | EventGuard [Int] [RecordedIndivEvent] (STM ()) [Event]-             | NestedGuards [Guard]+             | EventGuard ((Unique -> Integer) -> [RecordedIndivEvent Unique]) (STM ()) [Event]  skipGuard :: Guard-skipGuard = SkipGuard []+skipGuard = SkipGuard -badGuard :: String -> Guard-badGuard = BadGuard+isSkipGuard :: Guard -> Bool+isSkipGuard SkipGuard = True+isSkipGuard _ = False +badGuard :: String -> Either String a+badGuard = Left+ stopGuard :: Guard stopGuard = StopGuard +isStopGuard :: Guard -> Bool+isStopGuard StopGuard = True+isStopGuard _ = False+ -- Microseconds guardWaitFor :: Int -> Guard guardWaitFor n@@ -63,6 +70,6 @@         return $ do b <- readTVar signalDone                     if b == False                       then retry-                      else return []+                      else return ()  
Control/Concurrent/CHP/Monad.hs view
@@ -32,6 +32,7 @@   (    -- * CHP Monad   CHP, MonadCHP(..), runCHP, runCHP_,+  embedCHP, embedCHP_, embedCHP1, embedCHP1_,    onPoisonTrap, onPoisonRethrow, throwPoison, Poisonable(..), poisonAll, @@ -43,9 +44,9 @@    ) where  import Control.Concurrent-import Control.Monad.Error import Control.Monad.State import Control.Monad.Trans+import Data.Unique  -- This module primarily re-exports the public definitions from -- Control.Concurrent.CHP.{Base,CSP,Poison}:@@ -59,12 +60,39 @@ -- communicate between those processes using channels.  Instead, run this function -- once and use it to spawn off the parallel processes that you need. runCHP :: CHP a -> IO (Maybe a)-runCHP = liftM fst . (runCHPAndTrace :: CHP a -> IO (Maybe a, TraceOff))+runCHP = liftM fst . (runCHPAndTrace :: CHP a -> IO (Maybe a, TraceOff Unique))  -- | Runs a CHP program.  Like 'runCHP' but discards the output. runCHP_ :: CHP a -> IO () runCHP_ p = runCHP p >> return () +-- | Allows embedding of the CHP monad back into the IO monad.  The argument+-- that this function takes is a CHP action (with arbitrary behaviour).  The+-- function is monadic, and returns something of type: IO a.  This+-- is an IO action that you can now use in the IO monad wherever you like.+-- What it returns is the result of your original action.+--+-- This function is intended for use wherever you need to supply an IO callback+-- (for example with the OpenGL libraries) that needs to perform CHP communications.+--  It is the safe way to do things, rather than using runCHP twice (and also works+-- with CSP and VCR traces -- but not structural traces!).+embedCHP :: CHP a -> CHP (IO (Maybe a))+embedCHP m = liftM ($ ()) $ embedCHP1 (\() -> m)++-- | A convenient version of embedCHP that ignores the result+embedCHP_ :: CHP a -> CHP (IO ())+embedCHP_ = liftM (>> return ()) . embedCHP++-- | A helper like embedCHP for callbacks that take an argument+embedCHP1 :: (a -> CHP b) -> CHP (a -> IO (Maybe b))+embedCHP1 f = do t <- liftPoison $ liftTrace get+                 return $ liftM fst . runCHPProgramWith t (const ()) . f++-- | A convenient version of embedCHP1 that ignores the result+embedCHP1_ :: (a -> CHP b) -> CHP (a -> IO ())+embedCHP1_ = liftM (\m x -> m x >> return ()) . embedCHP1++ -- | A monad transformer for easier looping.  This is independent of the -- CHP aspects, but has all the right type-classes defined for it to make -- it easy to use with the CHP library.@@ -88,12 +116,6 @@ instance MonadCHP m => MonadCHP (LoopWhileT m) where   liftCHP = lift . liftCHP -instance MonadError e m => MonadError e (LoopWhileT m) where-  throwError e = lift $ throwError e-  catchError m h = LWT $ catchError (getLoop m) (getLoop . h)----TODO instances for all the other monad transformers- -- | Runs the given action in a loop, executing it repeatedly until a 'while' -- statement inside it has a False condition.  If you use 'loop' without 'while', -- the effect is the same as 'forever'.@@ -126,7 +148,7 @@ -- /NOTE:/ If you wish to use this as part of a choice, you must use @-threaded@ -- as a GHC compilation option (at least under 6.8.2). waitFor :: Int -> CHP ()-waitFor n = liftPoison $ AltableT (guardWaitFor n, return ()) (liftIO $ threadDelay n)+waitFor n = liftPoison $ AltableT (Right [(guardWaitFor n, return ())]) (liftIO $ threadDelay n) -- TODO maybe fix the above lack of guarantees by keeping timeout guards explicit.  -- TODO add waitUntil@@ -135,14 +157,14 @@ -- -- Suitable for use in an 'Control.Concurrent.CHP.Alt.alt'. skip :: CHP ()-skip = liftPoison $ AltableT (skipGuard, return ()) (return ())+skip = liftPoison $ AltableT (Right [(skipGuard, return ())]) (return ())  -- | The stop guard.  Its main use is that it is never ready in a choice, so -- can be used to mask out guards.  If you actually execute 'stop', that process -- will do nothing more.  Any parent process waiting for it to complete will -- wait forever. stop :: CHP ()-stop = liftPoison $ AltableT (stopGuard, liftIO hang) (liftIO hang)+stop = liftPoison $ AltableT (Right [(stopGuard, liftIO hang)]) (liftIO hang)   where     -- Strangely, I can't work out a good way to actually implement stop.     -- If you wait on a variable that will never be ready, GHC will wake
Control/Concurrent/CHP/Parallel.hs view
@@ -117,16 +117,16 @@ runParallelPoison :: forall a. [CHP a] -> CHP [a] runParallelPoison processes   = do c <- liftIO $ atomically $ newResultsVar-       (_, trace) <- PoisonT $ lift $ liftTrace get+       trace <- PoisonT $ lift $ liftTrace get        blanks <- liftIO $ blankTraces trace (length processes)        liftIO $ -         mapM_ forkIO [do y <- wrapProcess p $ flip runStateT ([], btr) . pullOutStandard+         mapM_ forkIO [do y <- wrapProcess p $ flip runStateT btr . pullOutStandard                           C.block $ atomically $                             do ys <- readTVar c                                writeTVar c $ (case y of                                  Nothing -> (n, (Nothing, Nothing))-                                 Just (Right (x,(_,t))) -> (n, (Just x, Just t))-                                 Just (Left (_,t)) -> (n, (Nothing, Just t))+                                 Just (Right (x,t)) -> (n, (Just x, Just t))+                                 Just (Left t) -> (n, (Nothing, Just t))                                  ) : ys                       | (p, btr, n) <- zip3 processes blanks [0..]]        results <- liftIO $ atomically $ do xs <- readTVar c@@ -178,10 +178,10 @@               liftIO $ atomically $ do                 (pa, n) <- readTVar b                 writeTVar b (pa, n + 1)-              (_, trace) <- liftCHP $ PoisonT $ lift $ liftTrace get+              trace <- liftCHP $ PoisonT $ lift $ liftTrace get               [blank] <- liftIO $ blankTraces trace 1               liftIO $ forkIO $ do-                r <- wrapProcess p $ flip runStateT ([], blank) . pullOutStandard+                r <- wrapProcess p $ flip runStateT blank . pullOutStandard                 C.block $ atomically $ do                   (poisonedAlready, n) <- readTVar b                   writeTVar b $ (poisonedAlready || isNothing r, n - 1)
Control/Concurrent/CHP/Traces.hs view
@@ -66,6 +66,8 @@   ,ChannelLabels   ,RecordedEventType(..)   ,RecordedIndivEvent(..)+  ,recordedIndivEventLabel+  ,recordedIndivEventSeq   ,Trace(..)   ) where 
Control/Concurrent/CHP/Traces/Base.hs view
@@ -45,7 +45,9 @@ -- identifiers are per channel\/barrier, not per event.  Currently, -- channels and barriers can never have the same Unique as each other, but -- do not rely on this behaviour.-type RecordedEvent = (RecordedEventType, Unique)+--+-- The type became parameterised in version 1.3.0.+type RecordedEvent u = (RecordedEventType, u)  -- | An individual record of an event, found in nested traces.  Either a -- channel write or read, or a barrier synchronisation, each with a unique@@ -56,17 +58,41 @@ -- never have the same Unique as each other, but do not rely on this -- behaviour. ----- TimerSyncIndiv was added in version 1.2.0.-data RecordedIndivEvent = -  ChannelWrite Unique-  | ChannelRead Unique-  | BarrierSyncIndiv Unique-  | ClockSyncIndiv Unique String-  deriving (Eq, Ord)+-- ClockSyncIndiv was added in version 1.2.0.+--+-- The type became parameterised, and the Show and Read instances were added in version 1.3.0.+data RecordedIndivEvent u = +  ChannelWrite u Integer+  | ChannelRead u Integer+  | BarrierSyncIndiv u Integer+  | ClockSyncIndiv u Integer String+  deriving (Eq, Ord, Read, Show) -type RecEvents = ([RecordedEvent], [RecordedIndivEvent])+-- | Added in version 1.3.0.+recordedIndivEventLabel :: RecordedIndivEvent u -> u+recordedIndivEventLabel (ChannelWrite x _) = x+recordedIndivEventLabel (ChannelRead x _) = x+recordedIndivEventLabel (BarrierSyncIndiv x _) = x+recordedIndivEventLabel (ClockSyncIndiv x _ _) = x -getName :: String -> Unique -> State (Map.Map Unique String) String+-- | Added in version 1.3.0.+recordedIndivEventSeq :: RecordedIndivEvent u -> Integer+recordedIndivEventSeq (ChannelWrite _ n) = n+recordedIndivEventSeq (ChannelRead _ n) = n+recordedIndivEventSeq (BarrierSyncIndiv _ n) = n+recordedIndivEventSeq (ClockSyncIndiv _ n _) = n++indivRec :: (u -> Integer -> RecordedIndivEvent u)+            -> u -> (u -> Integer) -> (RecordedIndivEvent u)+indivRec r u f = r u (f u)++indivRecJust :: (u -> Integer -> RecordedIndivEvent u)+                -> u -> (u -> Integer) -> Maybe (RecordedIndivEvent u)+indivRecJust r u f = Just $ indivRec r u f++type RecEvents = ([RecordedEvent Unique], [RecordedIndivEvent Unique])++getName :: Ord u => String -> u -> State (Map.Map u String) String getName prefix u           = do m <- get                case Map.lookup u m of@@ -75,7 +101,7 @@                             do put $ Map.insert u x m                                return x -nameEvent :: RecordedEvent -> State (Map.Map Unique String) String+nameEvent :: Ord u => RecordedEvent u -> State (Map.Map u String) String nameEvent (t, c) = liftM (++ suffix) $ getName prefix c   where     (prefix, suffix) = case t of@@ -83,39 +109,54 @@       BarrierSync -> ("_b","")       ClockSync st -> ("_t", ':' : st) -nameIndivEvent :: RecordedIndivEvent -> State (Map.Map Unique String) String-nameIndivEvent (ChannelWrite c) = do c' <- getName "_c" c-                                     return $ c' ++ "!"-nameIndivEvent (ChannelRead c) = do c' <- getName "_c" c-                                    return $ c' ++ "?"-nameIndivEvent (BarrierSyncIndiv c) = do c' <- getName "_b" c-                                         return $ c' ++ "*"-nameIndivEvent (ClockSyncIndiv c t) = do c' <- getName "_t" c-                                         return $ c' ++ ":" ++ t+nameEvent' :: Ord u => RecordedEvent u -> State (Map.Map u String) (RecordedEvent String)+nameEvent' (t, c) = do c' <- getName prefix c+                       return (t, c' ++ suffix)+  where+    (prefix, suffix) = case t of+      ChannelComm -> ("_c","")+      BarrierSync -> ("_b","")+      ClockSync st -> ("_t", ':' : st)  -ensureAllNamed :: Map.Map Unique String -> [RecordedEvent] -> Map.Map Unique String--- Quite hacky:-ensureAllNamed m es = execState (mapM_ nameEvent es) m+nameIndivEvent :: Ord u => RecordedIndivEvent u -> State (Map.Map u String) String+nameIndivEvent (ChannelWrite c n) = do c' <- getName "_c" c+                                       return $ c' ++ "![" ++ show n ++ "]"+nameIndivEvent (ChannelRead c n) = do c' <- getName "_c" c+                                      return $ c' ++ "?[" ++ show n ++ "]"+nameIndivEvent (BarrierSyncIndiv c n) = do c' <- getName "_b" c+                                           return $ c' ++ "[" ++ show n ++ "]"+nameIndivEvent (ClockSyncIndiv c n t) = do c' <- getName "_t" c+                                           return $ c' ++ ":" ++ t+                                             ++ "[" ++ show n ++ "]" -ensureAllNamedIndiv :: Map.Map Unique String -> [RecordedIndivEvent] -> Map.Map Unique String--- Quite hacky:-ensureAllNamedIndiv m es = execState (mapM_ nameIndivEvent es) m+nameIndivEvent' :: Ord u => RecordedIndivEvent u -> State (Map.Map u String) (RecordedIndivEvent String)+nameIndivEvent' (ChannelWrite c n) = do c' <- getName "_c" c+                                        return $ ChannelWrite c' n+nameIndivEvent' (ChannelRead c n) = do c' <- getName "_c" c+                                       return $ ChannelRead c' n+nameIndivEvent' (BarrierSyncIndiv c n) = do c' <- getName "_b" c+                                            return $ BarrierSyncIndiv c' n+nameIndivEvent' (ClockSyncIndiv c n t) = do c' <- getName "_t" c+                                            return $ ClockSyncIndiv c' n t  --- The list of integers is for alting-type TraceT = StateT ([Int], TraceStore)+type TraceT = StateT TraceStore  data TraceStore =   NoTrace-  | Trace (ProcessId, TVar ChannelLabels, SubTraceStore)+  | Trace (ProcessId, TVar (ChannelLabels Unique), SubTraceStore) -type ChannelLabels = Map.Map Unique String+mapSubTrace :: (SubTraceStore -> SubTraceStore) -> TraceStore -> TraceStore+mapSubTrace _ NoTrace = NoTrace+mapSubTrace f (Trace (pid, tv, s)) = Trace (pid, tv, f s) +type ChannelLabels u = Map.Map u String+ data SubTraceStore =-  Hierarchy (Structured RecordedIndivEvent)-  | CSPTraceRev (TVar [(Int, [RecordedEvent])])-  | VCRTraceRev (TVar [Set.Set (Set.Set ProcessId, RecordedEvent)])+  Hierarchy (Structured (RecordedIndivEvent Unique))+  | CSPTraceRev (TVar [(Int, [RecordedEvent Unique])])+  | VCRTraceRev (TVar [Set.Set (Set.Set ProcessId, RecordedEvent Unique)])  data Ord a => Structured a =   StrEvent a@@ -124,7 +165,7 @@   deriving (Eq, Ord)  -- | Records an event where you were the last person to engage in the event-recordEventLast :: [(RecordedEvent, Set.Set ProcessId)] -> TraceStore -> STM ()+recordEventLast :: [(RecordedEvent Unique, Set.Set ProcessId)] -> TraceStore -> STM () recordEventLast news y            =    case y of                   Trace (_,_,CSPTraceRev tv) ->@@ -145,25 +186,17 @@                   _ -> return ()  -- | Records an event where you were one of the people involved-recordEvent :: [RecordedIndivEvent] -> TraceT IO ()-recordEvent e-           = do (x,y) <- get-                case (x, y) of-                  (as, Trace (pid,tvls,Hierarchy es)) ->-                       put (as, Trace (pid, tvls, Hierarchy (foldl (flip addSeqEventH) es e)))-                  _ -> return ()+recordEvent :: [RecordedIndivEvent Unique] -> TraceT IO ()+recordEvent e = modify $ mapSubTrace $ \(Hierarchy es) ->+                  Hierarchy (addParEventsH (map StrEvent e) es)  mergeSubProcessTraces :: [TraceStore] -> TraceT IO () mergeSubProcessTraces ts-  = do s <- get-       case s of-         (as, Trace (pid, tvls, Hierarchy es)) ->-           put (as, Trace (pid, tvls, Hierarchy $ addParEventsH ts' es))-           where ts' = [t | Trace (_,_,Hierarchy t) <- ts]-         _ -> return ()-+  = modify $ mapSubTrace $ \(Hierarchy es) -> Hierarchy (addParEventsH ts' es)+  where+    ts' = [t | Trace (_,_,Hierarchy t) <- ts] -shouldMakeNewSetVCR :: Set.Set ProcessId -> Set.Set (Set.Set ProcessId, RecordedEvent)+shouldMakeNewSetVCR :: Set.Set ProcessId -> Set.Set (Set.Set ProcessId, RecordedEvent Unique)   -> Bool shouldMakeNewSetVCR newIds existingSet   = exists existingSet $ \(bigP,_) -> exists bigP $ \p -> exists newIds $ \q ->@@ -222,18 +255,18 @@ addRLE x nes = (1,[x]):nes  -labelEvent :: Event -> String -> StateT (a, TraceStore) IO ()+labelEvent :: Event -> String -> StateT TraceStore IO () labelEvent e l   = labelUnique (getEventUnique e) l -labelUnique :: Unique -> String -> StateT (a, TraceStore) IO ()+labelUnique :: Unique -> String -> StateT TraceStore IO () labelUnique u l-  = do (_,t) <- get+  = do t <- get        case t of          NoTrace -> return ()          Trace (_,tvls,_) -> add tvls   where-    add :: TVar (Map.Map Unique String) -> StateT (a, TraceStore) IO ()+    add :: TVar (Map.Map Unique String) -> StateT TraceStore IO ()     add tv = liftIO $ atomically $ do       m <- readTVar tv       writeTVar tv $ Map.insert u l m
Control/Concurrent/CHP/Traces/CSP.hs view
@@ -34,6 +34,7 @@ import Control.Concurrent.STM import Control.Monad.State import qualified Data.Map as Map+import Data.Unique import Text.PrettyPrint.HughesPJ  import Control.Concurrent.CHP.Base@@ -41,9 +42,9 @@  -- | A classic CSP trace.  It is simply the channel labels, and a list of recorded -- events in sequence -- the head of the list is the first (oldest) event.-newtype CSPTrace = CSPTrace (ChannelLabels, [RecordedEvent])+newtype CSPTrace u = CSPTrace (ChannelLabels u, [RecordedEvent u]) -instance Show CSPTrace where+instance Ord u => Show (CSPTrace u) where   show = renderStyle (Style OneLineMode 1 1) . prettyPrint  instance Trace CSPTrace where@@ -52,21 +53,18 @@                         runCHPProgramWith' (CSPTraceRev tv) toPublic p    prettyPrint (CSPTrace (labels, events))-    = char '<' <+> (sep $ punctuate (char ',') $ map (text . nameCSP labels') events) <+> char '>'-    where-      labels' = ensureAllNamed labels events      +    = char '<' <+> (sep $ punctuate (char ',') $ evalState (mapM (liftM text . nameEvent) events) labels) <+> char '>' -toPublic :: ChannelLabels -> SubTraceStore -> IO CSPTrace+  labelAll (CSPTrace (labels, events))+    = CSPTrace (Map.empty, evalState (mapM nameEvent' events) labels)++toPublic :: ChannelLabels Unique -> SubTraceStore -> IO (CSPTrace Unique) toPublic l (CSPTraceRev tv)   = do list <- atomically $ readTVar tv        return $ CSPTrace (l, concatMap (\(n,es) -> concat $ replicate n $ reverse es) $ reverse list) toPublic _ _ = error "Error in CSP trace -- tracing type got switched" -nameCSP :: ChannelLabels -> RecordedEvent -> String-nameCSP m = flip evalState m . nameEvent---runCHP_CSPTrace :: CHP a -> IO (Maybe a, CSPTrace)+runCHP_CSPTrace :: CHP a -> IO (Maybe a, CSPTrace Unique) runCHP_CSPTrace = runCHPAndTrace  runCHP_CSPTraceAndPrint :: CHP a -> IO ()
Control/Concurrent/CHP/Traces/Structural.hs view
@@ -37,10 +37,14 @@ module Control.Concurrent.CHP.Traces.Structural (StructuralTrace(..), EventHierarchy(..), runCHP_StructuralTrace, runCHP_StructuralTraceAndPrint,   getAllEventsInHierarchy) where +import Control.Applicative hiding (empty) import Control.Monad.State+import qualified Data.Foldable as F import Data.List import qualified Data.Map as Map import Data.Maybe+import qualified Data.Traversable as T+import Data.Unique import Text.PrettyPrint.HughesPJ  import Control.Concurrent.CHP.Base@@ -48,16 +52,29 @@  -- | A data type representing a hierarchy of events.  The count on the StructuralSequence -- count is a replicator count for that list of sequential items.+--+-- The Show, Read, Foldable and Traversable instances were added in version 1.3.0. data EventHierarchy a =   SingleEvent a   | StructuralSequence Int [EventHierarchy a]   | StructuralParallel [EventHierarchy a]+  deriving (Show, Read)  instance Functor EventHierarchy where   fmap f (SingleEvent x) = SingleEvent $ f x   fmap f (StructuralSequence n es) = StructuralSequence n $ map (fmap f) es   fmap f (StructuralParallel es) = StructuralParallel $ map (fmap f) es +instance F.Foldable EventHierarchy where+  foldr f y (SingleEvent x) = f x y+  foldr f y (StructuralSequence _ es) = F.foldr (flip $ F.foldr f) y es+  foldr f y (StructuralParallel es) = F.foldr (flip $ F.foldr f) y es++instance T.Traversable EventHierarchy where+  traverse f (SingleEvent x) = SingleEvent <$> f x+  traverse f (StructuralSequence n es) = StructuralSequence n <$> T.traverse (T.traverse f) es+  traverse f (StructuralParallel es) = StructuralParallel <$> T.traverse (T.traverse f) es+ -- | Flattens the events into a list.  The resulting list may contain duplicates, and it -- should not be assumed that the order relates in any way to the original -- hierarchy.@@ -69,9 +86,9 @@  -- | A nested (or hierarchical) trace.  The trace is an event hierarchy, wrapped -- in a Maybe type to allow for representation of the empty trace (Nothing).-newtype StructuralTrace = StructuralTrace (ChannelLabels, Maybe (EventHierarchy RecordedIndivEvent))+newtype StructuralTrace u = StructuralTrace (ChannelLabels u, Maybe (EventHierarchy (RecordedIndivEvent u))) -instance Show StructuralTrace where+instance Ord u => Show (StructuralTrace u) where   show = renderStyle (Style OneLineMode 1 1) . prettyPrint  instance Trace StructuralTrace where@@ -80,11 +97,8 @@    prettyPrint (StructuralTrace (_,Nothing)) = empty   prettyPrint (StructuralTrace (labels, Just h))-    = pp $ fmap (flip evalState labels' . nameIndivEvent) h+    = pp $ evalState (T.mapM nameIndivEvent h) labels     where-      labels' = ensureAllNamedIndiv labels $-        getAllEventsInHierarchy h-        pp :: EventHierarchy String -> Doc       pp (SingleEvent x) = text x       pp (StructuralSequence 1 es)@@ -95,7 +109,11 @@       pp (StructuralParallel es)         = parens $ sep $ intersperse (text "||") $ map pp es -toPublic :: ChannelLabels -> SubTraceStore -> IO StructuralTrace+  labelAll (StructuralTrace (_, Nothing)) = StructuralTrace (Map.empty, Nothing)+  labelAll (StructuralTrace (labels, Just h))+    = StructuralTrace (Map.empty, Just $ evalState (T.mapM nameIndivEvent' h) labels)++toPublic :: ChannelLabels Unique -> SubTraceStore -> IO (StructuralTrace Unique) toPublic l (Hierarchy h)   = return $ StructuralTrace (l, conv h)   where@@ -119,7 +137,7 @@           reverse s) rev toPublic _ _ = error "Error in Structural trace -- tracing type got switched" -runCHP_StructuralTrace :: CHP a -> IO (Maybe a, StructuralTrace)+runCHP_StructuralTrace :: CHP a -> IO (Maybe a, StructuralTrace Unique) runCHP_StructuralTrace = runCHPAndTrace  runCHP_StructuralTraceAndPrint :: CHP a -> IO ()
Control/Concurrent/CHP/Traces/TraceOff.hs view
@@ -38,14 +38,15 @@ import Text.PrettyPrint.HughesPJ  -- | A trace type that does not record anything.-newtype TraceOff = TraceOff ()+newtype TraceOff a = TraceOff () -instance Show TraceOff where+instance Show (TraceOff a) where   show = const ""  instance Trace TraceOff where   runCHPAndTrace = runCHPProgramWith NoTrace (const $ TraceOff ())   emptyTrace = TraceOff ()   prettyPrint = const empty+  labelAll = const emptyTrace  
Control/Concurrent/CHP/Traces/VCR.hs view
@@ -37,6 +37,7 @@ import Control.Monad.State import qualified Data.Map as Map import qualified Data.Set as Set+import Data.Unique import Text.PrettyPrint.HughesPJ  import Control.Concurrent.CHP.Base@@ -46,9 +47,11 @@ -- accompanied by a sequential list of sets of recorded events.  Each of -- the sets is a set of independent events.  The set at the head of the -- list is the first-recorded (oldest).-newtype VCRTrace = VCRTrace (ChannelLabels, [Set.Set RecordedEvent])+--+-- The type became parameterised in version 1.3.0+newtype VCRTrace u = VCRTrace (ChannelLabels u, [Set.Set (RecordedEvent u)]) -instance Show VCRTrace where+instance Ord u => Show (VCRTrace u) where   show = renderStyle (Style OneLineMode 1 1) . prettyPrint  instance Trace VCRTrace where@@ -59,24 +62,28 @@   prettyPrint (VCRTrace (labels, eventSets))     = char '<' <+> (sep $ punctuate (char ',') $ map (braces . sep . punctuate (char ',')) ropes) <+> char '>'     where-      labels' = ensureAllNamed labels (concatMap Set.toList eventSets)-      es = map (nameVCR labels') eventSets+      es = evalState (mapM nameVCR eventSets) labels        ropes :: [[Doc]]       ropes = map (map text . Set.toList) es +  labelAll (VCRTrace (labels, eventSets))+    = VCRTrace (Map.empty, evalState (mapM nameVCR' eventSets) labels) -toPublic :: ChannelLabels -> SubTraceStore -> IO VCRTrace+toPublic :: ChannelLabels Unique -> SubTraceStore -> IO (VCRTrace Unique) toPublic l (VCRTraceRev tv)   = do setList <- atomically $ readTVar tv        return $ VCRTrace (l, map (Set.map snd) $ reverse setList) toPublic _ _ = error "Error in VCR trace -- tracing type got switched" -nameVCR :: ChannelLabels -> Set.Set RecordedEvent -> Set.Set String-nameVCR m x = Set.fromList $ evalState (mapM nameEvent $ Set.toList x) m+nameVCR :: Ord u => Set.Set (RecordedEvent u) -> State (ChannelLabels u) (Set.Set String)+nameVCR = liftM Set.fromList . mapM nameEvent . Set.toList +nameVCR' :: Ord u => Set.Set (RecordedEvent u) -> State (ChannelLabels u) (Set.Set (RecordedEvent String))+nameVCR' = liftM Set.fromList . mapM nameEvent' . Set.toList -runCHP_VCRTrace :: CHP a -> IO (Maybe a, VCRTrace)++runCHP_VCRTrace :: CHP a -> IO (Maybe a, VCRTrace Unique) runCHP_VCRTrace = runCHPAndTrace  runCHP_VCRTraceAndPrint :: CHP a -> IO ()
chp.cabal view
@@ -1,5 +1,5 @@ Name:            chp-Version:         1.2.0+Version:         1.3.0 Synopsis:        An implementation of concurrency ideas from Communicating Sequential Processes License:         BSD3 License-file:    LICENSE@@ -18,7 +18,7 @@ Category:        Concurrency  Build-Type:      Simple-Build-Depends:   base, containers, mtl, parallel, pretty, stm+Build-Depends:   base >= 3 && < 5, containers, mtl, parallel, pretty, stm  Exposed-modules: Control.Concurrent.CHP                  Control.Concurrent.CHP.Actions@@ -51,7 +51,7 @@                  Control.Concurrent.CHP.Traces.Base  Extensions:      ScopedTypeVariables MultiParamTypeClasses-                 FlexibleInstances UndecidableInstances+                 FlexibleInstances                  GeneralizedNewtypeDeriving CPP  GHC-Options:     -Wall -threaded