chp 1.5.1 → 1.6.0
raw patch · 8 files changed
+232/−22 lines, 8 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Control.Concurrent.CHP.Arrow: instance Functor (ProcessPipeline a)
+ Control.Concurrent.CHP.Behaviours: alongside :: CHPBehaviour a -> CHPBehaviour b -> CHPBehaviour (a, b)
+ Control.Concurrent.CHP.Behaviours: alongside_ :: CHPBehaviour a -> CHPBehaviour b -> CHPBehaviour ()
+ Control.Concurrent.CHP.Behaviours: data CHPBehaviour a
+ Control.Concurrent.CHP.Behaviours: endWhen :: CHP a -> CHPBehaviour (Maybe a)
+ Control.Concurrent.CHP.Behaviours: instance Functor CHPBehaviour
+ Control.Concurrent.CHP.Behaviours: offer :: CHPBehaviour a -> CHP a
+ Control.Concurrent.CHP.Behaviours: offerAll :: [CHPBehaviour a] -> CHP [a]
+ Control.Concurrent.CHP.Behaviours: once :: CHP a -> CHPBehaviour (Maybe a)
+ Control.Concurrent.CHP.Behaviours: repeatedly :: CHP a -> CHPBehaviour [a]
+ Control.Concurrent.CHP.Behaviours: repeatedlyRecurse :: (a -> CHP (b, a)) -> a -> CHPBehaviour [b]
+ Control.Concurrent.CHP.Behaviours: repeatedlyRecurse_ :: (a -> CHP a) -> a -> CHPBehaviour ()
+ Control.Concurrent.CHP.Behaviours: repeatedly_ :: CHP a -> CHPBehaviour ()
- Control.Concurrent.CHP.Monad: stop :: CHP ()
+ Control.Concurrent.CHP.Monad: stop :: CHP a
Files
- Control/Concurrent/CHP.hs +2/−0
- Control/Concurrent/CHP/Arrow.hs +5/−0
- Control/Concurrent/CHP/Behaviours.hs +190/−0
- Control/Concurrent/CHP/Channels/BroadcastReduce.hs +22/−13
- Control/Concurrent/CHP/Common.hs +3/−3
- Control/Concurrent/CHP/Monad.hs +6/−3
- Control/Concurrent/CHP/Traces/Base.hs +2/−2
- chp.cabal +2/−1
Control/Concurrent/CHP.hs view
@@ -33,6 +33,8 @@ -- * "Control.Concurrent.CHP.Actions" -- -- * "Control.Concurrent.CHP.Arrow"+-- +-- * "Control.Concurrent.CHP.Behaviours" -- -- * "Control.Concurrent.CHP.Buffers" --
Control/Concurrent/CHP/Arrow.hs view
@@ -167,6 +167,8 @@ -- | The type that is an instance of 'Arrow' for process pipelines. See 'runPipeline'.+--+-- The 'Functor' instance was added in version 1.6.0. data ProcessPipeline a b = ProcessPipeline { runPipeline :: Chanin a -> Chanout b -> CHP () -- ^ Given a 'ProcessPipeline' (formed using its 'Arrow' instance) and@@ -208,6 +210,9 @@ -- Added in version 1.3.2 arrStrict :: NFData b => (a -> b) -> ProcessPipeline a b arrStrict = ProcessPipeline . CHP.map'++instance Functor (ProcessPipeline a) where+ fmap f x = x >>> arr f #if __GLASGOW_HASKELL__ >= 609 instance Category ProcessPipeline where
+ Control/Concurrent/CHP/Behaviours.hs view
@@ -0,0 +1,190 @@+-- Communicating Haskell Processes.+-- Copyright (c) 2009, University of Kent.+-- All rights reserved.+-- +-- Redistribution and use in source and binary forms, with or without+-- modification, are permitted provided that the following conditions are+-- met:+--+-- * Redistributions of source code must retain the above copyright+-- notice, this list of conditions and the following disclaimer.+-- * Redistributions in binary form must reproduce the above copyright+-- notice, this list of conditions and the following disclaimer in the+-- documentation and/or other materials provided with the distribution.+-- * Neither the name of the University of Kent nor the names of its+-- contributors may be used to endorse or promote products derived from+-- this software without specific prior written permission.+--+-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS+-- IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,+-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR+-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR+-- CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,+-- EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,+-- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR+-- PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF+-- LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING+-- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS+-- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+++-- | A module containing CHP behaviours. See 'offer' for details.+--+-- This whole module was added in CHP 1.6.0.+module Control.Concurrent.CHP.Behaviours (+ CHPBehaviour, offer, offerAll, alongside, alongside_, endWhen, once, repeatedly, repeatedly_,+ repeatedlyRecurse, repeatedlyRecurse_) where++import Control.Applicative+import Control.Monad++import Control.Concurrent.CHP++-- | This data represents a behaviour (potentially repeated) that will result in+-- returning a value of type @a@. See 'offer' for more details.+data CHPBehaviour a = CHPBehaviour a (Maybe (CHP (CHPBehaviour a)))++instance Functor CHPBehaviour where+ fmap f (CHPBehaviour x Nothing) = CHPBehaviour (f x) Nothing+ fmap f (CHPBehaviour x (Just m)) = CHPBehaviour (f x) (Just $ fmap f <$> m)++-- | Offers the given behaviour, and when it occurs, ends the entire call to 'offer'.+-- Returns Just the result if the behaviour happens, otherwise gives Nothing.+endWhen :: CHP a -> CHPBehaviour (Maybe a)+endWhen m = CHPBehaviour Nothing (Just $ (\x -> CHPBehaviour (Just x) Nothing) <$> m)++-- | Offers the given behaviour, and when it occurs, does not offer it again.+-- Returns Just the result if the behaviour happens, otherwise gives Nothing.+-- 'once' is different to 'endWhen' because the latter terminates the call to 'offer'+-- regardless of other behaviours, whereas 'once' does not terminate the call to 'offer',+-- it just won't be offered again during the call to 'offer'. Thus if you only+-- offer some 'once' items without any 'endWhen', then after all the 'once' events+-- have happened, the process will deadlock.+once :: CHP a -> CHPBehaviour (Maybe a)+once m = CHPBehaviour Nothing (Just $ (\x -> CHPBehaviour (Just x) (Just stop)) <$> m)++-- | Repeatedly offers the given behaviour until the outer call to 'offer' is terminated+-- by an 'endWhen' event. A list is returned (in chronological order) of the results+-- of each occurrence of the behaviour.+repeatedly :: forall a. CHP a -> CHPBehaviour [a]+repeatedly m = reverse <$> repeatedly' []+ where+ repeatedly' :: [a] -> CHPBehaviour [a]+ repeatedly' xs = CHPBehaviour xs $ Just $ + (\x -> repeatedly' (x:xs)) <$> m++-- | Like 'repeatedly', but discards the output. Useful if the event is likely+-- to occur a lot, and you don't need the results.+repeatedly_ :: CHP a -> CHPBehaviour ()+repeatedly_ m = CHPBehaviour () $ Just $ m >> return (repeatedly_ m)++-- | Like 'repeatedly', but allows some state (of type @a@) to be passed from one+-- subsequent call to another, as well as generating the results of type @b@.+-- To begin with the function (first parameter) will be called with the initial+-- state (second parameter). If chosen, it will return the new state, and a result+-- to be accumulated into the list. The second call to the function will be passed+-- the new state, to then return the even newer state and a second result, and+-- so on.+--+-- If you want to use this with the StateT monad transformer from the mtl library,+-- you can call:+--+-- > repeatedlyRecurse (runStateT myStateAction) initialState+-- > where+-- > myStateAction :: StateT s CHP a+-- > initialState :: s+repeatedlyRecurse :: forall a b. (a -> CHP (b, a)) -> a -> CHPBehaviour [b]+repeatedlyRecurse f = fmap reverse . repeatedlyRecurse' []+ where+ repeatedlyRecurse' :: [b] -> a -> CHPBehaviour [b]+ repeatedlyRecurse' rs x = CHPBehaviour rs $ Just $+ (\(r, y) -> repeatedlyRecurse' (r : rs) y) <$> f x++-- | Like 'repeatedlyRecurse', but does not accumulate a list of results.+--+-- If you want to use this with the StateT monad transformer from the mtl library,+-- you can call:+--+-- > repeatedlyRecurse (execStateT myStateAction) initialState+-- > where+-- > myStateAction :: StateT s CHP a+-- > initialState :: s+repeatedlyRecurse_ :: forall a. (a -> CHP a) -> a -> CHPBehaviour ()+repeatedlyRecurse_ f = repeatedlyRecurse'+ where+ repeatedlyRecurse' :: a -> CHPBehaviour ()+ repeatedlyRecurse' x = CHPBehaviour () $ Just $ repeatedlyRecurse' <$> f x++-- | Offers one behaviour alongside another, combining their semantics. See 'offer'.+--+-- This operation is semantically associative and commutative.+alongside :: CHPBehaviour a -> CHPBehaviour b -> CHPBehaviour (a, b)+alongside oa@(CHPBehaviour a mfa) ob@(CHPBehaviour b mfb)+ = CHPBehaviour (a, b) (do fa <- mfa+ fb <- mfb+ return $ (flip alongside ob <$> fa) <-> (alongside oa <$> fb)+ )++-- | Offers one behaviour alongside another, combining their semantics. See 'offer'.+-- Unlike 'alongside', discards the output of the behaviours.+--+-- This operation is associative and commutative.+alongside_ :: CHPBehaviour a -> CHPBehaviour b -> CHPBehaviour ()+alongside_ (CHPBehaviour _ mfa) (CHPBehaviour _ mfb)+ = CHPBehaviour () (liftM2 (<->) (liftM blank <$> mfa) (liftM blank <$> mfb))+ where+ blank :: CHPBehaviour c -> CHPBehaviour ()+ blank = fmap (const ())++infixr `alongside`++-- | Offers the given behaviour until finished.+--+-- For example,+-- +-- > offer $ repeatedly p `alongside` repeatedly q+-- +-- will repeatedly offer p and q without ever terminating. This:+-- +-- > offer $ repeatedly p `alongside` repeatedly q `alongside` endWhen r+-- +-- will offer p repeatedly and q repeatedly and r, until r happens, at which point+-- the behaviour will end.+-- This:+-- +-- > offer $ once p `alongside` endWhen q+-- +-- will offer p and q; if p happens first it will wait for q, but if q happens+-- first it will finish. This:+-- +-- > offer $ once p `alongside` endWhen q `alongside` endWhen r+-- +-- permits p to happen at most once, while either of q or r happening will finish+-- the call.+--+-- All sorts of combinations are possible, but it is important to note that you+-- need at least one 'endWhen' event if you ever intend the call to finish. Some+-- laws involving 'offer' (ignoring the types and return values) are:+--+-- > offer (repeatedly p) == forever p+-- > offer (once p) == p >> stop -- i.e. it does not finish+-- > offer (endWhen q) == Just <$> q+-- > offer (endWhen p `alongside` endWhen q) == p <-> q+-- > offer (once p `alongside` endWhen q) == (p >> q) <-> q+--+-- Most other uses of 'offer' and 'alongside' do not reduce down to simple CHP+-- programs, which is of course their attraction.+offer :: CHPBehaviour a -> CHP a+offer (CHPBehaviour x Nothing) = return x+offer (CHPBehaviour _x (Just m)) = m >>= offer++-- | Offers all the given behaviours together, and gives back a list of the outcomes.+-- +-- This is roughly a shorthand for @offer . foldl1 alongside@, except that if you+-- pass the empty list, you simply get the empty list returned (rather than an+-- error)+offerAll :: [CHPBehaviour a] -> CHP [a]+offerAll [] = return []+offerAll bs = offer $ foldl1 (\x y -> fmap (uncurry (++)) $ alongside x y) bs'+ where+ bs' = map (fmap (:[])) bs
Control/Concurrent/CHP/Channels/BroadcastReduce.hs view
@@ -36,8 +36,15 @@ -- -- 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).+-- the writer wants to communicate and no readers are enrolled was undefined+-- in versions 1.5.1 and earlier (the writer may block, or may communicate+-- happily to no-one). However, in version 1.6.0 onwards the semantics are+-- clear: a writer that communicates on a broadcast channel with no readers+-- involved will succeed immediately, communicating to no-one (i.e. the data+-- is lost). Similarly, a read from a reduce channel will immediately return+-- 'mempty' when no writers are enrolled. It is possible to get into a state+-- of livelock (by repeatedly performing these instant communications) in+-- these sorts of situations. -- -- 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@@ -82,7 +89,7 @@ -- | The Eq instance was added in version 1.4.0. ----- In version 1.5.0, the broadcast and reduce channels do not appear correctly+-- In versions 1.5.0-1.6.0, the broadcast and reduce channels do not appear correctly -- in the traces. newtype BroadcastChannel a = BC (Barrier, TVar (Maybe a), ManyToOneTVar Int) @@ -166,14 +173,14 @@ -- | Added in version 1.5.0. ----- In version 1.5.0, the broadcast and reduce channels do not appear correctly+-- In versions 1.5.0-1.6.0, the broadcast and reduce channels do not appear correctly -- in the traces. oneToManyChannel' :: MonadCHP m => ChanOpts a -> m (OneToManyChannel a) oneToManyChannel' = newChannel' -- | Added in version 1.5.0. ----- In version 1.5.0, the broadcast and reduce channels do not appear correctly+-- In versions 1.5.0-1.6.0, the broadcast and reduce channels do not appear correctly -- in the traces. anyToManyChannel' :: MonadCHP m => ChanOpts a -> m (AnyToManyChannel a) anyToManyChannel' = newChannel'@@ -181,7 +188,7 @@ -- | The Eq instance was added in version 1.4.0. -- --- In version 1.5.0, the broadcast and reduce channels do not appear correctly+-- In versions 1.5.0-1.6.0, the broadcast and reduce channels do not appear correctly -- in the traces. newtype ReduceChannel a = GC (Barrier, ManyToOneTVar (Int, Maybe (a, TVar Bool)), (a -> a -> a, a)) @@ -219,14 +226,16 @@ return r instance ReadableChannel ReduceChanin where- extReadChannel (GI (GC (b, tv, (_, _empty)))) f+ extReadChannel (GI (GC (b, tv, (_, empty)))) f = do syncBarrierWith (indivRecJust ChannelRead) (\n -> resetManyToOneTVar tv (pred n, Nothing)) $ Enrolled b -- Subtract one for reader- (_, Just (x, tvb)) <- liftIO $ atomically $ readManyToOneTVar tv- y <- f x- liftIO $ atomically $ writeTVar tvb True- return y+ (_, val) <- liftIO $ atomically $ readManyToOneTVar tv+ case val of+ Just (x, tvb) -> do y <- f x+ liftIO $ atomically $ writeTVar tvb True+ return y+ Nothing -> f empty instance Poisonable (Enrolled ReduceChanout a) where poison (Enrolled (GO (GC (b,_,_)))) = poison $ Enrolled b@@ -267,14 +276,14 @@ -- | Added in version 1.5.0. -- --- In version 1.5.0, the broadcast and reduce channels do not appear correctly+-- In versions 1.5.0-1.6.0, the broadcast and reduce channels do not appear correctly -- in the traces. manyToOneChannel' :: (Monoid a, MonadCHP m) => ChanOpts a -> m (ManyToOneChannel a) manyToOneChannel' = const manyToOneChannel --TODO -- | Added in version 1.5.0. -- --- In version 1.5.0, the broadcast and reduce channels do not appear correctly+-- In versions 1.5.0-1.6.0, the broadcast and reduce channels do not appear correctly -- in the traces. manyToAnyChannel' :: (Monoid a, MonadCHP m) => ChanOpts a -> m (ManyToAnyChannel a) manyToAnyChannel' = const manyToAnyChannel --TODO
Control/Concurrent/CHP/Common.hs view
@@ -123,7 +123,7 @@ -- 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 ()-map f in_ out = forever (readChannel in_ >>= (return . f) >>= writeChannel out)+map f in_ out = forever (readChannel in_ >>= writeChannel out . f) `onPoisonRethrow` (poison in_ >> poison out) -- | Like 'map', but applies the transformation strictly before sending on@@ -131,7 +131,7 @@ -- -- Added in version 1.1.0. map' :: NFData b => (a -> b) -> Chanin a -> Chanout b -> CHP ()-map' f in_ out = forever (readChannel in_ >>= (return . f) >>= writeChannelStrict out)+map' f in_ out = forever (readChannel in_ >>= writeChannelStrict out . f) `onPoisonRethrow` (poison in_ >> poison out) -- | Forever reads in a value, and then based on applying the given function@@ -219,7 +219,7 @@ -- | 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 ()-joinList ins out = (forever $ runParallel [readChannel c | c <- ins] >>= writeChannel out+joinList ins out = (forever $ runParMapM readChannel ins >>= writeChannel out ) `onPoisonRethrow` (poisonAll ins >> poison out)
Control/Concurrent/CHP/Monad.hs view
@@ -164,7 +164,9 @@ -- 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 ()+--+-- The type of this function was generalised in CHP 1.6.0.+stop :: CHP a stop = liftPoison $ AltableT (Right [(stopGuard, liftIO hang)]) (liftIO hang) where -- Strangely, I can't work out a good way to actually implement stop.@@ -173,5 +175,6 @@ -- CPU. Throwing an exception would be caught and terminate the -- process, which is not the desired behaviour. The only thing I can think -- to do is to repeatedly wait for a very long time.- hang :: IO ()- hang = forever $ threadDelay maxBound+ hang :: IO a+ hang = do forever $ threadDelay maxBound+ return undefined
Control/Concurrent/CHP/Traces/Base.hs view
@@ -115,8 +115,8 @@ nameEvent (t, c) = liftM (++ suffix) $ getName prefix c where (prefix, suffix) = case t of- ChannelComm x -> ("_c", if null x then "" else "[" ++ x ++ "]")- BarrierSync x -> ("_b", if null x then "" else "[" ++ x ++ "]")+ ChannelComm x -> ("_c", if null x then "" else "." ++ x)+ BarrierSync x -> ("_b", if null x then "" else "." ++ x) ClockSync st -> ("_t", ':' : st) nameEvent' :: Ord u => RecordedEvent u -> State (Map.Map u String) (RecordedEvent String)
chp.cabal view
@@ -1,5 +1,5 @@ Name: chp-Version: 1.5.1+Version: 1.6.0 Synopsis: An implementation of concurrency ideas from Communicating Sequential Processes License: BSD3 License-file: LICENSE@@ -27,6 +27,7 @@ Control.Concurrent.CHP.Alt Control.Concurrent.CHP.Arrow Control.Concurrent.CHP.Barriers+ Control.Concurrent.CHP.Behaviours Control.Concurrent.CHP.BroadcastChannels Control.Concurrent.CHP.Buffers Control.Concurrent.CHP.Channels