-- Communicating Haskell Processes. -- Copyright (c) 2008, 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 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: -- -- * 'Control.Concurrent.CHP.Monad.skip' -- -- * 'Control.Concurrent.CHP.Monad.stop' -- -- * 'Control.Concurrent.CHP.Monad.waitFor' -- -- * Reading from a channel (including extended reads): that is, calls to 'Control.Concurrent.CHP.Channels.readChannel' -- and 'Control.Concurrent.CHP.Channels.extReadChannel' -- -- * Writing to a channel (including extended writes): that is, calls to 'Control.Concurrent.CHP.Channels.writeChannel' -- and 'Control.Concurrent.CHP.Channels.extWriteChannel' -- -- * Synchronising on a barrier (using 'Control.Concurrent.CHP.Barriers.syncBarrier') -- -- * An alting construct (that is, you can nest alts) such as 'alt', 'priAlt' (or -- the operator versions) -- -- * A sequential composition, if the first event supports alting (i.e. is in this -- list) -- -- * A call to 'every', which joins together several items (see the documentation -- on 'every'). -- -- Examples of events that do /NOT/ support alting are: -- -- * Enrolling and resigning with a barrier -- -- * Poisoning a channel -- -- * Processes composed in parallel (using 'runParallel', etc) -- -- * Any lifted IO event -- -- * Creating channels, barriers, etc -- -- * Claiming a shared channel (yet...) -- -- It is not easily possible to represent this at the type level (while still -- making CHP easy to use). Therefore it is left to you to not try to alt -- over something that does not support it. Given how much of the library -- does support alting, that should hopefully be straightforward. -- -- Here are some examples of using alting: -- -- * Wait for an integer channel, or 1 second to elapse: -- -- > liftM Just (readChannel c) <-> (waitFor 1000000 >> return Nothing) -- -- * Check if a channel is ready, otherwise return immediately. Note that you must use the -- alt operator with priority here, otherwise your skip guard might be chosen, -- even when the channel is ready. -- -- > liftM Just (readChannel c) (skip >> return Nothing) -- -- * Wait for input from one of two (identically typed) channels -- -- > readChannel c0 <-> readChannel c1 -- -- * Check if a channel is ready; if so send, it on, otherwise return immediately: -- -- > (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 (alt, (<->), priAlt, (), every, (<&>)) where import Control.Concurrent.STM import Control.Monad.State import Control.Monad.Trans import Data.List import Data.Maybe import System.IO import Control.Concurrent.CHP.Base import Control.Concurrent.CHP.Event import Control.Concurrent.CHP.Guard import Control.Concurrent.CHP.Parallel import Control.Concurrent.CHP.Poison import Control.Concurrent.CHP.Traces.Base -- | 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 alt = priAlt -- | 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 'Control.Concurrent.CHP.Monad.waitFor') -- -- 3. dummy guards ('Control.Concurrent.CHP.Monad.skip' and 'Control.Concurrent.CHP.Monad.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 'Control.Concurrent.CHP.Monad.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 priAlt items = (liftPoison $ priAlt' $ map wrapPoison items) >>= checkPoison -- | 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 (<->) a b = alt [a,b] -- | 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 () a b = priAlt [a,b] infixl infixl <-> infixl <&> -- | 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: -- -- * A call to 'Control.Concurrent.CHP.Channels.readChannel' (or 'Control.Concurrent.CHP.Channels.extReadChannel'). -- -- * A call to 'Control.Concurrent.CHP.Channels.writeChannel' (or 'Control.Concurrent.CHP.Channels.extWriteChannel'). -- -- * 'Control.Concurrent.CHP.Monad.skip', the always-ready guard. -- -- * 'Control.Concurrent.CHP.Monad.stop', the never-ready guard (will cause the whole 'every' to never be ready, -- since 'every' has to wait for all guards). -- -- * A call to 'Control.Concurrent.CHP.Monad.syncBarrier'. -- -- * A sequential composition where the first item is one of the things in this -- list. -- -- * A call to 'every' (you can nest 'every' blocks inside each other). -- -- Timeouts (e.g. 'Control.Concurrent.CHP.Monad.waitFor') are currently not supported. You can always get another -- process to synchronise on an event with you once a certain time has passed. -- -- Note also that you cannot put an 'alt' inside an 'every'. So you cannot say: -- -- > 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] 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] where (gs, bodies) = unzip $ map (pullOutAltable . wrapPoison) xs blankEvent :: Guard -> Guard blankEvent (EventGuard _ rec act es) = EventGuard [] rec act es blankEvent g = g 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" -- | 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 (<&>) :: forall a b. CHP a -> CHP b -> CHP (a, b) (<&>) a b = every [a >>= return . Left, b >>= return . Right] >>= return . merge where merge :: [Either a b] -> (a, b) merge [Left x, Right y] = (x, y) merge [Right y, Left x] = (x, y) merge _ = error "Invalid merge possibility in <&>" -- ALTing is implemented as follows in CHP. The CHP monad has [Int] in its -- state. When you choose between N events, you form one body, that pulls -- the next number from the head of the state and executes the body for the -- event corresponding to that index. Nested ALTs prepend to the list. -- So for example, if you choose between: -- -- > (a <-> b) <-> c -- -- The overall number corresponding to a is [0,0], b is [0,1], c is [1]. The -- outer choice peels off the head of the list. On 1 it executes c; on 0 it -- descends to the nested choice, which takes the next number in the list and -- executes a or b given 0 or 1 respectively. -- -- If an event is poisoned, an integer (of arbitrary value) is /appended/ to -- the list. Thus when an event-based guard is executed, if the list in the -- state is non-empty, it knows it has been poisoned. -- -- I did try implementing this in a more functional manner, making each event -- in the monad take [Int] and return the body, rather than using state. However, -- I had some memory efficiency problems so I went with the state-monad-based -- approach instead. priAlt' :: forall a. [CHP' a] -> CHP' 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) 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) 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 -- 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 -- 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 in liftIO $ do hPutStrLn stderr err ioError $ userError err | otherwise = do earliestReady <- liftIO $ atomically checkNormalGuards tv <- liftIO . atomically $ newTVar Nothing pid <- getProcessId (_, tr) <- get mn <- liftIO . atomically $ do ret <- enableEvents tv pid (maybe id take earliestReady [(x,y,z) | (_,x,y,z)<-eventGuards]) (isNothing earliestReady) maybe (return ()) (\(_,es) -> recordEventLast (nub es) tr) ret return ret case (mn, earliestReady) of -- 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 -- 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 -- 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 if wasAltingBarrier then recordEvent (findEventAssoc ns) >> storeChoice ns -- 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) case mn' of -- An event overrides our non-event choice: Just bns -> recordEvent (findEventAssoc bns) >> storeChoice bns -- Go with the original option, no events -- became ready: Nothing -> storeChoice ns where waitAlting :: TVar (Maybe [Int]) -> STM [Int] 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)"