git.iko.soy/distributed-process
on master help sign in
distributed-process-tests/distributed-process-tests.cabal+1−1
since splitsnapshots
unifiedsplit
@@ -6,7 +6,7 @@ description: Tests and test suite for Cloud Haskell libraries.
6license: BSD3
6license: BSD3
7license-file: LICENSE
7license-file: LICENSE
8Author: Duncan Coutts, Nicolas Wu, Edsko de Vries
8Author: Duncan Coutts, Nicolas Wu, Edsko de Vries
9−Maintainer: Facundo Domínguez <facundo.dominguez@tweag.io>
9+Maintainer: Tim Watson <watson.timothy@gmail.com>
10copyright: Well-Typed LLP
10copyright: Well-Typed LLP
11category: Control, Cloud Haskell
11category: Control, Cloud Haskell
12build-type: Simple
12build-type: Simple
distributed-process-tests/src/Control/Distributed/Process/Tests/CH.hs+110−32
@@ -44,13 +44,13 @@ import Control.Distributed.Process.Internal.Types
44import Control.Distributed.Process.Node
44import Control.Distributed.Process.Node
45import Control.Distributed.Process.Debug
45import Control.Distributed.Process.Debug
46import Control.Distributed.Process.Management.Internal.Types
46import Control.Distributed.Process.Management.Internal.Types
47−import Control.Distributed.Process.Tests.Internal.Utils (shouldBe)
47+import Control.Distributed.Process.Tests.Internal.Utils (shouldBe, pause)
48import Control.Distributed.Process.Serializable (Serializable)
48import Control.Distributed.Process.Serializable (Serializable)
49−
49+import Data.Maybe (isNothing, isJust)
50import Test.HUnit (Assertion, assertBool, assertFailure)
50import Test.HUnit (Assertion, assertBool, assertFailure)
51import Test.Framework (Test, testGroup)
51import Test.Framework (Test, testGroup)
52import Test.Framework.Providers.HUnit (testCase)
52import Test.Framework.Providers.HUnit (testCase)
53−import Control.Rematch hiding (match)
53+import Control.Rematch hiding (match, isNothing, isJust)
54import Control.Rematch.Run (Match(..))
54import Control.Rematch.Run (Match(..))
55
55
56newtype Ping = Ping ProcessId
56newtype Ping = Ping ProcessId
@@ -87,10 +87,9 @@ verifyClient :: String -> MVar Bool -> IO ()
87verifyClient s b = takeMVar b >>= assertBool s
87verifyClient s b = takeMVar b >>= assertBool s
88
88
89expectPing :: MVar Bool -> Process ()
89expectPing :: MVar Bool -> Process ()
90−expectPing mv = expect >>= liftIO . putMVar mv . checkPing
90+expectPing mv = expect >>= liftIO . putMVar mv . checkPing
91 where
91 where
92 checkPing (Ping _) = True
92 checkPing (Ping _) = True
93− checkPing _ = False
94
93
95-- | Quick and dirty synchronous version of whereisRemoteAsync
94-- | Quick and dirty synchronous version of whereisRemoteAsync
96whereisRemote :: NodeId -> String -> Process (Maybe ProcessId)
95whereisRemote :: NodeId -> String -> Process (Maybe ProcessId)
@@ -240,10 +239,10 @@ testPing TestTransport{..} = do
240 pid <- getSelfPid
239 pid <- getSelfPid
241 replicateM_ numPings $ do
240 replicateM_ numPings $ do
242 send pingServer (Pong pid)
241 send pingServer (Pong pid)
243− p <- expect
242+ p <- expectTimeout 3000000
244 case p of
243 case p of
245− Ping _ -> return ()
244+ Just (Ping _) -> return ()
246− _ -> die "Unexpected message"
245+ Nothing -> die "Failed to receive Ping"
247
246
248 putMVar clientDone ()
247 putMVar clientDone ()
249
248
@@ -560,7 +559,7 @@ testMergeChannels TestTransport{..} = do
560 ss <- liftIO $ mapM readMVar vs
559 ss <- liftIO $ mapM readMVar vs
561 case ss of
560 case ss of
562 [sa, sb, sc] ->
561 [sa, sb, sc] ->
563− mapM_ ((>> liftIO (threadDelay 10000)) . uncurry sendChan)
562+ mapM_ ((>> pause 10000) . uncurry sendChan)
564 [ -- a, b, c
563 [ -- a, b, c
565 (sa, 'a')
564 (sa, 'a')
566 , (sb, 'b')
565 , (sb, 'b')
@@ -736,7 +735,6 @@ testRemoteRegistry :: TestTransport -> Assertion
736testRemoteRegistry TestTransport{..} = do
735testRemoteRegistry TestTransport{..} = do
737 node1 <- newLocalNode testTransport initRemoteTable
736 node1 <- newLocalNode testTransport initRemoteTable
738 node2 <- newLocalNode testTransport initRemoteTable
737 node2 <- newLocalNode testTransport initRemoteTable
739− done <- newEmptyMVar
740
738
741 pingServer <- forkProcess node1 ping
739 pingServer <- forkProcess node1 ping
742 deadProcess <- forkProcess node1 (return ())
740 deadProcess <- forkProcess node1 (return ())
@@ -751,6 +749,7 @@ testRemoteRegistry TestTransport{..} = do
751
749
752 pid <- verifyWhereIsRemote nid1 "ping"
750 pid <- verifyWhereIsRemote nid1 "ping"
753 liftIO $ assertBool "Expected pindServer to match pid" $ pingServer == pid
751 liftIO $ assertBool "Expected pindServer to match pid" $ pingServer == pid
752+
754 us <- getSelfPid
753 us <- getSelfPid
755 nsendRemote nid1 "ping" (Pong us)
754 nsendRemote nid1 "ping" (Pong us)
756 receiveWait [
755 receiveWait [
@@ -760,22 +759,22 @@ testRemoteRegistry TestTransport{..} = do
760 -- test that if process was not registered Nothing is returned
759 -- test that if process was not registered Nothing is returned
761 -- in owner field.
760 -- in owner field.
762 registerRemoteAsync nid1 "dead" deadProcess
761 registerRemoteAsync nid1 "dead" deadProcess
763− receiveWait [
764− matchIf (\(RegisterReply label' False Nothing) -> "dead" == label')
762+ receiveWait [ matchIf (\(RegisterReply label' f mPid) -> "dead" == label')
765− (\(RegisterReply _ _ _) -> return ()) ]
763+ (\(RegisterReply _ f mPid) -> return (not f && isNothing mPid))
764+ ] >>= liftIO . assertBool "Expected False Nothing in RegisterReply"
765+
766 registerRemoteAsync nid1 "ping" deadProcess
766 registerRemoteAsync nid1 "ping" deadProcess
767 receiveWait [
767 receiveWait [
768− matchIf (\(RegisterReply label' False (Just pid'')) ->
768+ matchIf (\(RegisterReply label' False mPid) ->
769− "ping" == label' && pid'' == pingServer)
769+ "ping" == label' && isJust mPid)
770− (\(RegisterReply _ _ _) -> return ()) ]
770+ (\(RegisterReply _ f (Just pid'')) -> return (not f && pid'' == pingServer))
771+ ] >>= liftIO . assertBool "Expected False and (Just alreadyRegisteredPid) in RegisterReply"
772+
771 unregisterRemoteAsync nid1 "dead"
773 unregisterRemoteAsync nid1 "dead"
772 receiveWait [
774 receiveWait [
773− matchIf (\(RegisterReply label' False Nothing) ->
775+ matchIf (\(RegisterReply label' _ _) -> "dead" == label')
774− "dead" == label' && pid == pingServer)
776+ (\(RegisterReply _ f mPid) -> return (not f && isNothing mPid))
775− (\(RegisterReply _ _ _) -> return ()) ]
777+ ] >>= liftIO . assertBool "Expected False and Nothing in RegisterReply"
776− liftIO $ putMVar done ()
777−
778− takeMVar done
779
778
780testRemoteRegistryRemoteProcess :: TestTransport -> Assertion
779testRemoteRegistryRemoteProcess :: TestTransport -> Assertion
781testRemoteRegistryRemoteProcess TestTransport{..} = do
780testRemoteRegistryRemoteProcess TestTransport{..} = do
@@ -1273,6 +1272,76 @@ testReceiveChanFeatures TestTransport{..} = do
1273
1272
1274 takeMVar done
1273 takeMVar done
1275
1274
1275+testChanLifecycle :: TestTransport -> Assertion
1276+testChanLifecycle TestTransport{..} = let delay = 3000000 in do
1277+ result <- newEmptyMVar
1278+ tchMV <- newEmptyMVar
1279+ localNode <- newLocalNode testTransport initRemoteTable
1280+ runProcess localNode $ do
1281+
1282+ pid <- spawnLocal $ do tCh <- newChan :: Process (SendPort (), ReceivePort ())
1283+ liftIO $ putMVar tchMV tCh
1284+ expect :: Process ()
1285+ let (sp, _) = tCh
1286+ sendChan sp ()
1287+ expect :: Process ()
1288+
1289+ mRefPid <- monitor pid
1290+
1291+ cPid <- spawnLocal $ do
1292+ (sp', rp) <- liftIO $ takeMVar tchMV
1293+ -- say "picked up our test channel"
1294+ send pid ()
1295+ -- say "told pid to continue"
1296+ res <- receiveChanTimeout delay rp
1297+ case res of
1298+ Nothing -> say "initial chan () missing!" >> (liftIO $ putMVar result False)
1299+ Just () -> do mr <- monitor pid
1300+ pause 10000
1301+ -- say "sending pid a second () will cause it to exit"
1302+ send pid ()
1303+
1304+ -- say "make sure we see a DOWN notification for pid having stopped"
1305+ receiveWait [ match (\(_ :: ProcessMonitorNotification) -> return ()) ]
1306+
1307+ -- now that pid has died, the send port should be useless...
1308+ liftIO $ putMVar tchMV (sp', rp)
1309+
1310+ -- let's verify that we do not see the message from our
1311+ -- parent process on the channel, once pid has died...
1312+ recv <- receiveChanTimeout delay rp
1313+ -- say $ "finished waiting for second (), writing result" ++ (show recv)
1314+ liftIO $ putMVar result $ isNothing recv
1315+
1316+ mRefCPid <- monitor cPid
1317+
1318+ receiveWait
1319+ [ matchIf (\(ProcessMonitorNotification r _ _) -> r == mRefPid)
1320+ (\_ -> return ())
1321+ ]
1322+
1323+ -- say "seen first pid die..."
1324+
1325+ (sendPort, _) <- liftIO $ takeMVar tchMV
1326+ sendChan sendPort ()
1327+ -- say "sent () after owning pid died"
1328+
1329+ -- let cPid know we've written to the channel...
1330+ send cPid ()
1331+
1332+ receiveWait
1333+ [ matchIf (\(ProcessMonitorNotification r _ _) -> r == mRefCPid)
1334+ (\_ -> return ())
1335+ ]
1336+
1337+ -- say "seen both pids die now..."
1338+
1339+ -- and wait on the result back in IO land...
1340+ testRes <- takeMVar result
1341+ -- runProcess localNode $ say "got result..."
1342+ assertBool "Expected sending on the channel to fail, but received data!" testRes
1343+
1344+
1276testKillLocal :: TestTransport -> Assertion
1345testKillLocal :: TestTransport -> Assertion
1277testKillLocal TestTransport{..} = do
1346testKillLocal TestTransport{..} = do
1278 localNode <- newLocalNode testTransport initRemoteTable
1347 localNode <- newLocalNode testTransport initRemoteTable
@@ -1487,20 +1556,24 @@ testRegistryMonitoring :: TestTransport -> Assertion
1487testRegistryMonitoring TestTransport{..} = do
1556testRegistryMonitoring TestTransport{..} = do
1488 node1 <- newLocalNode testTransport initRemoteTable
1557 node1 <- newLocalNode testTransport initRemoteTable
1489 node2 <- newLocalNode testTransport initRemoteTable
1558 node2 <- newLocalNode testTransport initRemoteTable
1490− waitH <- newEmptyMVar
1491
1559
1492 let nid = localNodeId node2
1560 let nid = localNodeId node2
1493 pid <- forkProcess node1 $ do
1561 pid <- forkProcess node1 $ do
1562+ self <- getSelfPid
1494− getSelfPid >>= runUntilRegistered nid
1563+ runUntilRegistered nid self
1495− liftIO $ takeMVar waitH
1564+ say $ (show self) ++ " registered as " ++ regName
1565+ expect :: Process ()
1566+ say $ (show self) ++ " exiting normally"
1496
1567
1497 runProcess node2 $ do
1568 runProcess node2 $ do
1498 register regName pid
1569 register regName pid
1570+ say $ regName ++ " registered to " ++ show pid
1499 res <- whereis regName
1571 res <- whereis regName
1572+ send pid ()
1573+ say $ " sent finish signal to " ++ show pid
1500 us <- getSelfPid
1574 us <- getSelfPid
1501− liftIO $ do
1502− putMVar waitH ()
1503− assertBool "expected (Just pid)" $ res == (Just pid)
1575+ liftIO $ assertBool "expected (Just pid)" $ res == (Just pid)
1576+
1504
1577
1505 -- This delay isn't essential!
1578 -- This delay isn't essential!
1506 -- The test case passes perfectly fine without it (feel free to comment out
1579 -- The test case passes perfectly fine without it (feel free to comment out
@@ -1525,7 +1598,9 @@ testRegistryMonitoring TestTransport{..} = do
1525 regHere <- newEmptyMVar
1598 regHere <- newEmptyMVar
1526 runProcess node2 $ whereis regName >>= liftIO . putMVar regHere
1599 runProcess node2 $ whereis regName >>= liftIO . putMVar regHere
1527 res <- takeMVar regHere
1600 res <- takeMVar regHere
1601+ case res of
1602+ Nothing -> return ()
1528− assertBool "expected Nothing, but process still registered" (res == Nothing)
1603+ Just pid -> assertBool ("expected Nothing, but got " ++ show pid) False
1529
1604
1530 where
1605 where
1531 runUntilRegistered nid us = do
1606 runUntilRegistered nid us = do
@@ -1537,11 +1612,13 @@ testRegistryMonitoring TestTransport{..} = do
1537
1612
1538 delayUntilMaybeUnregistered nid p = do
1613 delayUntilMaybeUnregistered nid p = do
1539 whereisRemoteAsync nid regName
1614 whereisRemoteAsync nid regName
1540− receiveTimeout 20000000 {- 20 sec delay -} [
1615+ res <- receiveTimeout 20000000 {- 20 sec delay -} [
1541− matchIf (\(WhereIsReply n p) -> n == regName && p == Nothing)
1616+ matchIf (\(WhereIsReply n p) -> n == regName && isNothing p)
1542 (const $ return ())
1617 (const $ return ())
1543 ]
1618 ]
1619+ case res of
1544− return ()
1620+ Just () -> return ()
1621+ Nothing -> delayUntilMaybeUnregistered nid p
1545
1622
1546 regName = "testRegisterRemote"
1623 regName = "testRegisterRemote"
1547
1624
@@ -1737,6 +1814,7 @@ tests testtrans = return [
1737 , testCase "MatchMessageUnwrap" (testMatchMessageWithUnwrap testtrans)
1814 , testCase "MatchMessageUnwrap" (testMatchMessageWithUnwrap testtrans)
1738 , testCase "ReceiveChanTimeout" (testReceiveChanTimeout testtrans)
1815 , testCase "ReceiveChanTimeout" (testReceiveChanTimeout testtrans)
1739 , testCase "ReceiveChanFeatures" (testReceiveChanFeatures testtrans)
1816 , testCase "ReceiveChanFeatures" (testReceiveChanFeatures testtrans)
1817+ , testCase "ChanLifecycle" (testChanLifecycle testtrans)
1740 , testCase "KillLocal" (testKillLocal testtrans)
1818 , testCase "KillLocal" (testKillLocal testtrans)
1741 , testCase "KillRemote" (testKillRemote testtrans)
1819 , testCase "KillRemote" (testKillRemote testtrans)
1742 , testCase "Die" (testDie testtrans)
1820 , testCase "Die" (testDie testtrans)
distributed-process-tests/src/Control/Distributed/Process/Tests/Internal/Utils.hs+3−1
@@ -91,6 +91,7 @@ import Test.HUnit (Assertion, assertFailure)
91import Test.HUnit.Base (assertBool)
91import Test.HUnit.Base (assertBool)
92
92
93import GHC.Generics
93import GHC.Generics
94+import System.Timeout (timeout)
94
95
95-- | A mutable cell containing a test result.
96-- | A mutable cell containing a test result.
96type TestResult a = MVar a
97type TestResult a = MVar a
@@ -233,7 +234,8 @@ testProcessReport pid = do
233tryRunProcess :: LocalNode -> Process () -> IO ()
234tryRunProcess :: LocalNode -> Process () -> IO ()
234tryRunProcess node p = do
235tryRunProcess node p = do
235 tid <- liftIO myThreadId
236 tid <- liftIO myThreadId
237+ void $ timeout (1000000 * 60 * 5 :: Int) $
236− runProcess node $ catch p (\e -> liftIO $ throwTo tid (e::SomeException))
238+ runProcess node $ catch p (\e -> liftIO $ throwTo tid (e::SomeException))
237
239
238tryForkProcess :: LocalNode -> Process () -> IO ProcessId
240tryForkProcess :: LocalNode -> Process () -> IO ProcessId
239tryForkProcess node p = do
241tryForkProcess node p = do
distributed-process-tests/src/Control/Distributed/Process/Tests/Mx.hs+156−125
@@ -1,9 +1,10 @@
1−{-# LANGUAGE DeriveGeneric #-}
1+{-# LANGUAGE DeriveGeneric #-}
2+{-# LANGUAGE ParallelListComp #-}
3+
2module Control.Distributed.Process.Tests.Mx (tests) where
4module Control.Distributed.Process.Tests.Mx (tests) where
3
5
4import Control.Distributed.Process.Tests.Internal.Utils
6import Control.Distributed.Process.Tests.Internal.Utils
5import Network.Transport.Test (TestTransport(..))
7import Network.Transport.Test (TestTransport(..))
6−import Control.Exception (SomeException)
7import Control.Distributed.Process hiding (bracket, finally, try)
8import Control.Distributed.Process hiding (bracket, finally, try)
8import Control.Distributed.Process.Internal.Types
9import Control.Distributed.Process.Internal.Types
9 ( ProcessExitException(..)
10 ( ProcessExitException(..)
@@ -15,6 +16,7 @@ import qualified Control.Distributed.Process.UnsafePrimitives as Unsafe
15 , nsend
16 , nsend
16 , nsendRemote
17 , nsendRemote
17 , usend
18 , usend
19+ , sendChan
18 )
20 )
19import Control.Distributed.Process.Management
21import Control.Distributed.Process.Management
20 ( MxEvent(..)
22 ( MxEvent(..)
@@ -35,8 +37,8 @@ import Control.Monad (void, unless)
35import Control.Monad.Catch(finally, bracket, try)
37import Control.Monad.Catch(finally, bracket, try)
36import Control.Rematch (equalTo)
38import Control.Rematch (equalTo)
37import Data.Binary
39import Data.Binary
38−import Data.List (find, sort)
40+import Data.List (find, sort, intercalate)
39−import Data.Maybe (isJust, fromJust, isNothing)
41+import Data.Maybe (isJust, fromJust, isNothing, fromMaybe, catMaybes)
40import Data.Typeable
42import Data.Typeable
41import GHC.Generics hiding (from)
43import GHC.Generics hiding (from)
42#if ! MIN_VERSION_base(4,6,0)
44#if ! MIN_VERSION_base(4,6,0)
@@ -64,7 +66,7 @@ awaitExit pid =
64 where
66 where
65 withMonitorRef p = bracket (monitor p) unmonitor
67 withMonitorRef p = bracket (monitor p) unmonitor
66
68
67−testAgentBroadcast :: TestResult () -> Process ()
69+testAgentBroadcast :: TestResult (Maybe ()) -> Process ()
68testAgentBroadcast result = do
70testAgentBroadcast result = do
69 (resultSP, resultRP) <- newChan :: Process (SendPort (), ReceivePort ())
71 (resultSP, resultRP) <- newChan :: Process (SendPort (), ReceivePort ())
70
72
@@ -79,10 +81,10 @@ testAgentBroadcast result = do
79 mxNotify ()
81 mxNotify ()
80 -- Once the publisher has seen our message, it will broadcast the Publish
82 -- Once the publisher has seen our message, it will broadcast the Publish
81 -- and the consumer will see that and send the result to our typed channel.
83 -- and the consumer will see that and send the result to our typed channel.
82− stash result =<< receiveChan resultRP
84+ stash result =<< receiveChanTimeout 10000000 resultRP
83
85
84− kill publisher "finished" >> awaitExit publisher
86+ kill publisher "finished"
85− kill consumer "finished" >> awaitExit consumer
87+ kill consumer "finished"
86
88
87testAgentDualInput :: TestResult (Maybe Int) -> Process ()
89testAgentDualInput :: TestResult (Maybe Int) -> Process ()
88testAgentDualInput result = do
90testAgentDualInput result = do
@@ -97,6 +99,8 @@ testAgentDualInput result = do
97 else mxReady)
99 else mxReady)
98 ]
100 ]
99
101
102+ mRef <- monitor s
103+
100 mxNotify (1 :: Int)
104 mxNotify (1 :: Int)
101 nsend "sum-agent" (3 :: Int)
105 nsend "sum-agent" (3 :: Int)
102 mxNotify (2 :: Int)
106 mxNotify (2 :: Int)
@@ -104,7 +108,10 @@ testAgentDualInput result = do
104 mxNotify (5 :: Int)
108 mxNotify (5 :: Int)
105
109
106 stash result =<< receiveChanTimeout 10000000 rp
110 stash result =<< receiveChanTimeout 10000000 rp
107− awaitExit s
111+ died <- receiveTimeout 10000000 [
112+ matchIf (\(ProcessMonitorNotification r _ _) -> r == mRef) (const $ return True)
113+ ]
114+ died `shouldBe` equalTo (Just True)
108
115
109testAgentPrioritisation :: TestResult [String] -> Process ()
116testAgentPrioritisation :: TestResult [String] -> Process ()
110testAgentPrioritisation result = do
117testAgentPrioritisation result = do
@@ -118,10 +125,10 @@ testAgentPrioritisation result = do
118 (sp, rp) <- newChan
125 (sp, rp) <- newChan
119 s <- mxAgent (MxAgentId name) ["first"] [
126 s <- mxAgent (MxAgentId name) ["first"] [
120 mxSink (\(s :: String) -> do
127 mxSink (\(s :: String) -> do
121− mxUpdateLocal ((s:))
128+ mxUpdateLocal (s:)
122 st <- mxGetLocal
129 st <- mxGetLocal
123 case length st of
130 case length st of
124− n | n == 5 -> do liftMX $ sendChan sp st
131+ n | n == 5 -> do liftMX $ sendChan sp (sort st)
125 mxDeactivate "finished"
132 mxDeactivate "finished"
126 _ -> mxReceive -- go to the mailbox
133 _ -> mxReceive -- go to the mailbox
127 )
134 )
@@ -132,7 +139,7 @@ testAgentPrioritisation result = do
132 mxNotify "fourth"
139 mxNotify "fourth"
133 nsend name "fifth"
140 nsend name "fifth"
134
141
135− stash result . sort =<< receiveChan rp
142+ stash result . fromMaybe [] =<< receiveChanTimeout 10000000 rp
136 awaitExit s
143 awaitExit s
137
144
138testAgentMailboxHandling :: TestResult (Maybe ()) -> Process ()
145testAgentMailboxHandling :: TestResult (Maybe ()) -> Process ()
@@ -145,12 +152,19 @@ testAgentMailboxHandling result = do
145 nsend "mailbox-agent" ()
152 nsend "mailbox-agent" ()
146
153
147 stash result =<< receiveChanTimeout 1000000 rp
154 stash result =<< receiveChanTimeout 1000000 rp
148− kill agent "finished" >> awaitExit agent
155+ kill agent "finished"
149
156
150testAgentEventHandling :: TestResult Bool -> Process ()
157testAgentEventHandling :: TestResult Bool -> Process ()
151testAgentEventHandling result = do
158testAgentEventHandling result = do
152− let initState = [] :: [MxEvent]
159+ us <- getSelfPid
160+ -- because this test is a bit racy, let's ensure it can't run indefinitely
161+ timer <- spawnLocal $ do
162+ pause (1000000 * 60 * 5)
163+ -- okay we've waited 5 mins, let's kill the test off if it's stuck...
164+ stash result False
165+ kill us "Test Timed Out"
153
166
167+ let initState = [] :: [MxEvent]
154 (rc, rs) <- newChan
168 (rc, rs) <- newChan
155 agentPid <- mxAgent (MxAgentId "lifecycle-listener-agent") initState [
169 agentPid <- mxAgent (MxAgentId "lifecycle-listener-agent") initState [
156 (mxSink $ \ev -> do
170 (mxSink $ \ev -> do
@@ -183,8 +197,7 @@ testAgentEventHandling result = do
183 -- TODO: yes, this is racy, but we're at the mercy of the scheduler here...
197 -- TODO: yes, this is racy, but we're at the mercy of the scheduler here...
184 faff 2000000 rs
198 faff 2000000 rs
185
199
186− _ <- monitor agentPid
187− (sp, rp) <- newChan
200+ (sp, rp) <- newChan :: Process (SendPort (), ReceivePort ())
188
201
189 pid <- spawnLocal $ expect >>= sendChan sp
202 pid <- spawnLocal $ expect >>= sendChan sp
190
203
@@ -193,9 +206,11 @@ testAgentEventHandling result = do
193 monitor pid
206 monitor pid
194
207
195 send pid ()
208 send pid ()
196− () <- receiveChan rp
209+ rct <- receiveChanTimeout 10000000 rp
210+ unless (isJust rct) $ die "No response on channel"
197
211
198− receiveWait [ match (\(ProcessMonitorNotification _ _ _) -> return ()) ]
212+ pmn <- receiveTimeout 2000000 [ match (\ProcessMonitorNotification{} -> return ()) ]
213+ unless (isJust pmn) $ die "No monitor notification arrived"
199
214
200 -- TODO: yes, this is racy, but we're at the mercy of the scheduler here...
215 -- TODO: yes, this is racy, but we're at the mercy of the scheduler here...
201 faff 2000000 rs
216 faff 2000000 rs
@@ -204,17 +219,16 @@ testAgentEventHandling result = do
204 mxNotify (MxSpawned pid, replyTo)
219 mxNotify (MxSpawned pid, replyTo)
205 mxNotify (MxProcessDied pid DiedNormal, replyTo)
220 mxNotify (MxProcessDied pid DiedNormal, replyTo)
206
221
207− seenAlive <- receiveChan reply
222+ seenAlive <- receiveChanTimeout 10000000 reply
208− seenDead <- receiveChan reply
223+ seenDead <- receiveChanTimeout 10000000 reply
209
224
210− stash result $ seenAlive && seenDead
225+ stash result $ foldr (&&) True $ catMaybes [seenAlive, seenDead]
211− kill agentPid "test-complete" >> awaitExit agentPid
226+ kill timer "test-complete"
227+ kill agentPid "test-complete"
212 where
228 where
213 faff delay port = do
229 faff delay port = do
214 res <- receiveChanTimeout delay port
230 res <- receiveChanTimeout delay port
215− unless (isNothing res) $ do
231+ unless (isNothing res) $ pause delay
216− pause delay
217− faff delay port
218
232
219testMxRegEvents :: TestResult () -> Process ()
233testMxRegEvents :: TestResult () -> Process ()
220testMxRegEvents result = do
234testMxRegEvents result = do
@@ -261,7 +275,6 @@ testMxRegEvents result = do
261 reg3 `shouldBe` equalTo (Just (label, p1))
275 reg3 `shouldBe` equalTo (Just (label, p1))
262
276
263 mapM_ (flip kill $ "test-complete") [agent, p1, p2]
277 mapM_ (flip kill $ "test-complete") [agent, p1, p2]
264− awaitExit agent
265
278
266testMxRegMon :: LocalNode -> TestResult () -> Process ()
279testMxRegMon :: LocalNode -> TestResult () -> Process ()
267testMxRegMon remoteNode result = do
280testMxRegMon remoteNode result = do
@@ -319,67 +332,99 @@ testMxRegMon remoteNode result = do
319 evts `shouldContain` (Just (label1, p1))
332 evts `shouldContain` (Just (label1, p1))
320 evts `shouldContain` (Just (label2, p1))
333 evts `shouldContain` (Just (label2, p1))
321
334
322− kill agent "test-complete" >> awaitExit agent
335+ kill agent "test-complete"
323
336
324ensure :: Process () -> Process () -> Process ()
337ensure :: Process () -> Process () -> Process ()
325ensure = flip finally
338ensure = flip finally
326
339
327testNSend :: (String -> () -> Process ())
340testNSend :: (String -> () -> Process ())
328 -> Maybe LocalNode
341 -> Maybe LocalNode
329− -> TestResult Bool
330 -> Process ()
342 -> Process ()
331−testNSend op n r = testMxSend n r $ \p1 sink -> do
343+testNSend op n = do
344+ us <- getSelfPid
332 let delay = 5000000
345 let delay = 5000000
333− let label = "testMxSend"
346+ let label = "testMxSend" ++ (show us)
334 let isValid = isValidLabel n label
347 let isValid = isValidLabel n label
335
348
349+ testMxSend n label $ \p1 sink -> do
336− register label p1
350+ register label p1
337− reg1 <- receiveChanTimeout delay sink
351+ reg1 <- receiveChanTimeout delay sink
338− case reg1 of
352+ case reg1 of
339− Just (MxRegistered pd lb)
353+ Just (MxRegistered pd lb)
340− | pd == p1 && isValid lb -> return ()
354+ | pd == p1 && isValid lb -> return ()
341− _ -> die $ "Reg-Failed: " ++ show reg1
355+ _ -> die $ "Reg-Failed: " ++ show reg1
342
356
343− op label ()
357+ op label ()
344
358
345− us <- getSelfPid
346− sent <- receiveChanTimeout delay sink
359+ sent <- receiveChanTimeout delay sink
347− case sent of
360+ case sent of
348− Just (MxSentToName lb by _)
361+ Just (MxSentToName lb by _)
349− | by == us && isValid lb -> return True
362+ | by == us && isValid lb -> return True
350− _ -> die $ "Send-Failed: " ++ show sent
363+ _ -> die $ "Send-Failed: " ++ show sent
351
364
352 where
365 where
353− isValidLabel n l1 l2
366+ isValidLabel nd l1 l2
354 | l2 == l2 = True
367 | l2 == l2 = True
355− | isJust n = l2 == l1 ++ "@" ++ show (localNodeId $ fromJust n)
368+ | isJust nd = l2 == l1 ++ "@" ++ show (localNodeId $ fromJust nd)
356 | otherwise = False
369 | otherwise = False
357
370
358testSend :: (ProcessId -> () -> Process ())
371testSend :: (ProcessId -> () -> Process ())
359 -> Maybe LocalNode
372 -> Maybe LocalNode
360− -> TestResult Bool
361 -> Process ()
373 -> Process ()
362−testSend op n r = testMxSend n r $ \p1 sink -> do
374+testSend op n = do
375+ us <- getSelfPid
376+ let label = "testMxSend" ++ (show us)
377+ testMxSend n label $ \p1 sink -> do
378+ -- initiate a send
379+ op p1 ()
380+
381+ -- verify the management event
382+ sent <- receiveChanTimeout 5000000 sink
383+ case sent of
384+ Just (MxSent pidTo pidFrom _)
385+ | pidTo == p1 && pidFrom == us -> return True
386+ _ -> return False
387+
388+testChan :: (SendPort () -> () -> Process ())
389+ -> Maybe LocalNode
390+ -> Process ()
391+testChan op n = testMxSend n "" $ \p1 sink -> do
392+
393+ us <- getSelfPid
394+ send p1 us
395+
396+ cleared <- receiveChanTimeout 2000000 sink
397+ case cleared of
398+ Just (MxSent pidTo pidFrom _)
399+ | pidTo == p1 && pidFrom == us -> return ()
400+ _ -> die "Received uncleared Mx Event"
401+
402+ chan <- receiveTimeout 5000000 [ match return ]
403+ let ch' = fromMaybe (error "No reply chan received") chan
404+
363 -- initiate a send
405 -- initiate a send
364− op p1 ()
406+ op ch' ()
365
407
366 -- verify the management event
408 -- verify the management event
367− us <- getSelfPid
368 sent <- receiveChanTimeout 5000000 sink
409 sent <- receiveChanTimeout 5000000 sink
369 case sent of
410 case sent of
370− Just (MxSent pidTo pidFrom _)
411+ Just (MxSentToPort sId spId _)
371− | pidTo == p1 && pidFrom == us -> return True
412+ | sId == us && spId == sendPortId ch' -> return True
372− _ -> return False
413+ _ -> return False
373
414
374type SendTest = ProcessId -> ReceivePort MxEvent -> Process Bool
415type SendTest = ProcessId -> ReceivePort MxEvent -> Process Bool
375
416
376−testMxSend :: Maybe LocalNode -> TestResult Bool -> SendTest -> Process ()
417+testMxSend :: Maybe LocalNode -> String -> SendTest -> Process ()
377−testMxSend mNode result test = do
418+testMxSend mNode label test = do
378 us <- getSelfPid
419 us <- getSelfPid
420+ (sp, rp) <- newChan
379 (chan, sink) <- newChan
421 (chan, sink) <- newChan
380 agent <- mxAgent (MxAgentId $ agentLabel us) () [
422 agent <- mxAgent (MxAgentId $ agentLabel us) () [
381 mxSink $ \ev -> do
423 mxSink $ \ev -> do
382 case ev of
424 case ev of
425+ m@(MxSentToPort _ cid _)
426+ | cid /= sendPortId chan
427+ && cid /= sendPortId sp -> liftMX $ sendChan chan m
383 m@(MxSent _ fromPid _)
428 m@(MxSent _ fromPid _)
384 | fromPid == us -> liftMX $ sendChan chan m
429 | fromPid == us -> liftMX $ sendChan chan m
385 m@(MxSentToName _ fromPid _)
430 m@(MxSentToName _ fromPid _)
@@ -390,29 +435,39 @@ testMxSend mNode result test = do
390 mxReady
435 mxReady
391 ]
436 ]
392
437
393− (sp, rp) <- newChan
394 case mNode of
438 case mNode of
395 Nothing -> void $ spawnLocal (proc sp)
439 Nothing -> void $ spawnLocal (proc sp)
396 Just remoteNode -> void $ liftIO $ forkProcess remoteNode $ proc sp
440 Just remoteNode -> void $ liftIO $ forkProcess remoteNode $ proc sp
397
441
398− p1 <- receiveChan rp
442+ p1 <- receiveChanTimeout 2000000 rp
443+ unless (isJust p1) $ die "Timed out waiting for ProcessId"
399− res <- try (test p1 sink)
444+ res <- try $ test (fromJust p1) sink
400− case res of
445+
401− Left (ProcessExitException _ m) -> (liftIO $ putStrLn $ "SomeException-" ++ show m) >> stash result False
402− Right tr -> stash result tr
403 kill agent "bye"
446 kill agent "bye"
404− kill p1 "bye"
447+ kill (fromJust p1) "bye"
448+
449+ case res of
450+ Left (ProcessExitException _ m) -> (liftIO $ putStrLn $ "SomeException-" ++ show m) >> die m
451+ Right tr -> tr `shouldBe` equalTo True
452+
405
453
406 where
454 where
407− label = "testMxSend"
408 agentLabel s = "mx-unsafe-check-agent-" ++ show s
455 agentLabel s = "mx-unsafe-check-agent-" ++ show s
409− proc sp' = getSelfPid >>= sendChan sp' >> expect :: Process ()
456+ proc sp' = getSelfPid >>= sendChan sp' >> go Nothing
457+
458+ go :: Maybe (ReceivePort ()) -> Process ()
459+ go Nothing = receiveTimeout 5000000 [ match replyChannel ] >>= go
460+ go c@(Just c') = receiveWait [ matchChan c' return ] >> go c
461+
462+ replyChannel p' = do
463+ (s, r) <- newChan
464+ send p' s
465+ return r
410
466
411tests :: TestTransport -> IO [Test]
467tests :: TestTransport -> IO [Test]
412tests TestTransport{..} = do
468tests TestTransport{..} = do
413 node1 <- newLocalNode testTransport initRemoteTable
469 node1 <- newLocalNode testTransport initRemoteTable
414 node2 <- newLocalNode testTransport initRemoteTable
470 node2 <- newLocalNode testTransport initRemoteTable
415− let nid = localNodeId node2
416 return [
471 return [
417 testGroup "MxAgents" [
472 testGroup "MxAgents" [
418 testCase "EventHandling"
473 testCase "EventHandling"
@@ -422,7 +477,7 @@ tests TestTransport{..} = do
422 , testCase "InterAgentBroadcast"
477 , testCase "InterAgentBroadcast"
423 (delayedAssertion
478 (delayedAssertion
424 "expected (), but no broadcast was received"
479 "expected (), but no broadcast was received"
425− node1 () testAgentBroadcast)
480+ node1 (Just ()) testAgentBroadcast)
426 , testCase "AgentMailboxHandling"
481 , testCase "AgentMailboxHandling"
427 (delayedAssertion
482 (delayedAssertion
428 "expected (Just ()), but no regular (mailbox) input was handled"
483 "expected (Just ()), but no regular (mailbox) input was handled"
@@ -447,65 +502,41 @@ tests TestTransport{..} = do
447 (delayedAssertion
502 (delayedAssertion
448 "expected process deaths to result in unregistration events"
503 "expected process deaths to result in unregistration events"
449 node1 () (testMxRegMon node2))
504 node1 () (testMxRegMon node2))
450− , testGroup "SentEvents" [
505+ , testGroup "SendEvents" $ buildTestCases node1 node2
451− testGroup "RemoteTargets" [
506+ ]
452− testCase "Unsafe.nsend"
507+ ]
453− (delayedAssertion "expected mx events failed"
508+ where
454− node1 True (testNSend Unsafe.nsend $ Just node2))
509+ buildTestCases n1 n2 = let nid = localNodeId n2 in build n1 n2 [
455− , testCase "Unsafe.nsendRemote"
510+ ("NSend", [
456− (delayedAssertion "expected mx events failed"
511+ ("nsend", testNSend nsend)
457− node1 True (testNSend (Unsafe.nsendRemote nid) $ Just node2))
512+ , ("Unsafe.nsend", testNSend Unsafe.nsend)
458− , testCase "Unsafe.send"
513+ , ("nsendRemote", testNSend (nsendRemote nid))
459− (delayedAssertion "expected mx events failed"
514+ , ("Unsafe.nsendRemote", testNSend (Unsafe.nsendRemote nid))
460− node1 True (testSend Unsafe.send $ Just node2))
515+ ])
461− , testCase "Unsafe.usend"
516+ , ("Send", [
462− (delayedAssertion "expected mx events failed"
517+ ("send", testSend send)
463− node1 True (testSend Unsafe.usend $ Just node2))
518+ , ("Unsafe.send", testSend Unsafe.send)
464− , testCase "nsend"
519+ , ("usend", testSend usend)
465− (delayedAssertion "expected mx events failed"
520+ , ("Unsafe.usend", testSend Unsafe.usend)
466− node1 True (testNSend nsend $ Just node2))
521+ , ("sendChan", testChan sendChan)
467− , testCase "nsendRemote"
522+ , ("Unsafe.sendChan", testChan Unsafe.sendChan)
468− (delayedAssertion "expected mx events failed"
523+ ])
469− node1 True (testNSend (nsendRemote nid) $ Just node2))
524+ , ("Forward", [
470− , testCase "send"
471− (delayedAssertion "expected mx events failed"
472− node1 True (testSend send $ Just node2))
473− , testCase "usend"
474− (delayedAssertion "expected mx events failed"
475− node1 True (testSend usend $ Just node2))
476− , testCase "forward"
477− (delayedAssertion "expected mx events failed"
478− node1 True (testSend (\p m -> forward (unsafeCreateUnencodedMessage m) p) $ Just node2))
479− , testCase "uforward"
480− (delayedAssertion "expected mx events failed"
481− node1 True (testSend (\p m -> uforward (unsafeCreateUnencodedMessage m) p) $ Just node2))
482− ]
483− , testGroup "LocalTargets" [
484− testCase "Unsafe.nsend"
485− (delayedAssertion "expected mx events failed"
486− node1 True (testNSend Unsafe.nsend Nothing))
487− , testCase "Unsafe.send"
488− (delayedAssertion "expected mx events failed"
489− node1 True (testSend Unsafe.send Nothing))
490− , testCase "Unsafe.usend"
491− (delayedAssertion "expected mx events failed"
492− node1 True (testSend Unsafe.usend Nothing))
493− , testCase "nsend"
494− (delayedAssertion "expected mx events failed"
495− node1 True (testNSend nsend Nothing))
496− , testCase "send"
497− (delayedAssertion "expected mx events failed"
498− node1 True (testSend send Nothing))
499− , testCase "usend"
500− (delayedAssertion "expected mx events failed"
501− node1 True (testSend usend Nothing))
502− , testCase "forward"
503− (delayedAssertion "expected mx events failed"
504− node1 True (testSend (\p m -> forward (unsafeCreateUnencodedMessage m) p) Nothing))
525+ ("forward", testSend (\p m -> forward (unsafeCreateUnencodedMessage m) p))
505− , testCase "uforward"
506− (delayedAssertion "expected mx events failed"
507− node1 True (testSend (\p m -> uforward (unsafeCreateUnencodedMessage m) p) Nothing))
526+ , ("uforward", testSend (\p m -> uforward (unsafeCreateUnencodedMessage m) p))
527+ ])
508 ]
528 ]
529+
530+ build :: LocalNode
531+ -> LocalNode
532+ -> [(String, [(String, (Maybe LocalNode -> Process ()))])]
533+ -> [Test]
534+ build n ln specs =
535+ [ testGroup (intercalate "-" [groupName, caseSuffix]) [
536+ testCase (intercalate "-" [caseName, caseSuffix])
537+ (runProcess n (caseImpl caseNode))
538+ | (caseName, caseImpl) <- groupCases
509− ]
539+ ]
510− ]
540+ | (groupName, groupCases) <- specs
541+ , (caseSuffix, caseNode) <- [("RemotePid", Just ln), ("LocalPid", Nothing)]
511 ]
542 ]
distributed-process.cabal+1−1
@@ -6,7 +6,7 @@ License: BSD3
6License-File: LICENSE
6License-File: LICENSE
7Copyright: Well-Typed LLP, Tweag I/O Limited
7Copyright: Well-Typed LLP, Tweag I/O Limited
8Author: Duncan Coutts, Nicolas Wu, Edsko de Vries
8Author: Duncan Coutts, Nicolas Wu, Edsko de Vries
9−Maintainer: Facundo Domínguez <facundo.dominguez@tweag.io>
9+Maintainer: Tim Watson <watson.timothy@gmail.com>
10Stability: experimental
10Stability: experimental
11Homepage: http://haskell-distributed.github.com/
11Homepage: http://haskell-distributed.github.com/
12Bug-Reports: https://github.com/haskell-distributed/distributed-process/issues
12Bug-Reports: https://github.com/haskell-distributed/distributed-process/issues
src/Control/Distributed/Process/Internal/Primitives.hs+15−6
@@ -307,7 +307,13 @@ expect = receiveWait [match return]
307-- Channels --
307-- Channels --
308--------------------------------------------------------------------------------
308--------------------------------------------------------------------------------
309
309
310−-- | Create a new typed channel
310+-- | Create a new typed channel, bound to the calling Process
311+--
312+-- Note that the channel is bound to the lifecycle of the process that evaluates
313+-- this function, such that when it dies/exits, the channel will no longer
314+-- function, but will remain accessible. Thus reading from the ReceivePort will
315+-- fail silently thereafter, blocking indefinitely (unless a timeout is used).
316+--
311newChan :: Serializable a => Process (SendPort a, ReceivePort a)
317newChan :: Serializable a => Process (SendPort a, ReceivePort a)
312newChan = do
318newChan = do
313 proc <- ask
319 proc <- ask
@@ -335,13 +341,16 @@ newChan = do
335sendChan :: Serializable a => SendPort a -> a -> Process ()
341sendChan :: Serializable a => SendPort a -> a -> Process ()
336sendChan (SendPort cid) msg = do
342sendChan (SendPort cid) msg = do
337 proc <- ask
343 proc <- ask
338− let node = localNodeId (processNode proc)
344+ let node = processNode proc
345+ pid = processId proc
346+ us = localNodeId node
339− destNode = processNodeId (sendPortProcessId cid) in do
347+ them = processNodeId (sendPortProcessId cid) in do
348+ liftIO $ traceEvent (localEventBus node) (MxSentToPort pid cid $ wrapMessage msg)
340− case destNode == node of
349+ case them == us of
341 True -> sendChanLocal cid msg
350 True -> sendChanLocal cid msg
342 False -> do
351 False -> do
343− liftIO $ sendBinary (processNode proc)
352+ liftIO $ sendBinary node
344− (ProcessIdentifier (processId proc))
353+ (ProcessIdentifier pid)
345 (SendPortIdentifier cid)
354 (SendPortIdentifier cid)
346 NoImplicitReconnect
355 NoImplicitReconnect
347 msg
356 msg
src/Control/Distributed/Process/Management.hs+34−2
@@ -76,6 +76,40 @@
76--
76--
77-- * Whether messages will be taken from the mailbox first, or the event bus.
77-- * Whether messages will be taken from the mailbox first, or the event bus.
78--
78--
79+-- Since the event bus uses STM broadcast channels to communicate with agents,
80+-- no message written to the bus successfully can be lost.
81+--
82+-- Agents can also receive messages via their mailboxes - these are subject to
83+-- the same guarantees as all inter-process message sending.
84+--
85+-- Messages dispatched on an STM broadcast channel (i.e., management event bus)
86+-- are guaranteed to be delivered with the same FIFO ordering guarantees that
87+-- exist between two communicating processes, such that communication from the
88+-- node controller's threads (i.e., MxEvent's) will never be re-ordered, but
89+-- messages dispatched to the event bus by other processes (including, but not
90+-- limited to agents) are only guaranteed to be ordered between one sender and
91+-- one receiver.
92+--
93+-- No guarantee exists for the ordering in which messages sent to an agent's
94+-- mailbox will be delivered, vs messages dispatched via the event bus.
95+--
96+-- Because of the above, there are no ordering guarantees for messages sent
97+-- between agents, or for processes to agents, except for those that apply to
98+-- messages sent between regular processes, since agents are
99+-- implemented as such.
100+--
101+-- The event bus is serial and single threaded. Anything that is published by
102+-- the node controller will be seen in FIFO order. There are no ordering
103+-- guarantees pertaining to entries published to the event bus by other
104+-- processes or agents.
105+--
106+-- It should not be possible to see, for example, an @MxReceived@ before the
107+-- corresponding @MxSent@ event, since the places where we issue the @MxSent@
108+-- write directly to the event bus (using STM) in the calling (green) thread,
109+-- before dispatching instructions to the node controller to perform the
110+-- necessary routing to deliver the message to a process (or registered name,
111+-- or typed channel) locally or remotely.
112+--
79-- [Management Data API]
113-- [Management Data API]
80--
114--
81-- Both management agents and clients of the API have access to a variety of
115-- Both management agents and clients of the API have access to a variety of
@@ -144,8 +178,6 @@
144-- > monitorNames = getSelfPid >>= nsend "name-monitor"
178-- > monitorNames = getSelfPid >>= nsend "name-monitor"
145-- > monitorNames2 = getSelfPid >>= mxNotify
179-- > monitorNames2 = getSelfPid >>= mxNotify
146--
180--
147−-- For some real-world examples, see the distributed-process-platform package.
148−--
149-- [Performance, Stablity and Scalability]
181-- [Performance, Stablity and Scalability]
150--
182--
151-- /Management Agents/ offer numerous advantages over regular processes:
183-- /Management Agents/ offer numerous advantages over regular processes:
src/Control/Distributed/Process/Management/Internal/Types.hs+5
@@ -21,6 +21,7 @@ import Control.Concurrent.STM
21import Control.Distributed.Process.Internal.Types
21import Control.Distributed.Process.Internal.Types
22 ( Process
22 ( Process
23 , ProcessId
23 , ProcessId
24+ , SendPortId
24 , Message
25 , Message
25 , DiedReason
26 , DiedReason
26 , NodeId
27 , NodeId
@@ -59,8 +60,12 @@ data MxEvent =
59 -- ^ fired whenever a message is sent from a local process
60 -- ^ fired whenever a message is sent from a local process
60 | MxSentToName String ProcessId Message
61 | MxSentToName String ProcessId Message
61 -- ^ fired whenever a named send occurs
62 -- ^ fired whenever a named send occurs
63+ | MxSentToPort ProcessId SendPortId Message
64+ -- ^ fired whenever a sendChan occurs
62 | MxReceived ProcessId Message
65 | MxReceived ProcessId Message
63 -- ^ fired whenever a message is received by a local process
66 -- ^ fired whenever a message is received by a local process
67+ | MxReceivedPort SendPortId Message
68+ -- ^ fired whenever a message is received via a typed channel
64 | MxConnected ConnectionId EndPointAddress
69 | MxConnected ConnectionId EndPointAddress
65 -- ^ fired when a network-transport connection is first established
70 -- ^ fired when a network-transport connection is first established
66 | MxDisconnected ConnectionId EndPointAddress
71 | MxDisconnected ConnectionId EndPointAddress
src/Control/Distributed/Process/Node.hs+15−10
@@ -492,7 +492,7 @@ type IncomingConnection = (NT.EndPointAddress, IncomingTarget)
492data IncomingTarget =
492data IncomingTarget =
493 Uninit
493 Uninit
494 | ToProc ProcessId (Weak (CQueue Message))
494 | ToProc ProcessId (Weak (CQueue Message))
495− | ToChan TypedChannel
495+ | ToChan SendPortId TypedChannel
496 | ToNode
496 | ToNode
497
497
498data ConnectionState = ConnectionState {
498data ConnectionState = ConnectionState {
@@ -547,13 +547,17 @@ handleIncomingMessages node = go initConnectionState
547 enqueue queue msg -- 'enqueue' is strict
547 enqueue queue msg -- 'enqueue' is strict
548 trace node (MxReceived pid msg)
548 trace node (MxReceived pid msg)
549 go st
549 go st
550− Just (_, ToChan (TypedChannel chan')) -> do
550+ Just (_, ToChan chId (TypedChannel chan')) -> do
551 mChan <- deRefWeak chan'
551 mChan <- deRefWeak chan'
552 -- If mChan is Nothing, the process has given up the read end of
552 -- If mChan is Nothing, the process has given up the read end of
553 -- the channel and we simply ignore the incoming message
553 -- the channel and we simply ignore the incoming message
554− forM_ mChan $ \chan -> atomically $
554+ forM_ mChan $ \chan -> do
555+ msg' <- atomically $ do
556+ msg <- return $! decode (BSL.fromChunks payload)
555− -- We make sure the message is fully decoded when it is enqueued
557+ -- We make sure the message is fully decoded when it is enqueued
556− writeTQueue chan $! decode (BSL.fromChunks payload)
558+ writeTQueue chan msg
559+ return msg
560+ trace node $ MxReceivedPort chId $ unsafeCreateUnencodedMessage msg'
557 go st
561 go st
558 Just (_, ToNode) -> do
562 Just (_, ToNode) -> do
559 let ctrlMsg = decode . BSL.fromChunks $ payload
563 let ctrlMsg = decode . BSL.fromChunks $ payload
@@ -580,7 +584,7 @@ handleIncomingMessages node = go initConnectionState
580 mChannel <- withMVar (processState proc) $ return . (^. typedChannelWithId lcid)
584 mChannel <- withMVar (processState proc) $ return . (^. typedChannelWithId lcid)
581 case mChannel of
585 case mChannel of
582 Just channel ->
586 Just channel ->
583− go (incomingAt cid ^= Just (src, ToChan channel) $ st)
587+ go (incomingAt cid ^= Just (src, ToChan chId channel) $ st)
584 Nothing ->
588 Nothing ->
585 invalidRequest cid st $
589 invalidRequest cid st $
586 "incoming attempt to connect to unknown channel of"
590 "incoming attempt to connect to unknown channel of"
@@ -1050,11 +1054,12 @@ ncEffectLocalPortSend from msg = do
1050 -- If ch is Nothing, the process has given up the read end of
1054 -- If ch is Nothing, the process has given up the read end of
1051 -- the channel and we simply ignore the incoming message - this
1055 -- the channel and we simply ignore the incoming message - this
1052 ch <- deRefWeak chan'
1056 ch <- deRefWeak chan'
1053− forM_ ch $ \chan -> deliverChan msg chan
1057+ forM_ ch $ \chan -> deliverChan node from msg chan
1054− where deliverChan :: forall a . Message -> TQueue a -> IO ()
1058+ where deliverChan :: forall a . LocalNode -> SendPortId -> Message -> TQueue a -> IO ()
1055− deliverChan (UnencodedMessage _ raw) chan' =
1059+ deliverChan n p (UnencodedMessage _ raw) chan' = do
1056 atomically $ writeTQueue chan' ((unsafeCoerce raw) :: a)
1060 atomically $ writeTQueue chan' ((unsafeCoerce raw) :: a)
1061+ trace n (MxReceivedPort p $ unsafeCreateUnencodedMessage raw)
1057− deliverChan (EncodedMessage _ _) _ =
1062+ deliverChan _ _ (EncodedMessage _ _) _ =
1058 -- this will not happen unless someone screws with Primitives.hs
1063 -- this will not happen unless someone screws with Primitives.hs
1059 error "invalid local channel delivery"
1064 error "invalid local channel delivery"
1060
1065
src/Control/Distributed/Process/UnsafePrimitives.hs+15−13
@@ -178,20 +178,22 @@ usend them msg = do
178sendChan :: Serializable a => SendPort a -> a -> Process ()
178sendChan :: Serializable a => SendPort a -> a -> Process ()
179sendChan (SendPort cid) msg = do
179sendChan (SendPort cid) msg = do
180 proc <- ask
180 proc <- ask
181+ let node = processNode proc
182+ pid = processId proc
181− let node = localNodeId (processNode proc)
183+ us = localNodeId node
182− destNode = processNodeId (sendPortProcessId cid) in do
184+ them = processNodeId (sendPortProcessId cid)
183− case destNode == node of
185+ msg' = wrapMessage msg in do
186+ liftIO $ traceEvent (localEventBus node) (MxSentToPort pid cid msg')
187+ if them == us
184− True -> unsafeSendChanLocal cid msg
188+ then unsafeSendChanLocal cid msg' -- NB: we wrap to P.Message !!!
185− False ->
186− liftIO $ sendBinary (processNode proc)
189+ else liftIO $ sendBinary node
187− (ProcessIdentifier (processId proc))
190+ (ProcessIdentifier pid)
188− (SendPortIdentifier cid)
191+ (SendPortIdentifier cid)
189− NoImplicitReconnect
192+ NoImplicitReconnect
190− msg
193+ msg
191 where
194 where
192− unsafeSendChanLocal :: (Serializable a) => SendPortId -> a -> Process ()
195+ unsafeSendChanLocal :: SendPortId -> Message -> Process ()
193− unsafeSendChanLocal spId msg' =
194− sendCtrlMsg Nothing $ LocalPortSend spId (wrapMessage msg')
196+ unsafeSendChanLocal p m = sendCtrlMsg Nothing $ LocalPortSend p m
195
197
196-- | Create an unencoded @Message@ for any @Serializable@ type.
198-- | Create an unencoded @Message@ for any @Serializable@ type.
197wrapMessage :: Serializable a => a -> Message
199wrapMessage :: Serializable a => a -> Message