git.iko.soy/distributed-process
on master help sign in
Merge pull request #86 from simonmar/match-chan
38c9011Edsko de Vries
distributed-process-simplelocalnet/src/Control/Distributed/Process/Backend/SimpleLocalnet.hs+13−18
@@ -104,7 +104,7 @@ import Data.Foldable (forM_)
104import Data.Typeable (Typeable)
104import Data.Typeable (Typeable)
105import Control.Applicative ((<$>))
105import Control.Applicative ((<$>))
106import Control.Exception (throw)
106import Control.Exception (throw)
107−import Control.Monad (forever, forM, replicateM, when, replicateM_)
107+import Control.Monad (forever, replicateM, replicateM_)
108import Control.Monad.IO.Class (liftIO)
108import Control.Monad.IO.Class (liftIO)
109import Control.Concurrent (forkIO, threadDelay, ThreadId)
109import Control.Concurrent (forkIO, threadDelay, ThreadId)
110import Control.Concurrent.MVar (MVar, newMVar, readMVar, modifyMVar_)
110import Control.Concurrent.MVar (MVar, newMVar, readMVar, modifyMVar_)
@@ -116,17 +116,13 @@ import Control.Distributed.Process
116 , WhereIsReply(..)
116 , WhereIsReply(..)
117 , whereis
117 , whereis
118 , whereisRemoteAsync
118 , whereisRemoteAsync
119− , registerRemoteAsync
120− , reregisterRemoteAsync
121 , getSelfPid
119 , getSelfPid
122 , register
120 , register
123 , reregister
121 , reregister
124 , expect
122 , expect
125 , nsendRemote
123 , nsendRemote
126 , receiveWait
124 , receiveWait
127− , receiveTimeout
128 , match
125 , match
129− , matchIf
130 , processNodeId
126 , processNodeId
131 , monitorNode
127 , monitorNode
132 , monitor
128 , monitor
@@ -138,7 +134,6 @@ import Control.Distributed.Process
138 , receiveChan
134 , receiveChan
139 , nsend
135 , nsend
140 , SendPort
136 , SendPort
141− , RegisterReply(..)
142 , bracket
137 , bracket
143 , try
138 , try
144 , send
139 , send
@@ -180,21 +175,21 @@ data BackendState = BackendState {
180initializeBackend :: N.HostName -> N.ServiceName -> RemoteTable -> IO Backend
175initializeBackend :: N.HostName -> N.ServiceName -> RemoteTable -> IO Backend
181initializeBackend host port rtable = do
176initializeBackend host port rtable = do
182 mTransport <- NT.createTransport host port NT.defaultTCPParameters
177 mTransport <- NT.createTransport host port NT.defaultTCPParameters
183− (recv, send) <- initMulticast "224.0.0.99" 9999 1024
178+ (recv, sendp) <- initMulticast "224.0.0.99" 9999 1024
184 (_, backendState) <- fixIO $ \ ~(tid, _) -> do
179 (_, backendState) <- fixIO $ \ ~(tid, _) -> do
185 backendState <- newMVar BackendState
180 backendState <- newMVar BackendState
186 { _localNodes = []
181 { _localNodes = []
187 , _peers = Set.empty
182 , _peers = Set.empty
188 , discoveryDaemon = tid
183 , discoveryDaemon = tid
189 }
184 }
190− tid' <- forkIO $ peerDiscoveryDaemon backendState recv send
185+ tid' <- forkIO $ peerDiscoveryDaemon backendState recv sendp
191 return (tid', backendState)
186 return (tid', backendState)
192 case mTransport of
187 case mTransport of
193 Left err -> throw err
188 Left err -> throw err
194 Right transport ->
189 Right transport ->
195 let backend = Backend {
190 let backend = Backend {
196 newLocalNode = apiNewLocalNode transport rtable backendState
191 newLocalNode = apiNewLocalNode transport rtable backendState
197− , findPeers = apiFindPeers send backendState
192+ , findPeers = apiFindPeers sendp backendState
198 , redirectLogsHere = apiRedirectLogsHere backend
193 , redirectLogsHere = apiRedirectLogsHere backend
199 }
194 }
200 in return backend
195 in return backend
@@ -214,8 +209,8 @@ apiFindPeers :: (PeerDiscoveryMsg -> IO ())
214 -> MVar BackendState
209 -> MVar BackendState
215 -> Int
210 -> Int
216 -> IO [NodeId]
211 -> IO [NodeId]
217−apiFindPeers send backendState delay = do
212+apiFindPeers sendfn backendState delay = do
218− send PeerDiscoveryRequest
213+ sendfn PeerDiscoveryRequest
219 threadDelay delay
214 threadDelay delay
220 Set.toList . (^. peers) <$> readMVar backendState
215 Set.toList . (^. peers) <$> readMVar backendState
221
216
@@ -238,14 +233,14 @@ peerDiscoveryDaemon :: MVar BackendState
238 -> IO (PeerDiscoveryMsg, N.SockAddr)
233 -> IO (PeerDiscoveryMsg, N.SockAddr)
239 -> (PeerDiscoveryMsg -> IO ())
234 -> (PeerDiscoveryMsg -> IO ())
240 -> IO ()
235 -> IO ()
241−peerDiscoveryDaemon backendState recv send = forever go
236+peerDiscoveryDaemon backendState recv sendfn = forever go
242 where
237 where
243 go = do
238 go = do
244 (msg, _) <- recv
239 (msg, _) <- recv
245 case msg of
240 case msg of
246 PeerDiscoveryRequest -> do
241 PeerDiscoveryRequest -> do
247 nodes <- (^. localNodes) <$> readMVar backendState
242 nodes <- (^. localNodes) <$> readMVar backendState
248− forM_ nodes $ send . PeerDiscoveryReply . Node.localNodeId
243+ forM_ nodes $ sendfn . PeerDiscoveryReply . Node.localNodeId
249 PeerDiscoveryReply nid ->
244 PeerDiscoveryReply nid ->
250 modifyMVar_ backendState $ return . (peers ^: Set.insert nid)
245 modifyMVar_ backendState $ return . (peers ^: Set.insert nid)
251
246
@@ -272,8 +267,8 @@ apiRedirectLogsHere _backend slavecontrollers = do
272 -- Wait for the replies
267 -- Wait for the replies
273 replicateM_ (length slavecontrollers) $ do
268 replicateM_ (length slavecontrollers) $ do
274 receiveWait
269 receiveWait
275− [ match (\(RedirectLogsReply from ok) -> return ())
270+ [ match (\(RedirectLogsReply {}) -> return ())
276− , match (\m@(NodeMonitorNotification {}) -> return ())
271+ , match (\(NodeMonitorNotification {}) -> return ())
277 ]
272 ]
278
273
279--------------------------------------------------------------------------------
274--------------------------------------------------------------------------------
@@ -335,8 +330,8 @@ slaveController = do
335 ok <- case (r :: Either ProcessRegistrationException ()) of
330 ok <- case (r :: Either ProcessRegistrationException ()) of
336 Right _ -> return True
331 Right _ -> return True
337 Left _ -> do
332 Left _ -> do
338− r <- try (register "logger" loggerPid)
333+ s <- try (register "logger" loggerPid)
339− case (r :: Either ProcessRegistrationException ()) of
334+ case (s :: Either ProcessRegistrationException ()) of
340 Right _ -> return True
335 Right _ -> return True
341 Left _ -> return False
336 Left _ -> return False
342 pid <- getSelfPid
337 pid <- getSelfPid
@@ -359,7 +354,7 @@ findSlaves backend = do
359 $ \_ -> do
354 $ \_ -> do
360
355
361 -- fire off whereis requests
356 -- fire off whereis requests
362− forM nodes $ \nid -> whereisRemoteAsync nid "slaveController"
357+ forM_ nodes $ \nid -> whereisRemoteAsync nid "slaveController"
363
358
364 -- Wait for the replies
359 -- Wait for the replies
365 catMaybes <$> replicateM (length nodes) (
360 catMaybes <$> replicateM (length nodes) (
distributed-process/src/Control/Distributed/Process.hs+2
@@ -39,6 +39,7 @@ module Control.Distributed.Process
39 , matchUnknown
39 , matchUnknown
40 , AbstractMessage(..)
40 , AbstractMessage(..)
41 , matchAny
41 , matchAny
42+ , matchChan
42 -- * Process management
43 -- * Process management
43 , spawn
44 , spawn
44 , call
45 , call
@@ -189,6 +190,7 @@ import Control.Distributed.Process.Internal.Primitives
189 , matchUnknown
190 , matchUnknown
190 , AbstractMessage(..)
191 , AbstractMessage(..)
191 , matchAny
192 , matchAny
193+ , matchChan
192 -- Process management
194 -- Process management
193 , terminate
195 , terminate
194 , ProcessTerminationException(..)
196 , ProcessTerminationException(..)
distributed-process/src/Control/Distributed/Process/Internal/CQueue.hs+173−64
@@ -1,8 +1,9 @@
1-- | Concurrent queue for single reader, single writer
1-- | Concurrent queue for single reader, single writer
2−{-# LANGUAGE MagicHash, UnboxedTuples #-}
2+{-# LANGUAGE MagicHash, UnboxedTuples, PatternGuards #-}
3module Control.Distributed.Process.Internal.CQueue
3module Control.Distributed.Process.Internal.CQueue
4 ( CQueue
4 ( CQueue
5 , BlockSpec(..)
5 , BlockSpec(..)
6+ , MatchOn(..)
6 , newCQueue
7 , newCQueue
7 , enqueue
8 , enqueue
8 , dequeue
9 , dequeue
@@ -12,14 +13,17 @@ module Control.Distributed.Process.Internal.CQueue
12import Prelude hiding (length, reverse)
13import Prelude hiding (length, reverse)
13import Control.Concurrent.STM
14import Control.Concurrent.STM
14 ( atomically
15 ( atomically
16+ , STM
15 , TChan
17 , TChan
18+ , tryReadTChan
16 , newTChan
19 , newTChan
17 , writeTChan
20 , writeTChan
18 , readTChan
21 , readTChan
19− , tryReadTChan
22+ , orElse
23+ , retry
20 )
24 )
21import Control.Applicative ((<$>), (<*>))
25import Control.Applicative ((<$>), (<*>))
22−import Control.Exception (mask, onException)
26+import Control.Exception (mask_, onException)
23import System.Timeout (timeout)
27import System.Timeout (timeout)
24import Control.Distributed.Process.Internal.StrictMVar
28import Control.Distributed.Process.Internal.StrictMVar
25 ( StrictMVar(StrictMVar)
29 ( StrictMVar(StrictMVar)
@@ -29,9 +33,9 @@ import Control.Distributed.Process.Internal.StrictMVar
29 )
33 )
30import Control.Distributed.Process.Internal.StrictList
34import Control.Distributed.Process.Internal.StrictList
31 ( StrictList(..)
35 ( StrictList(..)
32− , reverse
33− , reverse'
34 )
36 )
37+import qualified Control.Distributed.Process.Internal.StrictList as S
38+import Data.Maybe (fromJust)
35import GHC.MVar (MVar(MVar))
39import GHC.MVar (MVar(MVar))
36import GHC.IO (IO(IO))
40import GHC.IO (IO(IO))
37import GHC.Prim (mkWeak#)
41import GHC.Prim (mkWeak#)
@@ -55,71 +59,176 @@ data BlockSpec =
55 | Blocking
59 | Blocking
56 | Timeout Int
60 | Timeout Int
57
61
62+data MatchOn m a
63+ = MatchMsg (m -> Maybe a)
64+ | MatchChan (STM a)
65+
66+type MatchChunks m a = [Either [m -> Maybe a] [STM a]]
67+
68+chunkMatches :: [MatchOn m a] -> MatchChunks m a
69+chunkMatches [] = []
70+chunkMatches (MatchMsg m : ms) = Left (m : chk) : chunkMatches rest
71+ where (chk, rest) = spanMatchMsg ms
72+chunkMatches (MatchChan r : ms) = Right (r : chk) : chunkMatches rest
73+ where (chk, rest) = spanMatchChan ms
74+
75+spanMatchMsg :: [MatchOn m a] -> ([m -> Maybe a], [MatchOn m a])
76+spanMatchMsg [] = ([],[])
77+spanMatchMsg (m : ms)
78+ | MatchMsg msg <- m = (msg:msgs, rest)
79+ | otherwise = ([], m:ms)
80+ where !(msgs,rest) = spanMatchMsg ms
81+
82+spanMatchChan :: [MatchOn m a] -> ([STM a], [MatchOn m a])
83+spanMatchChan [] = ([],[])
84+spanMatchChan (m : ms)
85+ | MatchChan stm <- m = (stm:stms, rest)
86+ | otherwise = ([], m:ms)
87+ where !(stms,rest) = spanMatchChan ms
88+
58-- | Dequeue an element
89-- | Dequeue an element
59--
90--
60-- The timeout (if any) is applied only to waiting for incoming messages, not
91-- The timeout (if any) is applied only to waiting for incoming messages, not
61-- to checking messages that have already arrived
92-- to checking messages that have already arrived
62−dequeue :: forall a b.
93+dequeue :: forall m a.
63− CQueue a -- ^ Queue
94+ CQueue m -- ^ Queue
64 -> BlockSpec -- ^ Blocking behaviour
95 -> BlockSpec -- ^ Blocking behaviour
65− -> [a -> Maybe b] -- ^ List of matches
96+ -> [MatchOn m a] -- ^ List of matches
66− -> IO (Maybe b) -- ^ 'Nothing' only on timeout
97+ -> IO (Maybe a) -- ^ 'Nothing' only on timeout
67−dequeue (CQueue arrived incoming) blockSpec matches = go
98+dequeue (CQueue arrived incoming) blockSpec matchons =
99+ case blockSpec of
100+ Timeout n -> timeout n $ fmap fromJust go
101+ _other ->
102+ case chunks of
103+ [Right ports] -> -- channels only, this is easy:
104+ case blockSpec of
105+ NonBlocking -> atomically $ waitChans ports (return Nothing)
106+ _ -> atomically $ waitChans ports retry
107+ -- no onException needed
108+ _other -> go
68 where
109 where
69− go :: IO (Maybe b)
110+ chunks = chunkMatches matchons
70− go = mask $ \restore -> do
111+
112+ go = mask_ $ do
71− arr <- takeMVar arrived
113+ arr <- takeMVar arrived
72− -- We first check the arrived messages. If we get interrupted during this
114+ let grabNew xs = do
73− -- search, we just put the MVar back (we haven't read from the Chan yet)
115+ r <- atomically $ tryReadTChan incoming
74− (arr', mb) <- onException (restore (checkArrived Nil arr))
116+ case r of
75− (putMVar arrived arr)
117+ Nothing -> return xs
118+ Just x -> grabNew (Snoc xs x)
119+ arr' <- grabNew arr
120+ goCheck chunks arr'
121+
122+ waitChans ports on_block =
123+ foldr orElse on_block (map (fmap Just) ports)
124+
125+ --
126+ -- First check the MatchChunks against the messages already in the
127+ -- mailbox. For channel matches, we do a non-blocking check at
128+ -- this point.
129+ --
130+ goCheck :: MatchChunks m a
131+ -> StrictList m -- messages to check, in this order
132+ -> IO (Maybe a)
133+
134+ goCheck [] old = goWait old
135+
136+ goCheck (Right ports : rest) old = do
137+ r <- atomically $ waitChans ports (return Nothing) -- does not block
138+ case r of
139+ Just _ -> returnOld old r
140+ Nothing -> goCheck rest old
141+
142+ goCheck (Left matches : rest) old = do
143+ -- checkArrived might in principle take arbitrary time, so
144+ -- we ought to call restore and use an exception handler. However,
145+ -- the check is usually fast (just a comparison), and the overhead
146+ -- of passing around restore and setting up exception handlers is
147+ -- high. So just don't use expensive matchIfs!
148+ case checkArrived matches old of
149+ (old', Just r) -> returnOld old' (Just r)
150+ (old', Nothing) -> goCheck rest old'
151+ -- use the result list, which is now left-biased
152+
153+ --
154+ -- Construct an STM transaction that looks at the relevant channels
155+ -- in the correct order.
156+ --
157+ mkSTM :: MatchChunks m a -> STM (Either m a)
158+ mkSTM [] = retry
159+ mkSTM (Left _ : rest)
160+ = fmap Left (readTChan incoming) `orElse` mkSTM rest
161+ mkSTM (Right ports : rest)
162+ = foldr orElse (mkSTM rest) (map (fmap Right) ports)
163+
164+ waitIncoming :: IO (Maybe (Either m a))
76− case (mb, blockSpec) of
165+ waitIncoming = case blockSpec of
77− (Just b, _) -> do
166+ NonBlocking -> atomically $ fmap Just stm `orElse` return Nothing
78− putMVar arrived arr'
167+ _ -> atomically $ fmap Just stm
79− return (Just b)
168+ where
80− (Nothing, NonBlocking) ->
169+ stm = mkSTM chunks
81− checkNonBlocking arr'
170+
82− (Nothing, Blocking) ->
171+ --
83− Just <$> checkBlocking arr'
172+ -- The initial pass didn't find a message, so now we go into blocking
84− (Nothing, Timeout n) ->
173+ -- mode.
85− timeout n $ checkBlocking arr'
174+ --
86−
175+ -- Contents of 'arrived' from now on is (old ++ new), and
87− -- We reverse the accumulator on return only if we find a match
176+ -- messages that arrive are snocced onto new.
88− checkArrived :: StrictList a -> StrictList a -> IO (StrictList a, Maybe b)
177+ --
89− checkArrived acc Nil = return (acc, Nothing)
178+ goWait old = do
90− checkArrived acc (Cons x xs) =
179+ r <- waitIncoming `onException` putMVar arrived old
180+ case r of
181+ -- Nothing => non-blocking and no message
182+ Nothing -> returnOld old Nothing
183+ Just e -> case e of
184+ --
185+ -- Left => message arrived in the process mailbox. We now have to
186+ -- run through the MatchChunks checking each one, because we might
187+ -- have a situation where the first chunk fails to match and the
188+ -- second chunk is a channel match and there *is* a message in the
189+ -- channel. In that case the channel wins.
190+ --
191+ Left m -> goCheck1 chunks m old
192+ --
193+ -- Right => message arrived on a channel first
194+ --
195+ Right a -> returnOld old (Just a)
196+
197+ --
198+ -- A message arrived in the process inbox; check the MatchChunks for
199+ -- a valid match.
200+ --
201+ goCheck1 :: MatchChunks m a
202+ -> m -- single message to check
203+ -> StrictList m -- old messages we have already checked
204+ -> IO (Maybe a)
205+
206+ goCheck1 [] m old = goWait (Snoc old m)
207+
208+ goCheck1 (Right ports : rest) m old = do
209+ r <- atomically $ waitChans ports (return Nothing) -- does not block
91− case check x of
210+ case r of
92− Just y -> return (reverse' acc xs, Just y)
211+ Nothing -> goCheck1 rest m old
93− Nothing -> checkArrived (Cons x acc) xs
212+ Just _ -> returnOld (Snoc old m) r
94−
213+
95− -- If we call checkBlocking there may or may not be a timeout
214+ goCheck1 (Left matches : rest) m old = do
96− checkBlocking :: StrictList a -> IO b
215+ case checkMatches matches m of
97− checkBlocking acc = do
216+ Nothing -> goCheck1 rest m old
98− -- readTChan is a blocking call, and hence is interruptable. If it is
217+ Just p -> returnOld old (Just p)
99− -- interrupted, we put the value of the accumulator in 'arrived'
218+
100− -- (as opposed to the original value), so that no messages get lost
219+ -- a common pattern for putting back the arrived queue at the end
101− -- (hence the low-level structure using mask rather than modifyMVar)
220+ returnOld :: StrictList m -> Maybe a -> IO (Maybe a)
102− x <- onException (atomically $ readTChan incoming)
221+ returnOld old r = do putMVar arrived old; return r
103− (putMVar arrived $ reverse acc)
222+
104− case check x of
223+ -- as a side-effect, this left-biases the list
105− Nothing -> checkBlocking (Cons x acc)
224+ checkArrived :: [m -> Maybe a] -> StrictList m -> (StrictList m, Maybe a)
106− Just y -> putMVar arrived (reverse acc) >> return y
225+ checkArrived matches list = S.foldr f (Nil, Nothing) list
107−
226+ where
108− -- checkNonBlocking is only called if there is no timeout
227+ f x (rest,r)
109− checkNonBlocking :: StrictList a -> IO (Maybe b)
228+ | Just y <- checkMatches matches x = (rest, Just y)
110− checkNonBlocking acc = do
229+ | otherwise = (Cons x rest, r)
111− -- tryReadTChan is *not* interruptible
230+
112− mx <- atomically $ tryReadTChan incoming
113− case mx of
114− Nothing -> putMVar arrived (reverse acc) >> return Nothing
115− Just x -> case check x of
116− Nothing -> checkNonBlocking (Cons x acc)
117− Just y -> putMVar arrived (reverse acc) >> return (Just y)
118−
119− check :: a -> Maybe b
120− check = checkMatches matches
121−
122− checkMatches :: [a -> Maybe b] -> a -> Maybe b
231+ checkMatches :: [m -> Maybe a] -> m -> Maybe a
123 checkMatches [] _ = Nothing
232 checkMatches [] _ = Nothing
124 checkMatches (m:ms) a = case m a of Nothing -> checkMatches ms a
233 checkMatches (m:ms) a = case m a of Nothing -> checkMatches ms a
125 Just b -> Just b
234 Just b -> Just b
distributed-process/src/Control/Distributed/Process/Internal/Primitives.hs+13−5
@@ -21,6 +21,7 @@ module Control.Distributed.Process.Internal.Primitives
21 , matchUnknown
21 , matchUnknown
22 , AbstractMessage(..)
22 , AbstractMessage(..)
23 , matchAny
23 , matchAny
24+ , matchChan
24 -- * Process management
25 -- * Process management
25 , terminate
26 , terminate
26 , ProcessTerminationException(..)
27 , ProcessTerminationException(..)
@@ -106,7 +107,11 @@ import Control.Concurrent.STM
106 , readTVar
107 , readTVar
107 , writeTVar
108 , writeTVar
108 )
109 )
109−import Control.Distributed.Process.Internal.CQueue (dequeue, BlockSpec(..))
110+import Control.Distributed.Process.Internal.CQueue
111+ ( dequeue
112+ , BlockSpec(..)
113+ , MatchOn(..)
114+ )
110import Control.Distributed.Process.Serializable (Serializable, fingerprint)
115import Control.Distributed.Process.Serializable (Serializable, fingerprint)
111import Data.Accessor ((^.), (^:), (^=))
116import Data.Accessor ((^.), (^:), (^=))
112import Control.Distributed.Static (Closure, Static)
117import Control.Distributed.Static (Closure, Static)
@@ -258,7 +263,7 @@ mergePortsRR = \ps -> do
258--------------------------------------------------------------------------------
263--------------------------------------------------------------------------------
259
264
260-- | Opaque type used in 'receiveWait' and 'receiveTimeout'
265-- | Opaque type used in 'receiveWait' and 'receiveTimeout'
261−newtype Match b = Match { unMatch :: Message -> Maybe (Process b) }
266+newtype Match b = Match { unMatch :: MatchOn Message (Process b) }
262
267
263-- | Test the matches in order against each message in the queue
268-- | Test the matches in order against each message in the queue
264receiveWait :: [Match b] -> Process b
269receiveWait :: [Match b] -> Process b
@@ -281,13 +286,16 @@ receiveTimeout t ms = do
281 Nothing -> return Nothing
286 Nothing -> return Nothing
282 Just proc -> Just <$> proc
287 Just proc -> Just <$> proc
283
288
289+matchChan :: ReceivePort a -> (a -> Process b) -> Match b
290+matchChan p fn = Match $ MatchChan (fmap fn (receiveSTM p))
291+
284-- | Match against any message of the right type
292-- | Match against any message of the right type
285match :: forall a b. Serializable a => (a -> Process b) -> Match b
293match :: forall a b. Serializable a => (a -> Process b) -> Match b
286match = matchIf (const True)
294match = matchIf (const True)
287
295
288-- | Match against any message of the right type that satisfies a predicate
296-- | Match against any message of the right type that satisfies a predicate
289matchIf :: forall a b. Serializable a => (a -> Bool) -> (a -> Process b) -> Match b
297matchIf :: forall a b. Serializable a => (a -> Bool) -> (a -> Process b) -> Match b
290−matchIf c p = Match $ \msg ->
298+matchIf c p = Match $ MatchMsg $ \msg ->
291 case messageFingerprint msg == fingerprint (undefined :: a) of
299 case messageFingerprint msg == fingerprint (undefined :: a) of
292 True | c decoded -> Just (p decoded)
300 True | c decoded -> Just (p decoded)
293 where
301 where
@@ -304,7 +312,7 @@ data AbstractMessage = AbstractMessage {
304
312
305-- | Match against an arbitrary message
313-- | Match against an arbitrary message
306matchAny :: forall b. (AbstractMessage -> Process b) -> Match b
314matchAny :: forall b. (AbstractMessage -> Process b) -> Match b
307−matchAny p = Match $ Just . p . abstract
315+matchAny p = Match $ MatchMsg $ Just . p . abstract
308 where
316 where
309 abstract :: Message -> AbstractMessage
317 abstract :: Message -> AbstractMessage
310 abstract msg = AbstractMessage {
318 abstract msg = AbstractMessage {
@@ -319,7 +327,7 @@ matchAny p = Match $ Just . p . abstract
319
327
320-- | Remove any message from the queue
328-- | Remove any message from the queue
321matchUnknown :: Process b -> Match b
329matchUnknown :: Process b -> Match b
322−matchUnknown = Match . const . Just
330+matchUnknown p = Match $ MatchMsg (const (Just p))
323
331
324--------------------------------------------------------------------------------
332--------------------------------------------------------------------------------
325-- Process management --
333-- Process management --
distributed-process/src/Control/Distributed/Process/Internal/StrictList.hs+19−17
@@ -1,25 +1,27 @@
1-- | Spine and element strict list
1-- | Spine and element strict list
2module Control.Distributed.Process.Internal.StrictList
2module Control.Distributed.Process.Internal.StrictList
3− ( StrictList(Cons, Nil)
3+ ( StrictList(..)
4− , length
4+ , append
5− , reverse
5+ , foldr
6− , reverse'
7 ) where
6 ) where
8
7
9−import Prelude hiding (length, reverse)
8+import Prelude hiding (length, reverse, foldr)
10
9
11-- | Strict list
10-- | Strict list
11+data StrictList a
12−data StrictList a = Cons !a !(StrictList a) | Nil
12+ = Cons !a !(StrictList a)
13+ | Nil
14+ | Snoc !(StrictList a) !a
15+ | Append !(StrictList a) !(StrictList a)
13
16
14−length :: StrictList a -> Int
17+append :: StrictList a -> StrictList a -> StrictList a
15−length Nil = 0
18+append Nil l = l
16−length (Cons _ xs) = 1 + length xs
19+append l Nil = l
20+append l1 l2 = l1 `Append` l2
17
21
18−-- | Reverse a strict list
19−reverse :: StrictList a -> StrictList a
22+foldr :: (a -> b -> b) -> b -> StrictList a -> b
20−reverse xs = reverse' xs Nil
23+foldr f c0 xs0 = go xs0 c0
21−
24+ where go Nil c = c
22−-- | @reverseStrict' xs ys@ is 'reverse xs ++ ys' if they were lists
25+ go (Cons x xs) c = f x (go xs c)
23−reverse' :: StrictList a -> StrictList a -> StrictList a
26+ go (Snoc xs x) c = go xs (f x c)
24−reverse' Nil ys = ys
27+ go (Append xs ys) c = go xs (go ys c)
25−reverse' (Cons x xs) ys = reverse' xs (Cons x ys)
distributed-process/tests/TestReceive.hsnew file+149
@@ -0,0 +1,149 @@
1+{-# LANGUAGE TemplateHaskell, DeriveDataTypeable #-}
2+{-# OPTIONS_GHC -Wall #-}
3+import Control.Distributed.Process
4+import Control.Distributed.Process.Closure
5+
6+import Control.Monad
7+import Text.Printf
8+import Data.DeriveTH
9+import Data.Binary
10+import Data.Typeable
11+
12+import DistribUtils
13+
14+import Test.Framework (defaultMain, testGroup)
15+import Test.Framework.Providers.HUnit
16+import Test.HUnit
17+
18+-- Tests:
19+
20+-- 1. 2 matchChans, receive on each one
21+-- 2. matchChan/matchIf, receive on each one
22+-- 3. matchIf/matchChan, receive on each one
23+-- 4. matchIf/matchChan/matchIf, receive on each one
24+
25+recTest1 :: ReceivePort ()
26+ -> SendPort String
27+ -> ReceivePort String -> ReceivePort String
28+ -> Process ()
29+recTest1 wait sync r1 r2 = do
30+ forever $ do
31+ receiveChan wait
32+ r <- receiveWait
33+ [ matchChan r1 $ \s -> return ("received1 " ++ s)
34+ , matchChan r2 $ \s -> return ("received2 " ++ s)
35+ ]
36+ sendChan sync r
37+
38+recTest2 :: ReceivePort ()
39+ -> SendPort String
40+ -> ReceivePort String -> ReceivePort String
41+ -> Process ()
42+recTest2 wait sync r1 r2 = do
43+ forever $ do
44+ receiveChan wait
45+ r <- receiveWait
46+ [ matchChan r1 $ \s -> return ("received1 " ++ s)
47+ , matchIf (== "foo") $ \s -> return ("received2 " ++ s)
48+ ]
49+ sendChan sync r
50+
51+recTest3 :: ReceivePort ()
52+ -> SendPort String
53+ -> ReceivePort String -> ReceivePort String
54+ -> Process ()
55+recTest3 wait sync r1 r2 = do
56+ forever $ do
57+ receiveChan wait
58+ r <- receiveWait
59+ [ matchIf (== "foo") $ \s -> return ("received1 " ++ s)
60+ , matchChan r1 $ \s -> return ("received2 " ++ s)
61+ ]
62+ sendChan sync r
63+
64+recTest4 :: ReceivePort ()
65+ -> SendPort String
66+ -> ReceivePort String -> ReceivePort String
67+ -> Process ()
68+recTest4 wait sync r1 r2 = do
69+ forever $ do
70+ receiveChan wait
71+ r <- receiveWait
72+ [ matchIf (== "foo") $ \s -> return ("received1 " ++ s)
73+ , matchChan r1 $ \s -> return ("received2 " ++ s)
74+ , matchIf (== "bar") $ \s -> return ("received3 " ++ s)
75+ ]
76+ sendChan sync r
77+
78+
79+master :: [NodeId] -> Process ()
80+master peers = do
81+
82+ (waits,waitr) <- newChan
83+ (syncs,syncr) <- newChan
84+ let go expect = do
85+ sendChan waits ()
86+ r <- receiveChan syncr
87+ liftIO $ print (r,expect, r == expect)
88+ liftIO $ r @?= expect
89+
90+ liftIO $ putStrLn "---- Test 1 ----"
91+ (s1,r1) <- newChan
92+ (s2,r2) <- newChan
93+ p <- spawnLocal (recTest1 waitr syncs r1 r2)
94+
95+ sendChan s1 "a" >> go "received1 a"
96+ sendChan s2 "b" >> go "received2 b"
97+ sendChan s1 "a" >> sendChan s2 "b" >> go "received1 a"
98+ go "received2 b"
99+
100+ kill p "BANG"
101+
102+ liftIO $ putStrLn "\n---- Test 2 ----"
103+ (s1,r1) <- newChan
104+ (s2,r2) <- newChan
105+ p <- spawnLocal (recTest2 waitr syncs r1 r2)
106+
107+ sendChan s1 "a" >> go "received1 a"
108+ send p "foo" >> go "received2 foo"
109+ sendChan s1 "a" >> send p "foo" >> go "received1 a"
110+ sendChan s1 "a" >> send p "bar" >> go "received1 a"
111+ go "received2 foo"
112+
113+ kill p "BANG"
114+
115+ liftIO $ putStrLn "\n---- Test 3 ----"
116+ (s1,r1) <- newChan
117+ (s2,r2) <- newChan
118+ p <- spawnLocal (recTest3 waitr syncs r1 r2)
119+
120+ sendChan s1 "a" >> go "received2 a"
121+ send p "foo" >> go "received1 foo"
122+ sendChan s1 "a" >> send p "foo" >> go "received1 foo"
123+ sendChan s1 "a" >> send p "bar" >> go "received2 a"
124+ go "received2 a"
125+
126+ kill p "BANG"
127+
128+ liftIO $ putStrLn "\n---- Test 4 ----"
129+ (s1,r1) <- newChan
130+ (s2,r2) <- newChan
131+ p <- spawnLocal (recTest4 waitr syncs r1 r2)
132+
133+ sendChan s1 "a" >> go "received2 a"
134+ send p "foo" >> go "received1 foo"
135+ send p "bar" >> go "received3 bar"
136+ sendChan s1 "a" >> send p "foo" >> go "received1 foo"
137+ send p "bar" >> go "received2 a"
138+ send p "foo" >> go "received1 foo" >> go "received3 bar"
139+
140+ kill p "BANG"
141+
142+ terminate
143+
144+
145+-- NB. test doesn't work, because failure exceptions don't get
146+-- propagated.
147+
148+main :: IO ()
149+main = defaultMain [ testCase "all" $ distribMain master id ]