-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | An implementation of concurrency ideas from Communicating Sequential Processes -- -- The Communicating Haskell Processes (CHP) library is an implementation -- of ideas from Hoare's Communicating Sequential Processes. More details -- and a tutorial can be found at its homepage: -- http://www.cs.kent.ac.uk/projects/ofa/chp/ The library requires -- at least GHC 6.8.1. @package chp @version 1.3.2 -- | A module for recording View Centric Reasoning (VCR) traces. A view -- centric reasnoning trace is a list of sets of events. Each set -- contains independent events that have no causal relationship between -- them. Hopefully we will publish a paper explaining all this in detail -- soon. module Control.Concurrent.CHP.Traces.VCR -- | A VCR (View-Centric Reasoning) trace. It is the channel labels, -- 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). -- -- The type became parameterised in version 1.3.0 newtype VCRTrace u VCRTrace :: (ChannelLabels u, [Set (RecordedEvent u)]) -> VCRTrace u runCHP_VCRTrace :: CHP a -> IO (Maybe a, VCRTrace Unique) runCHP_VCRTraceAndPrint :: CHP a -> IO () instance Trace VCRTrace instance (Ord u) => Show (VCRTrace u) module Control.Concurrent.CHP.Traces.TraceOff -- | A trace type that does not record anything. data TraceOff a instance Trace TraceOff instance Show (TraceOff a) -- | This module contains support for structural traces. Structural traces -- reflect the parallel composition of your program. Effectively, each -- process records its own local trace. Parallel traces are then merged -- as parallel compositions, and you end up with a big tree of -- sequentially and parallel-composed traces. Note that in this tracing -- style, unlike CSP and VCR, events are recorded by every process -- involved in them, not just once per occurrence. module Control.Concurrent.CHP.Traces.Structural -- | 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 u StructuralTrace :: (ChannelLabels u, Maybe (EventHierarchy (RecordedIndivEvent u))) -> StructuralTrace u -- | 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 -> EventHierarchy a StructuralSequence :: Int -> [EventHierarchy a] -> EventHierarchy a StructuralParallel :: [EventHierarchy a] -> EventHierarchy a runCHP_StructuralTrace :: CHP a -> IO (Maybe a, StructuralTrace Unique) runCHP_StructuralTraceAndPrint :: CHP a -> IO () -- | 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. getAllEventsInHierarchy :: EventHierarchy a -> [a] instance (Show a) => Show (EventHierarchy a) instance (Read a) => Read (EventHierarchy a) instance Trace StructuralTrace instance (Ord u) => Show (StructuralTrace u) instance Traversable EventHierarchy instance Foldable EventHierarchy instance Functor EventHierarchy -- | This module contains support for CSP-style tracing. A CSP trace is -- simply a flat list of events in the order in which they occurred. module Control.Concurrent.CHP.Traces.CSP -- | 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 u CSPTrace :: (ChannelLabels u, [RecordedEvent u]) -> CSPTrace u runCHP_CSPTrace :: CHP a -> IO (Maybe a, CSPTrace Unique) runCHP_CSPTraceAndPrint :: CHP a -> IO () instance Trace CSPTrace instance (Ord u) => Show (CSPTrace u) -- | A module that re-exports all the parts of the library related to -- tracing. -- -- The idea of tracing is to record the concurrent events (i.e. channel -- communications and barrier synchronisations) that occur during a run -- of the program. You can think of it as automatically turning on a lot -- of debug logging. -- -- Typically, at the top-level of your program you should have: -- --
-- main :: IO () -- main = runCHP myMainProcess ---- -- To turn on the tracing mechanism of your choice (for example, -- CSP-style tracing) to automatically print out the trace after -- completion of your program, just use the appropriate helper function: -- --
-- main :: IO () -- main = runCHP_CSPTraceAndPrint myMainProcess ---- -- It could hardly be easier. If you want more fine-grained control and -- examination of the trace, you can use the helper functions of the form -- runCHP_CSPTrace that give back a data structure representing -- the trace, which you can then manipulate. -- -- The Doc used by the traces is from the Text.PrettyPrint.HughesPJ -- module that currently comes with GHC (I think). -- -- For more details on the theory behind the tracing, the logic behind -- its implementation, and example traces, see the paper "Representation -- and Implementation of CSP and VCR Traces", N.C.C. Brown and M.L. -- Smith, CPA 2008. An online version can be found at: -- http://twistedsquare.com/Traces.pdf module Control.Concurrent.CHP.Traces -- | A globally recorded event, found in CSP and VCR traces. Either a -- channel communication (with a unique identifier of the channel) or a -- barrier synchronisation (with a unique identifier of the barrier). The -- 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. -- -- The type became parameterised in version 1.3.0. type RecordedEvent u = (RecordedEventType, u) type ChannelLabels u = Map u String -- | ClockSync was added in version 1.2.0. data RecordedEventType ChannelComm :: RecordedEventType BarrierSync :: RecordedEventType ClockSync :: String -> RecordedEventType -- | An individual record of an event, found in nested traces. Either a -- channel write or read, or a barrier synchronisation, each with a -- unique identifier for the barrier/channel. The event will be recorded -- by everyone involved, and a ChannelWrite will have the same channel -- identifier as the corresponding channel-read. The 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. -- -- 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 -> RecordedIndivEvent u ChannelRead :: u -> Integer -> RecordedIndivEvent u BarrierSyncIndiv :: u -> Integer -> RecordedIndivEvent u ClockSyncIndiv :: u -> Integer -> String -> RecordedIndivEvent u -- | Added in version 1.3.0. recordedIndivEventLabel :: RecordedIndivEvent u -> u -- | Added in version 1.3.0. recordedIndivEventSeq :: RecordedIndivEvent u -> Integer -- | A class representing a type of trace. The main useful function is -- runCHPAndTrace, 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. -- -- The trace type involved became parameterised in version 1.3.0. class Trace t runCHPAndTrace :: (Trace t) => CHP a -> IO (Maybe a, t Unique) emptyTrace :: (Trace t) => t u prettyPrint :: (Trace t, Ord u) => t u -> Doc labelAll :: (Trace t, Ord u) => t u -> t String module Control.Concurrent.CHP.Parallel -- | This type-class supports parallel composition of processes. You may -- use the runParallel function to run a list of processes, or the -- <||> operator to run just a pair of processes. -- -- In each case, the composition waits for all processes to finish, -- either successfully or with poison. At the end of this, if any -- process exited with poison, the composition will "rethrow" this -- poison. If all the processes completed successfully, the results will -- be returned. If you want to ignore poison from the sub-processes, use -- an empty poison handler and onPoisonTrap with each branch. -- -- Runs the given list of processes in parallel, and then returns a list -- of the results, in the same order as the processes given. Will only -- return when all the inner processes have completed. runParallel :: [CHP a] -> CHP [a] -- | Runs all the given processes in parallel and discards any output. Does -- not return until all the processes have completed. runParallel_ -- ps is effectively equivalent to runParallel ps >> return -- (). runParallel_ :: [CHP a] -> CHP () -- | A useful operator for performing a two process equivalent of -- runParallel that gives the return values back as a pair rather -- than a list. This also allows the values to have different types (<||>) :: CHP a -> CHP b -> CHP (a, b) -- | An operator similar to <||> that discards the output -- (more like an operator version of runParallel_). -- -- Added in version 1.1.0. (<|*|>) :: CHP a -> CHP b -> CHP () -- | A monad transformer used for introducing forking blocks. data (Monad m, MonadCHP m) => ForkingT m a -- | Executes a forking block. Processes may be forked off inside (using -- the fork function). When the block completes, it waits for all -- the forked off processes to complete before returning the output, as -- long as none of the processes terminated with uncaught poison. If they -- did, the poison is propagated (rethrown). forking :: (MonadCHP m) => ForkingT m a -> m a -- | Forks off the given process. The process then runs in parallel with -- this code, until the end of the forking block, when all -- forked-off processes are waited for. At that point, once all of the -- processes have finished, if any of them threw poison it is propagated. fork :: (MonadCHP m) => CHP () -> ForkingT m () instance (Monad m) => Monad (ForkingT m) instance (MonadIO m) => MonadIO (ForkingT m) instance MonadTrans ForkingT instance (MonadCHP m) => MonadCHP (ForkingT m) -- | This module contains all the central monads in the CHP library. module Control.Concurrent.CHP.Monad -- | The central monad of the library. You can use -- Control.Concurrent.CHP.Monad.runCHP and -- Control.Concurrent.CHP.Monad.runCHP_ to execute programs in this -- monad. data CHP a -- | A monad transformer class that is very similar to MonadIO. This -- can be useful if you want to add monad transformers (such as -- StateT, ReaderT) on top of the CHP monad. class (MonadIO m) => MonadCHP m liftCHP :: (MonadCHP m) => CHP a -> m a -- | Runs a CHP program. You should use this once, at the top-level of your -- program. Do not ever use this function twice in parallel and attempt -- to 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) -- | Runs a CHP program. Like runCHP but discards the output. runCHP_ :: CHP a -> IO () -- | 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)) -- | A convenient version of embedCHP that ignores the result embedCHP_ :: CHP a -> CHP (IO ()) -- | A helper like embedCHP for callbacks that take an argument embedCHP1 :: (a -> CHP b) -> CHP (a -> IO (Maybe b)) -- | A convenient version of embedCHP1 that ignores the result embedCHP1_ :: (a -> CHP b) -> CHP (a -> IO ()) -- | Allows you to provide a handler for sections with poison. It is -- usually used in an infix form as follows: -- --
-- (readChannel c >>= writeChannel d) `onPoisonTrap` (poison c >> poison d) ---- -- It handles the poison and does not rethrow it (unless your handler -- does so). If you want to rethrow (and actually, you'll find you -- usually do), use onPoisonRethrow onPoisonTrap :: CHP a -> CHP a -> CHP a -- | Like onPoisonTrap, this function allows you to provide a -- handler for poison. The difference with this function is that even if -- the poison handler does not throw, the poison exception will always be -- re-thrown after the handler anyway. That is, the following lines of -- code all have identical behaviour: -- --
-- foo -- foo `onPoisonRethrow` throwPoison -- foo `onPoisonRethrow` return () --onPoisonRethrow :: CHP a -> CHP () -> CHP a -- | Throws a poison exception. throwPoison :: CHP a -- | A class indicating that something is poisonable. class Poisonable c poison :: (Poisonable c, MonadCHP m) => c -> m () checkForPoison :: (Poisonable c, MonadCHP m) => c -> m () -- | Poisons all the given items. A handy shortcut for mapM_ -- poison. poisonAll :: (Poisonable c, MonadCHP m) => [c] -> m () -- | 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. data (Monad m) => LoopWhileT m a -- | 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. loop :: (Monad m) => LoopWhileT m a -> m () -- | Continues executing the loop if the given value is True. If the value -- is False, the loop is broken immediately, and control jumps back to -- the next action after the outer loop statement. Thus you can -- build pre-condition, post-condition, and "mid-condition" loops, -- placing the condition wherever you like. while :: (Monad m) => Bool -> LoopWhileT m () -- | The classic skip process/guard. Does nothing, and is always ready. -- -- Suitable for use in an Control.Concurrent.CHP.Alt.alt. skip :: CHP () -- | 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 () -- | Waits for the specified number of microseconds (millionths of a -- second). There is no guaranteed precision, but the wait will never -- complete in less time than the parameter given. -- -- Suitable for use in an Control.Concurrent.CHP.Alt.alt, but note that -- waitFor 0 is not the same as skip. waitFor 0 -- Control.Concurrent.CHP.Alt.</> x will not always select the -- first guard, depending on x. Included in this is the lack of guarantee -- that waitFor 0 Control.Concurrent.CHP.Alt.</> -- waitFor n will select the first guard for any value of n -- (including 0). It is not useful to use two waitFor guards in a -- single Control.Concurrent.CHP.Alt.alt anyway. -- -- 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 () instance (MonadCHP m) => MonadCHP (LoopWhileT m) instance (MonadIO m) => MonadIO (LoopWhileT m) instance MonadTrans LoopWhileT instance (Monad m) => Monad (LoopWhileT m) -- | A module with support for things that are enrollable (barriers and -- broadcast channels). module Control.Concurrent.CHP.Enroll -- | An enrolled wrapper for barriers that shows at the type-level whether -- you are enrolled on a barrier. Enrolled Barriers should never be -- passed to two (or more) processes running in parallel; if two -- processes synchronise based on a single enroll call, undefined -- behaviour will result. data Enrolled b a class Enrollable b z enroll :: (Enrollable b z) => b z -> (Enrolled b z -> CHP a) -> CHP a resign :: (Enrollable b z) => Enrolled b z -> CHP a -> CHP a -- | Just like enroll, but starts with an already enrolled item. During the -- body, you are enrolled twice -- once from the original enrolled item -- (which is still valid in the body) and once from the new enrolled item -- passed to the inner function. Afterwards, you are enrolled once, on -- the original item you had. -- -- This function was added in version 1.3.2. furtherEnroll :: (Enrollable b z) => Enrolled b z -> (Enrolled b z -> CHP a) -> CHP a -- | Like enroll but enrolls on the given pair of barriers enrollPair :: (Enrollable b p, Enrollable b' p') => (b p, b' p') -> ((Enrolled b p, Enrolled b' p') -> CHP a) -> CHP a -- | Like enroll but enrolls on the given list of barriers enrollList :: (Enrollable b p) => [b p] -> ([Enrolled b p] -> CHP a) -> CHP a -- | A module containing the ALT constructs. An ALT (a term inherited from -- occam) is a choice between several alternate events. In CHP, we say -- that an event must support alting to be a valid choice. Events that -- do support alting are: -- --
-- liftM Just (readChannel c) <-> (waitFor 1000000 >> return Nothing) ---- --
-- liftM Just (readChannel c) </> (skip >> return Nothing) ---- --
-- readChannel c0 <-> readChannel c1 ---- --
-- (readChannel c >>= writeChannel d) </> skip ---- -- Note that if you wait for a sequential composition: -- --
-- (readChannel c >>= writeChannel d) <-> (writeChannel e 6 >> readChannel f) ---- -- This only waits for the first action in both (reading from channel c, -- or writing to channel e), not for all of the actions (as, for example, -- an STM transaction would). module Control.Concurrent.CHP.Alt -- | An alt between several actions, with arbitrary priority. The first -- available action is chosen (with an arbitrary choice if many actions -- are available at the same time), its body run, and its value returned. alt :: [CHP a] -> CHP a -- | 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 to use the alt function. alt -- may be more efficent than foldl1 (<->) (<->) :: CHP a -> CHP a -> CHP a -- | An alt between several actions, with descending priority. The first -- available action is chosen (biased towards actions nearest the -- beginning of the list), its body run, and its value returned. -- -- What priority means here is a difficult thing, and in some ways a -- historical artifact. We can group the guards into three categories: -- -- 1. synchronisation guards (reading from and writing to channels, and -- synchronising on barriers) -- -- 2. time-out guards (such as waitFor) -- -- 3. dummy guards (skip and stop) -- -- There exists priority when comparing dummy guards to anything else. So -- for example, -- --
-- priAlt [ skip, x ] ---- -- Will always select the first guard, whereas: -- --
-- priAlt [ x , skip ] ---- -- Is an effective way to poll and see if x is ready, otherwise the -- skip will be chosen. However, there is no priority between -- synchronisation guards and time-out guards. So the two lines: -- --
-- priAlt [ x, y ] -- priAlt [ y, x ] ---- -- May have the same or different behaviour (when x and y are not dummy -- guards), there is no guarantee either way. The reason behind this is -- that if you ask for: -- --
-- priAlt [ readChannel c, writeChannel d 6 ] ---- -- And the process at the other end is asking for: -- --
-- priAlt [ readChannel d, writeChannel c 8 ] ---- -- 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 -- | A useful operator to perform a priAlt. This operator is -- associative, and has descending priority (that is, it is left-biased). -- When you have lots of actions, it is probably easier to use the -- priAlt function. priAlt may be more efficent than -- foldl1 (</>) (>) :: CHP a -> CHP a -> CHP a -- | Runs all the given processes in parallel with each other, but only -- when the choice at the beginning of each item is ready. -- -- So for example, if you do: -- --
-- every [ readChannel c >>= writeChannel d, readChannel e >>= writeChannel f] ---- -- This will forward values from c and e to d and f respectively in -- parallel, but only once both channels c and e are ready to be read -- from. So f cannot be written to before c is read from (contrast this -- with what would happen if every were replaced with -- runParallel). -- -- This behaviour can be somewhat useful, but every is much more -- powerful when used as part of an alt. This code: -- --
-- alt [ every [ readChannel c, readChannel d] -- , every [ writeChannel e 6, writeChannel f 8] ] ---- -- Waits to either read from channels c and d, or to write to channels e -- and f. -- -- The events involved can partially overlap, e.g. -- --
-- alt [ every [ readChannel a, readChannel b] -- , every [ readChannel a, writeChannel c 6] ] ---- -- This will wait to either read from channels a and b, or to read from a -- and write to c, whichever combination is ready first. If both are -- ready, the choice between them will be arbitrary (just as with any -- other choices; see alt for more details). -- -- The sets can even be subsets of each other, such as: -- --
-- alt [ every [ readChannel a, readChannel b] -- , every [ readChannel a, readChannel b, readChannel b] ] ---- -- In this case there are no guarantees as to which choice will happen. -- Do not assume it will be the smaller, and do not assume it will be the -- larger. -- -- Be wary of what happens if a single event is included multiple times -- in the same every, as this may not do what you expect (with or -- without choice). Consider: -- --
-- every [ readChannel c >> writeChannel d 6 -- , readChannel c >> writeChannel d 8 ] ---- -- What will happen is that the excecution will wait to read from c, but -- then it will execute only one of the bodies (an arbitrary choice). In -- general, do not rely on this behaviour, and instead try to avoid -- having the same events in an every. Also note that if you -- synchronise on a barrier twice in an every, this will only -- count as one member enrolling, even if you have two enrolled ends! For -- such a use, look at runParallel instead. -- -- Also note that this currently applies to both ends of channels, so -- that: -- --
-- every [ readChannel c, writeChannel c 2 ] ---- -- Will block indefinitely, rather than completing the communication. -- -- Each item every must support choice (and in fact only a subset -- of the items supported by alt are supported by every). -- Currently the items in the list passed to every must be one of -- the following: -- --
-- every [ readChannel c -- , alt [ readChannel d, readChannel e ] ] ---- -- To wait for c and (d or e) like this you must expand it out into (c -- and d) or (c and e): -- --
-- alt [ every [ readChannel c, readChannel d] -- , every [ readChannel c, readChannel e] ] ---- -- As long as x meets the conditions laid out above, every [x] -- will have the same behaviour as x. -- -- Added in version 1.1.0 every :: [CHP a] -> CHP [a] -- | 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. -- -- Added in version 1.1.0 (<&>) :: CHP a -> CHP b -> CHP (a, b) -- | A module containing barriers. -- -- A barrier is a synchronisation primitive. When N processes are -- enrolled on a barrier, all N must synchronise on the barrier before -- any synchronisations may complete, at which point they all complete. -- That is, when a single process synchronises on a barrier, it must then -- wait until all the other enrolled processes also synchronise before it -- can finish. -- -- Only processes enrolled on a barrier may synchronise on it. Enrolled -- barriers should not be passed around between processes, or used twice -- in a parallel composition. Instead, each process should enroll on the -- barrier itself. -- -- Barriers support choice (alting). This can lead to a lot of -- non-determinism and some confusion. Consider these two processes, both -- enrolled on barriers a and b: -- --
-- (sync a <-> sync b) -- (sync b <-> sync a) ---- -- Which barrier completes is determined by the run-time, and will be an -- arbitrary choice. This is even the case when priority is involved: -- --
-- (sync a </> sync b) -- (sync b </> sync a) ---- -- Clearly there is no way to resolve this to satisfy both priorities; -- the run-time will end up choosing. -- -- Barrier poison can be detected when syncing, enrolling or resigning. -- You may only poison a barrier that you are currently enrolled on. -- -- Barriers can also support phases. The idea behind a phased barrier is -- that a barrier is always on a certain phase P. Whenever a barrier -- successfully completes, the phase is incremented (but it does not have -- to be an integer). Everyone is told the new phase once they complete a -- synchronisation, and may query the current phase for any barrier that -- they are currently enrolled on. module Control.Concurrent.CHP.Barriers -- | A special case of the PhasedBarrier that has no useful phases, i.e. a -- standard barrier. type Barrier = PhasedBarrier () -- | A useful type synonym for enrolled barriers with no phases -- -- Added in 1.1.0 type EnrolledBarrier = Enrolled PhasedBarrier () -- | Creates a new barrier with no processes enrolled newBarrier :: CHP Barrier -- | Creates a new barrier with no processes enrolled and labels it in -- traces using the given label. See newBarrier. newBarrierWithLabel :: String -> CHP Barrier -- | A phased barrier that is capable of being poisoned and throwing -- poison. You will need to enroll on it to do anything useful with it. -- For the phases you can use any type that satisfies Enum, -- Bounded and Eq. The phase increments every time the -- barrier completes. Incrementing consists of: if p == maxBound then -- minBound else succ p. Examples of things that make sense for -- phases: -- --
-- (a, b, c, d, e) <- newChannels ---- -- To create five channels of the same type. class ChannelTuple t newChannels :: (ChannelTuple t, MonadCHP m) => m t -- | Creates a list of channels of the same type with the given length. If -- you need to access some channels by index, use this function. -- Otherwise you may find using newChannels to be easier. newChannelList :: (Channel r w, MonadCHP m) => Int -> m [Chan r w a] -- | A helper that is like newChannelList, but labels the channels -- with the given list. The number of channels returned is the same as -- the length of the list of labels newChannelListWithLabels :: (Channel r w, MonadCHP m) => [String] -> m [Chan r w a] -- | A helper that is like newChannelList, but labels the channels -- according to a pattern. Given a stem such as foo, it names the -- channels in the list foo0, foo1, foo2, etc. newChannelListWithStem :: (Channel r w, MonadCHP m) => Int -> String -> m [Chan r w a] -- | Gets the channel's identifier. Useful if you need to be able to -- identify a channel in the trace later on. getChannelIdentifier :: Chan r w a -> Unique -- | A reading channel-end type that allows poison to be thrown -- -- Eq instance added in version 1.1.1 data Chanin a -- | A writing channel-end type that allows poison to be thrown -- -- Eq instance added in version 1.1.1 data Chanout a -- | Gets the reading end of a channel from its Chan type. reader :: Chan r w a -> r a -- | Gets the writing end of a channel from its Chan type. writer :: Chan r w a -> w a -- | Gets all the reading ends of a list of channels. A shorthand for -- map reader. readers :: [Chan r w a] -> [r a] -- | Gets all the writing ends of a list of channels. A shorthand for -- map writer. writers :: [Chan r w a] -> [w a] -- | A class indicating that a channel can be read from. class ReadableChannel chanEnd readChannel :: (ReadableChannel chanEnd) => chanEnd a -> CHP a extReadChannel :: (ReadableChannel chanEnd) => chanEnd a -> (a -> CHP b) -> CHP b -- | A class indicating that a channel can be written to. class WriteableChannel chanEnd writeChannel :: (WriteableChannel chanEnd) => chanEnd a -> a -> CHP () extWriteChannel :: (WriteableChannel chanEnd) => chanEnd a -> CHP a -> CHP () -- | Claims the given channel-end, executes the given block, then releases -- the channel-end and returns the output value. If poison or an IO -- exception is thrown inside the block, the channel is released and the -- poison/exception re-thrown. claim :: Shared c a -> (c a -> CHP b) -> CHP b -- | A wrapper (usually around a channel-end) indicating that the inner -- item is shared. Use the claim function to use this type. data Shared c a type OneToOneChannel = Chan Chanin Chanout -- | A type-constrained version of newChannel. oneToOneChannel :: (MonadCHP m) => m (OneToOneChannel a) -- | A type-constrained version of newChannelWithLabel. -- -- Added in version 1.2.0. oneToOneChannelWithLabel :: (MonadCHP m) => String -> m (OneToOneChannel a) type OneToAnyChannel = Chan (Shared Chanin) (Chanout) -- | A type-constrained version of newChannel. oneToAnyChannel :: (MonadCHP m) => m (OneToAnyChannel a) -- | A type-constrained version of newChannelWithLabel. -- -- Added in version 1.2.0. oneToAnyChannelWithLabel :: (MonadCHP m) => String -> m (OneToAnyChannel a) type AnyToOneChannel = Chan (Chanin) (Shared Chanout) -- | A type-constrained version of newChannel. anyToOneChannel :: (MonadCHP m) => m (AnyToOneChannel a) -- | A type-constrained version of newChannelWithLabel. -- -- Added in version 1.2.0. anyToOneChannelWithLabel :: (MonadCHP m) => String -> m (AnyToOneChannel a) type AnyToAnyChannel = Chan (Shared Chanin) (Shared Chanout) -- | A type-constrained version of newChannel. anyToAnyChannel :: (MonadCHP m) => m (AnyToAnyChannel a) -- | A type-constrained version of newChannelWithLabel. -- -- Added in version 1.2.0. anyToAnyChannelWithLabel :: (MonadCHP m) => String -> m (AnyToAnyChannel a) instance Eq (STMChannel a) instance Eq (Chanout a) instance Eq (Chanin a) instance Channel (Shared Chanin) (Shared Chanout) instance Channel Chanin (Shared Chanout) instance Channel (Shared Chanin) Chanout instance Channel Chanin Chanout instance ChanoutC STMChannel a instance ChaninC STMChannel a instance (Channel r w) => ChannelTuple (Chan r w a, Chan r w a, Chan r w a, Chan r w a, Chan r w a, Chan r w a) instance (Channel r w) => ChannelTuple (Chan r w a, Chan r w a, Chan r w a, Chan r w a, Chan r w a) instance (Channel r w) => ChannelTuple (Chan r w a, Chan r w a, Chan r w a, Chan r w a) instance (Channel r w) => ChannelTuple (Chan r w a, Chan r w a, Chan r w a) instance (Channel r w) => ChannelTuple (Chan r w a, Chan r w a) instance Poisonable (Chanout a) instance Poisonable (Chanin a) instance WriteableChannel Chanout instance ReadableChannel Chanin -- | A module containing broadcast channels (one-to-many). Whereas a -- one-to-any channel features one writer sending a single value -- to one (of many) readers, a one-to-many channel features one -- writer sending the same value to many readers. So a -- one-to-any channel involves claiming the channel-end to ensure -- exclusivity, but a one-to-many channel involves enrolling on the -- channel-end (subscribing) before it can engage in communication. -- -- A communication on a one-to-many channel only takes place when the -- writer and all readers currently enrolled agree to communicate. What -- happens when the writer wants to communicate and no readers are -- enrolled is undefined (the writer may block, or may communicate -- happily to no-one). -- -- This module also contains reduce channels (added in version 1.1.1). -- Because in CHP channels must have the same type at both ends, we use -- the Monoid type-class. It is important to be aware that the order of -- mappends will be non-deterministic, and thus you should either use an -- mappend that is commutative or code around this restruction. -- -- For example, a common thing to do would be to use lists as the type -- for reduce channels, make each writer write a single item list (but -- more is possible), then use the list afterwards, but be aware that it -- is unordered. If it is important to have an ordered list, make each -- writer write a pair containing a (unique) index value and the real -- data, then sort by the index value and discard it. -- -- Since reduce channels were added after the initial library design, -- there is a slight complication: it is not possible to use newChannel -- (and all similar functions) with reduce channels because it is -- impossible to express the Monoid constraint for the Channel instance. -- Instead, you must use manyToOneChannel and manyToAnyChannel. module Control.Concurrent.CHP.BroadcastChannels -- | The reading end of a broadcast channel. You must enroll on it before -- you can read from it or poison it. data BroadcastChanin a -- | The writing end of a broadcast channel. data BroadcastChanout a type OneToManyChannel = Chan BroadcastChanin BroadcastChanout type AnyToManyChannel = Chan BroadcastChanin (Shared BroadcastChanout) oneToManyChannel :: (MonadCHP m) => m (OneToManyChannel a) anyToManyChannel :: (MonadCHP m) => m (AnyToManyChannel a) -- | Added in version 1.2.0. oneToManyChannelWithLabel :: (MonadCHP m) => String -> m (OneToManyChannel a) -- | Added in version 1.2.0. anyToManyChannelWithLabel :: (MonadCHP m) => String -> m (AnyToManyChannel a) -- | The reading end of a reduce channel. data ReduceChanin a -- | The writing end of a reduce channel. You must enroll on it before you -- can read from it or poison it. data ReduceChanout a type ManyToOneChannel = Chan ReduceChanin ReduceChanout type ManyToAnyChannel = Chan (Shared ReduceChanin) ReduceChanout manyToOneChannel :: (Monoid a, MonadCHP m) => m (ManyToOneChannel a) manyToAnyChannel :: (Monoid a, MonadCHP m) => m (ManyToAnyChannel a) manyToOneChannelWithLabel :: (Monoid a, MonadCHP m) => String -> m (ManyToOneChannel a) manyToAnyChannelWithLabel :: (Monoid a, MonadCHP m) => String -> m (ManyToAnyChannel a) instance Enum Phase instance Bounded Phase instance Eq Phase instance Poisonable (ReduceChanin a) instance Poisonable (Enrolled ReduceChanout a) instance ReadableChannel ReduceChanin instance WriteableChannel (Enrolled ReduceChanout) instance Enrollable ReduceChanout a instance Channel BroadcastChanin (Shared BroadcastChanout) instance Channel BroadcastChanin BroadcastChanout instance Poisonable (Enrolled BroadcastChanin a) instance Poisonable (BroadcastChanout a) instance ReadableChannel (Enrolled BroadcastChanin) instance WriteableChannel BroadcastChanout instance Enrollable BroadcastChanin a -- | This module re-exports the core functionality of the CHP library. -- Other modules that you also may wish to import are: -- --
-- import qualified Control.Concurrent.CHP.Common as Common ---- -- or: -- --
-- import qualified Control.Concurrent.CHP.Common as CHP ---- -- to circumvent this problem. module Control.Concurrent.CHP.Common -- | Forever forwards the value onwards, unchanged. Adding this to your -- process network effectively adds a single-place buffer. id :: (ReadableChannel r, Poisonable (r a), WriteableChannel w, Poisonable (w a)) => r a -> w a -> CHP () -- | Forever forwards the value onwards. This is like id but does -- not add any buffering to your network, and its presence is -- indetectable to the process either side. -- -- extId is a unit of the associative operator -- Control.Concurrent.CHP.Utils.|->|. -- -- The behaviour of this process was corrected in version 1.1.0 to work -- properly when the reader of its output channel was offering choice. extId :: Chanin a -> Chanout a -> CHP () -- | A process that waits for an input, then sends it out on all its -- output channels (in order) during an extended rendezvous. This is -- often used to send the output on to both the normal recipient (without -- introducing buffering) and also to a listener process that wants to -- examine the value. If the listener process is first in the list, and -- does not take the input immediately, the value will not be sent to the -- other recipients until it does. The name of the process derives from -- the notion of a wire-tap, since the listener is hidden from the other -- processes (it does not visibly change the semantics for them -- except -- when the readers of the channels are offering a choice). tap :: Chanin a -> [Chanout a] -> CHP () -- | Sends out a single value first (the prefix) then behaves like id. prefix :: a -> Chanin a -> Chanout a -> CHP () -- | Forever reads in a value, and then sends out its successor (using -- succ). succ :: (Enum a) => Chanin a -> Chanout a -> CHP () -- | Reads in a value, and sends it out in parallel on all the given output -- channels. parDelta :: Chanin a -> [Chanout a] -> CHP () -- | Forever reads in a value, transforms it using the given function, and -- sends it out again. Note that the transformation is not applied -- strictly, so don't assume that this process will actually perform the -- computation. If you require a strict transformation, use map'. map :: (a -> b) -> Chanin a -> Chanout b -> CHP () -- | Like map, but applies the transformation strictly before -- sending on the value. -- -- Added in version 1.1.0. map' :: (NFData b) => (a -> b) -> Chanin a -> Chanout b -> CHP () -- | Forever reads in a value, and then based on applying the given -- function either discards it (if the function returns False) or sends -- it on (if the function returns True). filter :: (a -> Bool) -> Chanin a -> Chanout a -> CHP () -- | Streams all items in a Data.Traversable.Traversable container out in -- the order given by Data.Traversable.mapM on the output channel (one at -- a time). Lists, Maybe, and Data.Set.Set are all instances of -- Data.Traversable.Traversable, so this can be used for all of those. stream :: (Traversable t) => Chanin (t a) -> Chanout a -> CHP () -- | Forever waits for input from one of its many channels and sends it out -- again on the output channel. merger :: [Chanin a] -> Chanout a -> CHP () -- | Sends out the specified value on the given channel the specified -- number of times, then finishes. replicate :: Int -> a -> Chanout a -> CHP () -- | Forever sends out the same value on the given channel, until poisoned. -- Similar to the white-hole processes in some other frameworks. repeat :: a -> Chanout a -> CHP () -- | Forever reads values from the channel and discards them, until -- poisoned. Similar to the black-hole processes in some other -- frameworks. consume :: Chanin a -> CHP () -- | For the duration of the given process, acts as a consume process, but -- stops when the given process stops. Note that there could be a timing -- issue where extra inputs are consumed at the end of the lifetime of -- the process. Note also that while poison from the given process will -- be propagated on the consumption channel, there is no mechanism to -- propagate poison from the consumption channel into the given process. -- -- Added in version 1.2.0. consumeAlongside :: Chanin a -> CHP b -> CHP b -- | Forever reads a value from both its input channels in parallel, then -- joins the two values using the given function and sends them out -- again. For example, join (,) c d will pair the values read -- from c and d and send out the pair on the output -- channel, whereas join (&&) will send out the -- conjunction of two boolean values, join (==) will read two -- values and output whether they are equal or not, etc. join :: (a -> b -> c) -> Chanin a -> Chanin b -> Chanout c -> CHP () -- | Forever reads a value from all its input channels in parallel, then -- joins the values into a list in the same order as the channels, and -- sends them out again. joinList :: [Chanin a] -> Chanout [a] -> CHP () -- | Forever reads a pair from its input channel, then in parallel sends -- out the first and second parts of the pair on its output channels. -- -- Added in version 1.0.2. split :: Chanin (a, b) -> Chanout a -> Chanout b -> CHP () -- | A sorter process. When it receives its first Just x data -- item, it keeps it. When it receieves a second, it keeps the lowest of -- the two, and sends out the other one. When it receives Nothing, it -- sends out its data value, then sends Nothing too. The overall effect -- when chaining these things together is a sorting pump. You inject all -- the values with Just, then send in a single Nothing to get the results -- out (in reverse order). sorter :: (Ord a) => Chanin (Maybe a) -> Chanout (Maybe a) -> CHP () -- | Like sorter, but with a custom comparison method. You should pass in -- the equivalent of less-than: (<). sorter' :: (a -> a -> Bool) -> Chanin (Maybe a) -> Chanout (Maybe a) -> CHP () -- | A shared variable process. Given an initial value and two channels, it -- continually offers to output its current value or read in a new one. -- -- Added in version 1.1.1 -- -- Note that prior to version 1.2.0 (i.e. in version 1.1.1) there was a -- bug where poison would not be propagated between the input and output. valueStore :: (ReadableChannel r, Poisonable (r a), WriteableChannel w, Poisonable (w a)) => a -> r a -> w a -> CHP () -- | A shared variable process. The same as valueStore, but initially waits -- to read its starting value before then offering to either output its -- current value or read in a new one. -- -- Added in version 1.1.1 -- -- Note that prior to version 1.2.0 (i.e. in version 1.1.1) there was a -- bug where poison would not be propagated between the input and output. valueStore' :: (ReadableChannel r, Poisonable (r a), WriteableChannel w, Poisonable (w a)) => r a -> w a -> CHP () -- | Continually waits for a specific time on the given clock, each time -- applying the function to work out the next specific time to wait for. -- The most common thing to pass is Prelude.succ or (+1). -- -- Added in version 1.2.0. advanceTime :: (Waitable c, Ord t) => (t -> t) -> Enrolled c t -> CHP () -- | Various processes that act like buffers. Poisoning either end of a -- buffer process is immediately passed on to the other side, in contrast -- to C++CSP2 and JCSP. module Control.Concurrent.CHP.Buffers -- | Acts like a limited capacity FIFO buffer of the given size. When it is -- full it accepts no input, and when it is empty it offers no output. fifoBuffer :: Int -> Chanin a -> Chanout a -> CHP () -- | Acts like a FIFO buffer with unlimited capacity. Use with caution; -- make sure you do not let the buffer grow so large that it eats up all -- your memory. When it is empty, it offers no output. It always accepts -- input. infiniteBuffer :: Chanin a -> Chanout a -> CHP () -- | Acts like a FIFO buffer with unlimited capacity, but accumulates -- sequential inputs into a list which it offers in a single output. Use -- with caution; make sure you do not let the buffer grow so large that -- it eats up all your memory. When it is empty, it offers the empty -- list. It always accepts input. Once it has sent out a value (or -- values) it removes them from its internal storage. -- -- Added in version 1.2.0. accumulatingInfiniteBuffer :: Chanin a -> Chanout [a] -> CHP () -- | Acts like a FIFO buffer of limited capacity, except that when it is -- full, it always accepts input and discards it. When it is empty, it -- does not offer output. overflowingBuffer :: Int -> Chanin a -> Chanout a -> CHP () -- | Acts like a FIFO buffer of limited capacity, except that when it is -- full, it always accepts input and pushes out the oldest item in the -- buffer. When it is empty, it does not offer output. overwritingBuffer :: Int -> Chanin a -> Chanout a -> CHP () -- | Contains a process for easily using stdin, stdout and stderr as -- channels. module Control.Concurrent.CHP.Console -- | A set of channels to be given to the process to run, containing -- channels for stdin, stdout and stderr. data ConsoleChans ConsoleChans :: Chanin Char -> Chanout Char -> Chanout Char -> ConsoleChans cStdin :: ConsoleChans -> Chanin Char cStdout :: ConsoleChans -> Chanout Char cStderr :: ConsoleChans -> Chanout Char -- | A function for running the given CHP process that wants console -- channels. When your program finishes, the console channels are -- automatically poisoned, but it's good practice to poison them yourself -- when you finish. Only ever run one of these processes at a time, or -- undefined behaviour will result. -- -- When using this process, due to the way that the console handlers are -- terminated, you may sometimes see a notice that a thread was killed. -- This is normal behaviour (unfortunately). consoleProcess :: (ConsoleChans -> CHP ()) -> CHP () -- | A collection of useful functions to use with the library. module Control.Concurrent.CHP.Utils -- | Wires given processes up in a forward cycle. That is, the first -- process writes to the second, and receives from the last. It returns -- the list of wired-up processes, which you will almost certainly want -- to run in parallel. wireCycle :: (Channel r w) => [r a -> w a -> proc] -> CHP [proc] -- | Wires the given processes up in a forward pipeline. The first process -- in the list is connected to the given reading channel-end (the first -- parameter) and the writing end of a new channel, A. The second process -- is wired up to the reading end of A, and the writing end of the next -- new channel, B. This proceeds all the way to the end of the list, -- until the final process is wired to the reading end of Z (if you have -- 27 processes in the list, and therefore 26 channels in the middle of -- them) and the second parameter. The list of wired-up processes is -- returned, which you can then run in parallel. wirePipeline :: (Channel r w) => [r a -> w a -> proc] -> r a -> w a -> CHP [proc] -- | A specialised version of wirePipeline. Given a list of -- processes, composes them into an ordered pipeline, that takes the -- channel-ends for the sticking out ends of the pipeline and gives a -- process that returns a list of their results. This is equivalent to -- wirePipeline, with the return value fed to runParallel. -- -- Added in version 1.0.2. pipeline :: [Chanin a -> Chanout a -> CHP b] -> Chanin a -> Chanout a -> CHP [b] -- | A specialised version of wireCycle. Given a list of processes, -- composes them into a cycle and runs them all in parallel. This is -- equivalent to wireCycle with the return value fed into -- runParallel. -- -- Added in version 1.0.2. cycle :: [Chanin a -> Chanout a -> CHP b] -> CHP [b] -- | Process composition. Given two processes, composes them into a -- pipeline, like function composition (but with an opposite ordering). -- The function is associative. Using wirePipeline will be more efficient -- than foldl1 (|->|) for more than two processes. -- -- The type for this process became more specific in version 1.2.0. (|->|) :: (a -> Chanout b -> CHP ()) -> (Chanin b -> c -> CHP ()) -> (a -> c -> CHP ()) -- | The reversed version of the other operator. -- -- The type for this process became more specific in version 1.2.0. (|<-|) :: (Chanin b -> c -> CHP ()) -> (a -> Chanout b -> CHP ()) -> (a -> c -> CHP ()) -- | A function to use at the start of a pipeline you are chaining together -- with the '(|->|)' operator. Added in version 1.2.0. (->|) :: (Chanout b -> CHP ()) -> (Chanin b -> c -> CHP ()) -> (c -> CHP ()) -- | A function to use at the end of a pipeline you are chaining together -- with the '(|->|)' operator. Added in version 1.2.0. (|->) :: (a -> Chanout b -> CHP ()) -> (Chanin b -> CHP ()) -> (a -> CHP ()) -- | Provides an instance of Arrow for process pipelines. As described in -- the original paper on arrows, they can be used to represent stream -- processing, so CHP seemed like a possible fit for an arrow. -- -- Whether this is actually an instance of Arrow depends on -- technicalities. This can be demonstrated with the arrow law arr id -- >>> f = f = f >>> arr id. Whether CHP satisfies -- this arrow law depends on the definition of equality. -- --
-- runPipeline (arr (*2) *** arr (+1)) ---- -- Arrows are more useful where you already have processes written that -- process data and you want to easily wire them together. The arrow -- notation is probably easier for doing that than declaring all the -- channels yourself and composing everything in parallel. runPipeline :: ProcessPipeline a b -> Chanin a -> Chanout b -> CHP () -- | Adds a wrapper that forms this process into the right data type to be -- part of an arrow. -- -- Any process you apply this to should produce exactly one output per -- input, or else you will find odd behaviour resulting (including -- deadlock). So for example, don't use arrowProcess -- (Control.Concurrent.CHP.Common.filter ...) or arrowProcess -- Control.Concurrent.CHP.Common.stream -- -- Added in version 1.1.0 arrowProcess :: (Chanin a -> Chanout b -> CHP ()) -> ProcessPipeline a b -- | Like the arr function of the ProcessPipeline arrow instance, but fully -- evaluates the result before sending it. If you are building process -- pipelines with arrows to try and get some parallel speed-up, you -- should try this function instead of arr itself. -- -- Added in version 1.3.2 arrStrict :: (NFData b) => (a -> b) -> ProcessPipeline a b instance ArrowChoice ProcessPipeline instance Arrow ProcessPipeline instance Category ProcessPipeline