git.iko.soy/distributed-process
on master help sign in
.travis.yml+11−6
since splitsnapshots
unifiedsplit
@@ -6,11 +6,11 @@ matrix:
6 include:
6 include:
7 - env: ARGS="--stack-yaml stack-ghc-7.10.3.yaml"
7 - env: ARGS="--stack-yaml stack-ghc-7.10.3.yaml"
8 addons: {apt: {packages: [libgmp-dev]}}
8 addons: {apt: {packages: [libgmp-dev]}}
9−
10− - env: ARGS="--stack-yaml stack-ghc-8.0.2.yaml"
9+ - env: ARGS="--stack-yaml stack-ghc-8.2.2.yaml"
11 addons: {apt: {packages: [libgmp-dev]}}
10 addons: {apt: {packages: [libgmp-dev]}}
12−
11+ - env: ARGS="--stack-yaml stack-ghc-8.4.4.yaml"
13− - env: ARGS=
12+ addons: {apt: {packages: [libgmp-dev]}}
13+ - env: ARGS="--stack-yaml stack-nightly.yaml --resolver nightly"
14 addons: {apt: {packages: [libgmp-dev]}}
14 addons: {apt: {packages: [libgmp-dev]}}
15
15
16cache:
16cache:
@@ -34,5 +34,10 @@ script:
34- stack ${ARGS} test $ARG='--plain -t "!Flaky"' ${TEST_PACKAGE}TestCHInTCP
34- stack ${ARGS} test $ARG='--plain -t "!Flaky"' ${TEST_PACKAGE}TestCHInTCP
35- stack ${ARGS} test $ARG='--plain -t "!SpawnReconnect"' ${TEST_PACKAGE}TestClosure
35- stack ${ARGS} test $ARG='--plain -t "!SpawnReconnect"' ${TEST_PACKAGE}TestClosure
36- stack ${ARGS} test $ARG='--plain' ${TEST_PACKAGE}TestStats
36- stack ${ARGS} test $ARG='--plain' ${TEST_PACKAGE}TestStats
37−- stack ${ARGS} test $ARG='--plain' ${TEST_PACKAGE}TestMx
37+- stack ${ARGS} test $ARG='--plain' ${TEST_PACKAGE}TestMxInMemory
38+- stack ${ARGS} test $ARG='--plain' ${TEST_PACKAGE}TestMxInTCP
38−- stack ${ARGS} test $ARG='--plain' ${TEST_PACKAGE}TestTracing
39+- stack ${ARGS} test $ARG='--plain' ${TEST_PACKAGE}TestTracingInMemory
40+
41+notifications:
42+ slack:
43+ secure: LbqbNjK1KiHNiQo/i/Za4vkMD9l9mhmN+PWYFBQkNvGFWffSRayRKFrTqt4znV2p7h4dY1XZFoC7T5RemBVQLq2ppZAUWxkITeu1OUlnmVLDfJLeVYGTR/fn90nP6Y5ITE7T3A07nKmWaRbKpIgFBAOjzgqaM0csscDx3z0WW18=
benchmarks/Channels.hs+6−5
@@ -4,7 +4,7 @@ import Control.Monad
4import Control.Applicative
4import Control.Applicative
5import Control.Distributed.Process
5import Control.Distributed.Process
6import Control.Distributed.Process.Node
6import Control.Distributed.Process.Node
7−import Network.Transport.TCP (createTransport, defaultTCPParameters)
7+import Network.Transport.TCP (createTransport, defaultTCPAddr, defaultTCPParameters)
8import Data.Binary (encode, decode)
8import Data.Binary (encode, decode)
9import qualified Data.ByteString.Lazy as BSL
9import qualified Data.ByteString.Lazy as BSL
10
10
@@ -36,7 +36,8 @@ initialProcess "CLIENT" = do
36main :: IO ()
36main :: IO ()
37main = do
37main = do
38 [role, host, port] <- getArgs
38 [role, host, port] <- getArgs
39− Right transport <- createTransport
40− host port (\sn -> (host, port)) defaultTCPParameters
39+ trans <- createTransport (defaultTCPAddr host port) defaultTCPParameters
40+ case trans of
41− node <- newLocalNode transport initRemoteTable
41+ Right transport -> do node <- newLocalNode transport initRemoteTable
42− runProcess node $ initialProcess role
42+ runProcess node $ initialProcess role
43+ Left other -> error $ show other
benchmarks/Latency.hs+7−5
@@ -3,7 +3,7 @@ import Control.Monad
3import Control.Applicative
3import Control.Applicative
4import Control.Distributed.Process
4import Control.Distributed.Process
5import Control.Distributed.Process.Node
5import Control.Distributed.Process.Node
6−import Network.Transport.TCP (createTransport, defaultTCPParameters)
6+import Network.Transport.TCP (createTransport, defaultTCPAddr, defaultTCPParameters)
7import Data.Binary (encode, decode)
7import Data.Binary (encode, decode)
8import qualified Data.ByteString.Lazy as BSL
8import qualified Data.ByteString.Lazy as BSL
9
9
@@ -31,7 +31,9 @@ initialProcess "CLIENT" = do
31main :: IO ()
31main :: IO ()
32main = do
32main = do
33 [role, host, port] <- getArgs
33 [role, host, port] <- getArgs
34− Right transport <- createTransport
34+ trans <- createTransport
35− host port (\sn -> (host, sn)) defaultTCPParameters
35+ (defaultTCPAddr host port) defaultTCPParameters
36+ case trans of
36− node <- newLocalNode transport initRemoteTable
37+ Right transport -> do node <- newLocalNode transport initRemoteTable
37− runProcess node $ initialProcess role
38+ runProcess node $ initialProcess role
39+ Left other -> error $ show other
benchmarks/ProcessRing.hs+2−2
@@ -12,7 +12,7 @@ import Control.Monad
12import Control.Distributed.Process hiding (catch)
12import Control.Distributed.Process hiding (catch)
13import Control.Distributed.Process.Node
13import Control.Distributed.Process.Node
14import Control.Exception (catch, SomeException)
14import Control.Exception (catch, SomeException)
15−import Network.Transport.TCP (createTransport, defaultTCPParameters)
15+import Network.Transport.TCP (createTransport, defaultTCPAddr, defaultTCPParameters)
16import System.Environment
16import System.Environment
17import System.Console.GetOpt
17import System.Console.GetOpt
18
18
@@ -111,7 +111,7 @@ main = do
111 (opt, _) <- parseArgv argv
111 (opt, _) <- parseArgv argv
112 putStrLn $ "options: " ++ (show opt)
112 putStrLn $ "options: " ++ (show opt)
113 Right transport <- createTransport
113 Right transport <- createTransport
114− "127.0.0.1" "8090" (\sn -> ("127.0.0.1", sn)) defaultTCPParameters
114+ (defaultTCPAddr "127.0.0.1" "8090" ) defaultTCPParameters
115 node <- newLocalNode transport initRemoteTable
115 node <- newLocalNode transport initRemoteTable
116 catch (void $ runProcess node $ initialProcess opt)
116 catch (void $ runProcess node $ initialProcess opt)
117 (\(e :: SomeException) -> putStrLn $ "ERROR: " ++ (show e))
117 (\(e :: SomeException) -> putStrLn $ "ERROR: " ++ (show e))
benchmarks/Spawns.hs+6−5
@@ -7,7 +7,7 @@ import Control.Monad
7import Control.Applicative
7import Control.Applicative
8import Control.Distributed.Process
8import Control.Distributed.Process
9import Control.Distributed.Process.Node
9import Control.Distributed.Process.Node
10−import Network.Transport.TCP (createTransport, defaultTCPParameters)
10+import Network.Transport.TCP (createTransport, defaultTCPAddr, defaultTCPParameters)
11import Data.Binary (encode, decode)
11import Data.Binary (encode, decode)
12import qualified Data.ByteString.Lazy as BSL
12import qualified Data.ByteString.Lazy as BSL
13
13
@@ -42,7 +42,8 @@ initialProcess "CLIENT" = do
42main :: IO ()
42main :: IO ()
43main = do
43main = do
44 [role, host, port] <- getArgs
44 [role, host, port] <- getArgs
45− Right transport <- createTransport
46− host port (\sn -> (host, sn)) defaultTCPParameters
45+ trans <- createTransport (defaultTCPAddr host port) defaultTCPParameters
46+ case trans of
47− node <- newLocalNode transport initRemoteTable
47+ Right transport -> do node <- newLocalNode transport initRemoteTable
48− runProcess node $ initialProcess role
48+ runProcess node $ initialProcess role
49+ Left other -> error $ show other
benchmarks/Throughput.hs+6−5
@@ -6,7 +6,7 @@ import Control.Monad
6import Control.Applicative
6import Control.Applicative
7import Control.Distributed.Process
7import Control.Distributed.Process
8import Control.Distributed.Process.Node
8import Control.Distributed.Process.Node
9−import Network.Transport.TCP (createTransport, defaultTCPParameters)
9+import Network.Transport.TCP (createTransport, defaultTCPParameters, defaultTCPAddr)
10import Data.Binary
10import Data.Binary
11import qualified Data.ByteString.Lazy as BSL
11import qualified Data.ByteString.Lazy as BSL
12import Data.Typeable
12import Data.Typeable
@@ -67,7 +67,8 @@ initialProcess "CLIENT" = do
67main :: IO ()
67main :: IO ()
68main = do
68main = do
69 [role, host, port] <- getArgs
69 [role, host, port] <- getArgs
70− Right transport <- createTransport
71− host port (\sn -> (host, sn)) defaultTCPParameters
70+ trans <- createTransport (defaultTCPAddr host port) defaultTCPParameters
71+ case trans of
72− node <- newLocalNode transport initRemoteTable
72+ Right transport -> do node <- newLocalNode transport initRemoteTable
73− runProcess node $ initialProcess role
73+ runProcess node $ initialProcess role
74+ Left other -> error $ show other
distributed-process-tests/distributed-process-tests.cabal+12−25
@@ -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
@@ -31,9 +31,10 @@ library
31 bytestring >= 0.9 && < 0.12,
31 bytestring >= 0.9 && < 0.12,
32 distributed-process >= 0.6.0 && < 0.8,
32 distributed-process >= 0.6.0 && < 0.8,
33 distributed-static,
33 distributed-static,
34+ exceptions >= 0.5,
34 HUnit >= 1.2 && < 1.7,
35 HUnit >= 1.2 && < 1.7,
35 network-transport >= 0.4.1.0 && < 0.6,
36 network-transport >= 0.4.1.0 && < 0.6,
36− network >= 2.5 && < 2.7,
37+ network >= 2.5 && < 2.9,
37 random >= 1.0 && < 1.2,
38 random >= 1.0 && < 1.2,
38 rematch >= 0.1.2.1,
39 rematch >= 0.1.2.1,
39 setenv >= 0.1.1.3,
40 setenv >= 0.1.1.3,
@@ -60,7 +61,7 @@ Test-Suite TestCHInMemory
60 CPP-Options: -DTEST_SUITE_MODULE=Control.Distributed.Process.Tests.CH
61 CPP-Options: -DTEST_SUITE_MODULE=Control.Distributed.Process.Tests.CH
61 Build-Depends: base >= 4.4 && < 5,
62 Build-Depends: base >= 4.4 && < 5,
62 distributed-process-tests,
63 distributed-process-tests,
63− network >= 2.3 && < 2.7,
64+ network >= 2.3 && < 2.9,
64 network-transport >= 0.4.1.0 && < 0.6,
65 network-transport >= 0.4.1.0 && < 0.6,
65 network-transport-inmemory >= 0.5,
66 network-transport-inmemory >= 0.5,
66 test-framework >= 0.6 && < 0.9
67 test-framework >= 0.6 && < 0.9
@@ -75,7 +76,7 @@ Test-Suite TestCHInTCP
75 if flag(tcp)
76 if flag(tcp)
76 Build-Depends: base >= 4.4 && < 5,
77 Build-Depends: base >= 4.4 && < 5,
77 distributed-process-tests,
78 distributed-process-tests,
78− network >= 2.3 && < 2.7,
79+ network >= 2.3 && < 2.9,
79 network-transport >= 0.4.1.0 && < 0.6,
80 network-transport >= 0.4.1.0 && < 0.6,
80 network-transport-tcp >= 0.5 && < 0.7,
81 network-transport-tcp >= 0.5 && < 0.7,
81 test-framework >= 0.6 && < 0.9
82 test-framework >= 0.6 && < 0.9
@@ -92,7 +93,7 @@ Test-Suite TestClosure
92 CPP-Options: -DTEST_SUITE_MODULE=Control.Distributed.Process.Tests.Closure
93 CPP-Options: -DTEST_SUITE_MODULE=Control.Distributed.Process.Tests.Closure
93 Build-Depends: base >= 4.4 && < 5,
94 Build-Depends: base >= 4.4 && < 5,
94 distributed-process-tests,
95 distributed-process-tests,
95− network >= 2.3 && < 2.7,
96+ network >= 2.3 && < 2.9,
96 network-transport >= 0.4.1.0 && < 0.6,
97 network-transport >= 0.4.1.0 && < 0.6,
97 network-transport-inmemory >= 0.5,
98 network-transport-inmemory >= 0.5,
98 test-framework >= 0.6 && < 0.9
99 test-framework >= 0.6 && < 0.9
@@ -106,7 +107,7 @@ Test-Suite TestStats
106 CPP-Options: -DTEST_SUITE_MODULE=Control.Distributed.Process.Tests.Stats
107 CPP-Options: -DTEST_SUITE_MODULE=Control.Distributed.Process.Tests.Stats
107 Build-Depends: base >= 4.4 && < 5,
108 Build-Depends: base >= 4.4 && < 5,
108 distributed-process-tests,
109 distributed-process-tests,
109− network >= 2.3 && < 2.7,
110+ network >= 2.3 && < 2.9,
110 network-transport >= 0.4.1.0 && < 0.6,
111 network-transport >= 0.4.1.0 && < 0.6,
111 network-transport-inmemory >= 0.5,
112 network-transport-inmemory >= 0.5,
112 test-framework >= 0.6 && < 0.9
113 test-framework >= 0.6 && < 0.9
@@ -120,12 +121,12 @@ Test-Suite TestMxInMemory
120 CPP-Options: -DTEST_SUITE_MODULE=Control.Distributed.Process.Tests.Mx
121 CPP-Options: -DTEST_SUITE_MODULE=Control.Distributed.Process.Tests.Mx
121 Build-Depends: base >= 4.4 && < 5,
122 Build-Depends: base >= 4.4 && < 5,
122 distributed-process-tests,
123 distributed-process-tests,
123− network >= 2.3 && < 2.7,
124+ network >= 2.3 && < 2.9,
124 network-transport >= 0.4.1.0 && < 0.6,
125 network-transport >= 0.4.1.0 && < 0.6,
125 network-transport-inmemory >= 0.5,
126 network-transport-inmemory >= 0.5,
126 test-framework >= 0.6 && < 0.9
127 test-framework >= 0.6 && < 0.9
127 Extensions: CPP
128 Extensions: CPP
128− ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N -fno-warn-unused-do-bind
129+ ghc-options: -threaded -rtsopts -with-rtsopts=-N -fno-warn-unused-do-bind
129 HS-Source-Dirs: tests
130 HS-Source-Dirs: tests
130
131
131Test-Suite TestTracingInMemory
132Test-Suite TestTracingInMemory
@@ -134,12 +135,12 @@ Test-Suite TestTracingInMemory
134 CPP-Options: -DTEST_SUITE_MODULE=Control.Distributed.Process.Tests.Tracing
135 CPP-Options: -DTEST_SUITE_MODULE=Control.Distributed.Process.Tests.Tracing
135 Build-Depends: base >= 4.4 && < 5,
136 Build-Depends: base >= 4.4 && < 5,
136 distributed-process-tests,
137 distributed-process-tests,
137− network >= 2.3 && < 2.7,
138+ network >= 2.3 && < 2.9,
138 network-transport >= 0.4.1.0 && < 0.6,
139 network-transport >= 0.4.1.0 && < 0.6,
139 network-transport-inmemory >= 0.5,
140 network-transport-inmemory >= 0.5,
140 test-framework >= 0.6 && < 0.9
141 test-framework >= 0.6 && < 0.9
141 Extensions: CPP
142 Extensions: CPP
142− ghc-options: -Wall -eventlog -threaded -rtsopts -with-rtsopts=-N -fno-warn-unused-do-bind
143+ ghc-options: -eventlog -threaded -rtsopts -with-rtsopts=-N -fno-warn-unused-do-bind
143 HS-Source-Dirs: tests
144 HS-Source-Dirs: tests
144
145
145Test-Suite TestMxInTCP
146Test-Suite TestMxInTCP
@@ -148,24 +149,10 @@ Test-Suite TestMxInTCP
148 CPP-Options: -DTEST_SUITE_MODULE=Control.Distributed.Process.Tests.Mx
149 CPP-Options: -DTEST_SUITE_MODULE=Control.Distributed.Process.Tests.Mx
149 Build-Depends: base >= 4.4 && < 5,
150 Build-Depends: base >= 4.4 && < 5,
150 distributed-process-tests,
151 distributed-process-tests,
151− network >= 2.3 && < 2.7,
152+ network >= 2.3 && < 2.9,
152 network-transport >= 0.4.1.0 && < 0.6,
153 network-transport >= 0.4.1.0 && < 0.6,
153 network-transport-inmemory >= 0.5,
154 network-transport-inmemory >= 0.5,
154 test-framework >= 0.6 && < 0.9
155 test-framework >= 0.6 && < 0.9
155 Extensions: CPP
156 Extensions: CPP
156 ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N -fno-warn-unused-do-bind
157 ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N -fno-warn-unused-do-bind
157 HS-Source-Dirs: tests
158 HS-Source-Dirs: tests
158−
159−Test-Suite TestTracingInTCP
160− Type: exitcode-stdio-1.0
161− Main-Is: runInMemory.hs
162− CPP-Options: -DTEST_SUITE_MODULE=Control.Distributed.Process.Tests.Tracing
163− Build-Depends: base >= 4.4 && < 5,
164− distributed-process-tests,
165− network >= 2.3 && < 2.7,
166− network-transport >= 0.4.1.0 && < 0.6,
167− network-transport-inmemory >= 0.5,
168− test-framework >= 0.6 && < 0.9
169− Extensions: CPP
170− ghc-options: -Wall -eventlog -threaded -rtsopts -with-rtsopts=-N -fno-warn-unused-do-bind
171− HS-Source-Dirs: tests
distributed-process-tests/src/Control/Distributed/Process/Tests/CH.hs+459−341
@@ -25,13 +25,18 @@ import Control.Concurrent.MVar
25 )
25 )
26import Control.Monad (replicateM_, replicateM, forever, void, unless, join)
26import Control.Monad (replicateM_, replicateM, forever, void, unless, join)
27import Control.Exception (SomeException, throwIO, ErrorCall(..))
27import Control.Exception (SomeException, throwIO, ErrorCall(..))
28−import qualified Control.Exception as Ex (catch)
28+import Control.Monad.Catch (try, catch, finally, mask, onException)
29−import Control.Applicative ((<$>), (<*>), pure, (<|>))
29+import Control.Applicative ((<|>))
30import qualified Network.Transport as NT (closeEndPoint, EndPointAddress)
30import qualified Network.Transport as NT (closeEndPoint, EndPointAddress)
31−import Control.Distributed.Process
31+import Control.Distributed.Process hiding
32+ ( try
33+ , catch
34+ , finally
35+ , mask
36+ , onException
37+ )
32import Control.Distributed.Process.Internal.Types
38import Control.Distributed.Process.Internal.Types
33− ( NodeId(nodeAddress)
34− , LocalNode(localEndPoint)
39+ ( LocalNode(localEndPoint)
35 , ProcessExitException(..)
40 , ProcessExitException(..)
36 , nullProcessId
41 , nullProcessId
37 , createUnencodedMessage
42 , createUnencodedMessage
@@ -39,13 +44,13 @@ import Control.Distributed.Process.Internal.Types
39import Control.Distributed.Process.Node
44import Control.Distributed.Process.Node
40import Control.Distributed.Process.Debug
45import Control.Distributed.Process.Debug
41import Control.Distributed.Process.Management.Internal.Types
46import Control.Distributed.Process.Management.Internal.Types
42−import Control.Distributed.Process.Tests.Internal.Utils (shouldBe)
47+import Control.Distributed.Process.Tests.Internal.Utils (shouldBe, pause)
43import Control.Distributed.Process.Serializable (Serializable)
48import Control.Distributed.Process.Serializable (Serializable)
44−
49+import Data.Maybe (isNothing, isJust)
45−import Test.HUnit (Assertion, assertFailure, assertBool)
50+import Test.HUnit (Assertion, assertBool, assertFailure)
46import Test.Framework (Test, testGroup)
51import Test.Framework (Test, testGroup)
47import Test.Framework.Providers.HUnit (testCase)
52import Test.Framework.Providers.HUnit (testCase)
48−import Control.Rematch hiding (match)
53+import Control.Rematch hiding (match, isNothing, isJust)
49import Control.Rematch.Run (Match(..))
54import Control.Rematch.Run (Match(..))
50
55
51newtype Ping = Ping ProcessId
56newtype Ping = Ping ProcessId
@@ -68,7 +73,7 @@ expectThat a matcher = case res of
68forkTry :: IO () -> IO ThreadId
73forkTry :: IO () -> IO ThreadId
69forkTry p = do
74forkTry p = do
70 tid <- myThreadId
75 tid <- myThreadId
71− forkIO $ Ex.catch p (\e -> throwTo tid (e :: SomeException))
76+ forkIO $ catch p (\e -> throwTo tid (e :: SomeException))
72
77
73-- | The ping server from the paper
78-- | The ping server from the paper
74ping :: Process ()
79ping :: Process ()
@@ -78,12 +83,46 @@ ping = do
78 send partner (Ping self)
83 send partner (Ping self)
79 ping
84 ping
80
85
86+verifyClient :: String -> MVar Bool -> IO ()
87+verifyClient s b = takeMVar b >>= assertBool s
88+
89+expectPing :: MVar Bool -> Process ()
90+expectPing mv = expect >>= liftIO . putMVar mv . checkPing
91+ where
92+ checkPing (Ping _) = True
93+
81-- | Quick and dirty synchronous version of whereisRemoteAsync
94-- | Quick and dirty synchronous version of whereisRemoteAsync
82whereisRemote :: NodeId -> String -> Process (Maybe ProcessId)
95whereisRemote :: NodeId -> String -> Process (Maybe ProcessId)
83whereisRemote nid string = do
96whereisRemote nid string = do
84 whereisRemoteAsync nid string
97 whereisRemoteAsync nid string
98+ receiveWait [
85− WhereIsReply _ mPid <- expect
99+ match (\(WhereIsReply _ mPid) -> return mPid)
100+ ]
101+
102+verifyWhereIsRemote :: NodeId -> String -> Process ProcessId
103+verifyWhereIsRemote n s = whereisRemote n s >>= maybe (die "remote name not found") return
104+
105+waitForExit :: ProcessId -> Process DiedReason
106+waitForExit pid = monitor pid >>= waitForDown
107+
108+waitForDown :: MonitorRef -> Process DiedReason
109+waitForDown ref =
110+ receiveWait [ matchIf (\(ProcessMonitorNotification ref' _ _) -> ref == ref')
111+ (\(ProcessMonitorNotification _ _ dr) -> return dr) ]
112+
113+syncBreakConnection :: (NT.EndPointAddress -> NT.EndPointAddress -> IO ()) -> LocalNode -> LocalNode -> IO ()
114+syncBreakConnection breakConnection nid0 nid1 = do
115+ m <- newEmptyMVar
116+ _ <- forkProcess nid1 $ getSelfPid >>= liftIO . putMVar m
117+ runProcess nid0 $ do
118+ them <- liftIO $ takeMVar m
119+ pinger <- spawnLocal $ forever $ send them ()
120+ _ <- monitorNode (localNodeId nid1)
121+ liftIO $ breakConnection (nodeAddress $ localNodeId nid0)
122+ (nodeAddress $ localNodeId nid1)
123+ NodeMonitorNotification _ _ _ <- expect
124+ kill pinger "finished"
86− return mPid
125+ return ()
87
126
88data Add = Add ProcessId Double Double deriving (Typeable)
127data Add = Add ProcessId Double Double deriving (Typeable)
89data Divide = Divide ProcessId Double Double deriving (Typeable)
128data Divide = Divide ProcessId Double Double deriving (Typeable)
@@ -158,12 +197,18 @@ monitorTestProcess theirAddr mOrL un reason monitorSetup done =
158 unmonitor ref
197 unmonitor ref
159 liftIO $ putMVar done ()
198 liftIO $ putMVar done ()
160 (False, ref) -> do
199 (False, ref) -> do
200+ receiveWait [
161− ProcessMonitorNotification ref' pid reason' <- expect
201+ match (\(ProcessMonitorNotification ref' pid reason') -> do
202+ liftIO $ do
203+ assertBool "Bad Monitor Signal"
162− True <- return $ Just ref' == ref && pid == theirAddr && mOrL && reason == reason'
204+ (Just ref' == ref && pid == theirAddr &&
205+ mOrL && reason == reason')
163− liftIO $ putMVar done ()
206+ putMVar done ())
207+ ]
164 )
208 )
165 (\(ProcessLinkException pid reason') -> do
209 (\(ProcessLinkException pid reason') -> do
210+ (liftIO $ assertBool "link exception unmatched" $
166− True <- return $ pid == theirAddr && not mOrL && not un && reason == reason'
211+ pid == theirAddr && not mOrL && not un && reason == reason')
167 liftIO $ putMVar done ()
212 liftIO $ putMVar done ()
168 )
213 )
169
214
@@ -194,8 +239,10 @@ testPing TestTransport{..} = do
194 pid <- getSelfPid
239 pid <- getSelfPid
195 replicateM_ numPings $ do
240 replicateM_ numPings $ do
196 send pingServer (Pong pid)
241 send pingServer (Pong pid)
197− Ping _ <- expect
242+ p <- expectTimeout 3000000
243+ case p of
198− return ()
244+ Just (Ping _) -> return ()
245+ Nothing -> die "Failed to receive Ping"
199
246
200 putMVar clientDone ()
247 putMVar clientDone ()
201
248
@@ -282,7 +329,7 @@ testMonitorLocalDeadProcess TestTransport{..} mOrL un = do
282 runProcess localNode $ do
329 runProcess localNode $ do
283 monitor theirAddr
330 monitor theirAddr
284 -- wait for the process to die
331 -- wait for the process to die
285− ProcessMonitorNotification _ _ _ <- expect
332+ expect :: Process ProcessMonitorNotification
286 monitorTestProcess theirAddr mOrL un DiedUnknownId Nothing done
333 monitorTestProcess theirAddr mOrL un DiedUnknownId Nothing done
287
334
288 takeMVar done
335 takeMVar done
@@ -356,14 +403,17 @@ testMath TestTransport{..} = do
356 runProcess localNode $ do
403 runProcess localNode $ do
357 pid <- getSelfPid
404 pid <- getSelfPid
358 send mathServer (Add pid 1 2)
405 send mathServer (Add pid 1 2)
359− 3 <- expect :: Process Double
406+ three <- expect :: Process Double
360 send mathServer (Divide pid 8 2)
407 send mathServer (Divide pid 8 2)
361− 4 <- expect :: Process Double
408+ four <- expect :: Process Double
362 send mathServer (Divide pid 8 0)
409 send mathServer (Divide pid 8 0)
363− DivByZero <- expect
410+ divByZ <- expect
364− liftIO $ putMVar clientDone ()
411+ liftIO $ putMVar clientDone (three, four, divByZ)
365
412
366− takeMVar clientDone
413+ res <- takeMVar clientDone
414+ case res of
415+ (3, 4, DivByZero) -> return ()
416+ _ -> error $ "Something went horribly wrong"
367
417
368-- | Send first message (i.e. connect) to an already terminated process
418-- | Send first message (i.e. connect) to an already terminated process
369-- (without monitoring); then send another message to a second process on
419-- (without monitoring); then send another message to a second process on
@@ -391,11 +441,9 @@ testSendToTerminated TestTransport{..} = do
391 pid <- getSelfPid
441 pid <- getSelfPid
392 send server1 "Hi"
442 send server1 "Hi"
393 send server2 (Pong pid)
443 send server2 (Pong pid)
394− Ping pid' <- expect
395− True <- return $ pid' == server2
396− liftIO $ putMVar clientDone ()
444+ expectPing clientDone
397
445
398− takeMVar clientDone
446+ verifyClient "Expected Ping from server" clientDone
399
447
400-- | Test (non-zero) timeout
448-- | Test (non-zero) timeout
401testTimeout :: TestTransport -> Assertion
449testTimeout :: TestTransport -> Assertion
@@ -404,10 +452,10 @@ testTimeout TestTransport{..} = do
404 done <- newEmptyMVar
452 done <- newEmptyMVar
405
453
406 runProcess localNode $ do
454 runProcess localNode $ do
407− Nothing <- receiveTimeout 1000000 [match (\(Add _ _ _) -> return ())]
455+ res <- receiveTimeout 1000000 [match (\Add{} -> return ())]
408− liftIO $ putMVar done ()
456+ liftIO $ putMVar done $ res == Nothing
409
457
410− takeMVar done
458+ verifyClient "Expected receiveTimeout to timeout..." done
411
459
412-- | Test zero timeout
460-- | Test zero timeout
413testTimeout0 :: TestTransport -> Assertion
461testTimeout0 :: TestTransport -> Assertion
@@ -435,10 +483,9 @@ testTimeout0 TestTransport{..} = do
435 -- is not interested in, and then a single message that it wants
483 -- is not interested in, and then a single message that it wants
436 replicateM_ 10000 $ send server "Irrelevant message"
484 replicateM_ 10000 $ send server "Irrelevant message"
437 send server (Pong pid)
485 send server (Pong pid)
438− Ping _ <- expect
439− liftIO $ putMVar clientDone ()
486+ expectPing clientDone
440
487
441− takeMVar clientDone
488+ verifyClient "Expected Ping from server" clientDone
442
489
443-- | Test typed channels
490-- | Test typed channels
444testTypedChannels :: TestTransport -> Assertion
491testTypedChannels :: TestTransport -> Assertion
@@ -461,10 +508,10 @@ testTypedChannels TestTransport{..} = do
461 runProcess localNode $ do
508 runProcess localNode $ do
462 (clientSendPort, rport) <- newChan
509 (clientSendPort, rport) <- newChan
463 sendChan serverSendPort (clientSendPort, 5)
510 sendChan serverSendPort (clientSendPort, 5)
464− False <- receiveChan rport
511+ ch <- receiveChan rport
465− liftIO $ putMVar clientDone ()
512+ liftIO $ putMVar clientDone $ ch == False
466
513
467− takeMVar clientDone
514+ verifyClient "Expected channel to send 'False'" clientDone
468
515
469-- | Test merging receive ports
516-- | Test merging receive ports
470testMergeChannels :: TestTransport -> Assertion
517testMergeChannels :: TestTransport -> Assertion
@@ -487,9 +534,8 @@ testMergeChannels TestTransport{..} = do
487 rs <- mapM charChannel "abc"
534 rs <- mapM charChannel "abc"
488 m <- mergePorts biased rs
535 m <- mergePorts biased rs
489 xs <- replicateM 9 $ receiveChan m
536 xs <- replicateM 9 $ receiveChan m
490− True <- return $ xs == expected
491− liftIO $ putMVar done ()
537+ liftIO $ putMVar done $ xs == expected
492− takeMVar done
538+ verifyClient "Expected single layer merge to match expected ordering" done
493
539
494 -- Two layers of merging
540 -- Two layers of merging
495 testNested :: LocalNode -> Bool -> Bool -> String -> IO ()
541 testNested :: LocalNode -> Bool -> Bool -> String -> IO ()
@@ -500,9 +546,8 @@ testMergeChannels TestTransport{..} = do
500 ms <- mapM (mergePorts biasedInner) rss
546 ms <- mapM (mergePorts biasedInner) rss
501 m <- mergePorts biasedOuter ms
547 m <- mergePorts biasedOuter ms
502 xs <- replicateM (9 * 3) $ receiveChan m
548 xs <- replicateM (9 * 3) $ receiveChan m
503− True <- return $ xs == expected
549+ liftIO $ putMVar done $ xs == expected
504− liftIO $ putMVar done ()
550+ verifyClient "Expected nested channels to match expeted ordering" done
505− takeMVar done
506
551
507 -- Test that if no messages are (immediately) available, the scheduler makes no difference
552 -- Test that if no messages are (immediately) available, the scheduler makes no difference
508 testBlocked :: LocalNode -> Bool -> IO ()
553 testBlocked :: LocalNode -> Bool -> IO ()
@@ -511,43 +556,45 @@ testMergeChannels TestTransport{..} = do
511 done <- newEmptyMVar
556 done <- newEmptyMVar
512
557
513 forkProcess localNode $ do
558 forkProcess localNode $ do
514− [sa, sb, sc] <- liftIO $ mapM readMVar vs
559+ ss <- liftIO $ mapM readMVar vs
560+ case ss of
561+ [sa, sb, sc] ->
515− mapM_ ((>> liftIO (threadDelay 10000)) . uncurry sendChan)
562+ mapM_ ((>> pause 10000) . uncurry sendChan)
516− [ -- a, b, c
563+ [ -- a, b, c
517− (sa, 'a')
564+ (sa, 'a')
518− , (sb, 'b')
565+ , (sb, 'b')
519− , (sc, 'c')
566+ , (sc, 'c')
520− -- a, c, b
567+ -- a, c, b
521− , (sa, 'a')
568+ , (sa, 'a')
522− , (sc, 'c')
569+ , (sc, 'c')
523− , (sb, 'b')
570+ , (sb, 'b')
524− -- b, a, c
571+ -- b, a, c
525− , (sb, 'b')
572+ , (sb, 'b')
526− , (sa, 'a')
573+ , (sa, 'a')
527− , (sc, 'c')
574+ , (sc, 'c')
528− -- b, c, a
575+ -- b, c, a
529− , (sb, 'b')
576+ , (sb, 'b')
530− , (sc, 'c')
577+ , (sc, 'c')
531− , (sa, 'a')
578+ , (sa, 'a')
532− -- c, a, b
579+ -- c, a, b
533− , (sc, 'c')
580+ , (sc, 'c')
534− , (sa, 'a')
581+ , (sa, 'a')
535− , (sb, 'b')
582+ , (sb, 'b')
536− -- c, b, a
583+ -- c, b, a
537− , (sc, 'c')
584+ , (sc, 'c')
538− , (sb, 'b')
585+ , (sb, 'b')
539− , (sa, 'a')
586+ , (sa, 'a')
540− ]
587+ ]
588+ _ -> die "Something went horribly wrong"
541
589
542 forkProcess localNode $ do
590 forkProcess localNode $ do
543 (ss, rs) <- unzip <$> replicateM 3 newChan
591 (ss, rs) <- unzip <$> replicateM 3 newChan
544 liftIO $ mapM_ (uncurry putMVar) $ zip vs ss
592 liftIO $ mapM_ (uncurry putMVar) $ zip vs ss
545 m <- mergePorts biased rs
593 m <- mergePorts biased rs
546 xs <- replicateM (6 * 3) $ receiveChan m
594 xs <- replicateM (6 * 3) $ receiveChan m
547− True <- return $ xs == "abcacbbacbcacabcba"
548− liftIO $ putMVar done ()
595+ liftIO $ putMVar done $ xs == "abcacbbacbcacabcba"
549
596
550− takeMVar done
597+ verifyClient "Expected merged ports to match expected ordering" done
551
598
552 mergePorts :: Serializable a => Bool -> [ReceivePort a] -> Process (ReceivePort a)
599 mergePorts :: Serializable a => Bool -> [ReceivePort a] -> Process (ReceivePort a)
553 mergePorts True = mergePortsBiased
600 mergePorts True = mergePortsBiased
@@ -565,8 +612,9 @@ testTerminate TestTransport{..} = do
565 localNode <- newLocalNode testTransport initRemoteTable
612 localNode <- newLocalNode testTransport initRemoteTable
566 runProcess localNode $ do
613 runProcess localNode $ do
567 e <- try terminate :: Process (Either ProcessTerminationException ())
614 e <- try terminate :: Process (Either ProcessTerminationException ())
568− True <- return $ either show show e == show ProcessTerminationException
615+ if either show show e == show ProcessTerminationException
569− return ()
616+ then return ()
617+ else die "Unexpected result from terminate"
570
618
571testMonitorNode :: TestTransport -> Assertion
619testMonitorNode :: TestTransport -> Assertion
572testMonitorNode TestTransport{..} = do
620testMonitorNode TestTransport{..} = do
@@ -577,11 +625,12 @@ testMonitorNode TestTransport{..} = do
577
625
578 runProcess node2 $ do
626 runProcess node2 $ do
579 ref <- monitorNode (localNodeId node1)
627 ref <- monitorNode (localNodeId node1)
628+ receiveWait [
580− NodeMonitorNotification ref' nid DiedDisconnect <- expect
629+ match (\(NodeMonitorNotification ref' nid DiedDisconnect) ->
581− True <- return $ ref == ref' && nid == localNodeId node1
630+ return $ ref == ref' && nid == localNodeId node1)
582− liftIO $ putMVar done ()
631+ ] >>= liftIO . putMVar done
583
632
584− takeMVar done
633+ verifyClient "Expected NodeMonitorNotification with matching ref & nodeId" done
585
634
586testMonitorLiveNode :: TestTransport -> Assertion
635testMonitorLiveNode :: TestTransport -> Assertion
587testMonitorLiveNode TestTransport{..} = do
636testMonitorLiveNode TestTransport{..} = do
@@ -596,15 +645,16 @@ testMonitorLiveNode TestTransport{..} = do
596 liftIO $ putMVar ready ()
645 liftIO $ putMVar ready ()
597 liftIO $ takeMVar readyr
646 liftIO $ takeMVar readyr
598 send p ()
647 send p ()
648+ receiveWait [
599− NodeMonitorNotification ref' nid _ <- expect
649+ match (\(NodeMonitorNotification ref' nid _) ->
600− True <- return $ ref == ref' && nid == localNodeId node1
650+ (return $ ref == ref' && nid == localNodeId node1))
601− liftIO $ putMVar done ()
651+ ] >>= liftIO . putMVar done
602
652
603 takeMVar ready
653 takeMVar ready
604 closeLocalNode node1
654 closeLocalNode node1
605 putMVar readyr ()
655 putMVar readyr ()
606
656
607− takeMVar done
657+ verifyClient "Expected NodeMonitorNotification for LIVE node" done
608
658
609testMonitorChannel :: TestTransport -> Assertion
659testMonitorChannel :: TestTransport -> Assertion
610testMonitorChannel TestTransport{..} = do
660testMonitorChannel TestTransport{..} = do
@@ -614,18 +664,20 @@ testMonitorChannel TestTransport{..} = do
614 pid <- forkProcess node1 $ do
664 pid <- forkProcess node1 $ do
615 sport <- expect :: Process (SendPort ())
665 sport <- expect :: Process (SendPort ())
616 ref <- monitorPort sport
666 ref <- monitorPort sport
617− PortMonitorNotification ref' port' reason <- expect
667+ receiveWait [
618− -- reason might be DiedUnknownId if the receive port is GCed before the
668+ -- reason might be DiedUnknownId if the receive port is GCed before the
619− -- monitor is established (TODO: not sure that this is reasonable)
669+ -- monitor is established (TODO: not sure that this is reasonable)
670+ match (\(PortMonitorNotification ref' port' reason) ->
671+ return $ ref' == ref && port' == sendPortId sport &&
620− return $ ref' == ref && port' == sendPortId sport && (reason == DiedNormal || reason == DiedUnknownId)
672+ (reason == DiedNormal || reason == DiedUnknownId))
621− liftIO $ putMVar gotNotification ()
673+ ] >>= liftIO . putMVar gotNotification
622
674
623 runProcess node2 $ do
675 runProcess node2 $ do
624 (sport, _) <- newChan :: Process (SendPort (), ReceivePort ())
676 (sport, _) <- newChan :: Process (SendPort (), ReceivePort ())
625 send pid sport
677 send pid sport
626 liftIO $ threadDelay 100000
678 liftIO $ threadDelay 100000
627
679
628− takeMVar gotNotification
680+ verifyClient "Expected PortMonitorNotification" gotNotification
629
681
630testRegistry :: TestTransport -> Assertion
682testRegistry :: TestTransport -> Assertion
631testRegistry TestTransport{..} = do
683testRegistry TestTransport{..} = do
@@ -637,20 +689,28 @@ testRegistry TestTransport{..} = do
637
689
638 runProcess node $ do
690 runProcess node $ do
639 register "ping" pingServer
691 register "ping" pingServer
640− Just pid <- whereis "ping"
692+ whereis "ping" >>= liftIO . assertBool "Unexpected ping" . (== Just pingServer)
641− True <- return $ pingServer == pid
642 us <- getSelfPid
693 us <- getSelfPid
643 nsend "ping" (Pong us)
694 nsend "ping" (Pong us)
644− Ping pid' <- expect
695+ receiveWait [
645− True <- return $ pingServer == pid'
696+ matchIf (\(Ping pid') -> pingServer == pid') (const $ return ())
646− Left (ProcessRegistrationException "dead" Nothing) <- try $ register "dead" deadProcess
697+ ]
647− Left (ProcessRegistrationException "ping" (Just x)) <- try $ register "ping" deadProcess
698+ checkRegException "dead" Nothing deadProcess
648− True <- return $ x == pingServer
699+ checkRegException "ping" (Just pingServer) deadProcess
649− Left (ProcessRegistrationException "dead" Nothing) <- try $ unregister "dead"
700+ try (unregister "dead") >>= checkReg "dead" Nothing
650 liftIO $ putMVar done ()
701 liftIO $ putMVar done ()
651
702
652 takeMVar done
703 takeMVar done
653
704
705+ where
706+ checkRegException name pid dead =
707+ try (register name dead) >>= checkReg name pid
708+
709+ checkReg name pid res =
710+ case res of
711+ Left (ProcessRegistrationException name pid) -> return ()
712+ _ -> die $ "Unexpected Registration" ++ show res
713+
654testRegistryRemoteProcess :: TestTransport -> Assertion
714testRegistryRemoteProcess :: TestTransport -> Assertion
655testRegistryRemoteProcess TestTransport{..} = do
715testRegistryRemoteProcess TestTransport{..} = do
656 node1 <- newLocalNode testTransport initRemoteTable
716 node1 <- newLocalNode testTransport initRemoteTable
@@ -661,13 +721,13 @@ testRegistryRemoteProcess TestTransport{..} = do
661
721
662 runProcess node2 $ do
722 runProcess node2 $ do
663 register "ping" pingServer
723 register "ping" pingServer
664− Just pid <- whereis "ping"
724+ whereis "ping" >>= liftIO . assertBool "Unexpected ping" . (== Just pingServer)
665− True <- return $ pingServer == pid
666 us <- getSelfPid
725 us <- getSelfPid
667 nsend "ping" (Pong us)
726 nsend "ping" (Pong us)
668− Ping pid' <- expect
727+ receiveWait [
669− True <- return $ pingServer == pid'
728+ matchIf (\(Ping pid') -> pingServer == pid')
670− liftIO $ putMVar done ()
729+ (const $ liftIO $ putMVar done ())
730+ ]
671
731
672 takeMVar done
732 takeMVar done
673
733
@@ -675,7 +735,6 @@ testRemoteRegistry :: TestTransport -> Assertion
675testRemoteRegistry TestTransport{..} = do
735testRemoteRegistry TestTransport{..} = do
676 node1 <- newLocalNode testTransport initRemoteTable
736 node1 <- newLocalNode testTransport initRemoteTable
677 node2 <- newLocalNode testTransport initRemoteTable
737 node2 <- newLocalNode testTransport initRemoteTable
678− done <- newEmptyMVar
679
738
680 pingServer <- forkProcess node1 ping
739 pingServer <- forkProcess node1 ping
681 deadProcess <- forkProcess node1 (return ())
740 deadProcess <- forkProcess node1 (return ())
@@ -688,32 +747,34 @@ testRemoteRegistry TestTransport{..} = do
688 "ping" == label' && pid == pingServer)
747 "ping" == label' && pid == pingServer)
689 (\(RegisterReply _ _ _) -> return ()) ]
748 (\(RegisterReply _ _ _) -> return ()) ]
690
749
691− Just pid <- whereisRemote nid1 "ping"
750+ pid <- verifyWhereIsRemote nid1 "ping"
692− True <- return $ pingServer == pid
751+ liftIO $ assertBool "Expected pindServer to match pid" $ pingServer == pid
752+
693 us <- getSelfPid
753 us <- getSelfPid
694 nsendRemote nid1 "ping" (Pong us)
754 nsendRemote nid1 "ping" (Pong us)
695− Ping pid' <- expect
755+ receiveWait [
696− True <- return $ pingServer == pid'
756+ match (\(Ping pid') -> return $ pingServer == pid')
757+ ] >>= liftIO . assertBool "Expected Ping with ping server's ProcessId"
697
758
698 -- test that if process was not registered Nothing is returned
759 -- test that if process was not registered Nothing is returned
699 -- in owner field.
760 -- in owner field.
700 registerRemoteAsync nid1 "dead" deadProcess
761 registerRemoteAsync nid1 "dead" deadProcess
701− receiveWait [
702− matchIf (\(RegisterReply label' False Nothing) -> "dead" == label')
762+ receiveWait [ matchIf (\(RegisterReply label' f mPid) -> "dead" == label')
703− (\(RegisterReply _ _ _) -> return ()) ]
763+ (\(RegisterReply _ f mPid) -> return (not f && isNothing mPid))
764+ ] >>= liftIO . assertBool "Expected False Nothing in RegisterReply"
765+
704 registerRemoteAsync nid1 "ping" deadProcess
766 registerRemoteAsync nid1 "ping" deadProcess
705 receiveWait [
767 receiveWait [
706− matchIf (\(RegisterReply label' False (Just pid)) ->
768+ matchIf (\(RegisterReply label' False mPid) ->
707− "ping" == label' && pid == pingServer)
769+ "ping" == label' && isJust mPid)
708− (\(RegisterReply _ _ _) -> return ()) ]
770+ (\(RegisterReply _ f (Just pid'')) -> return (not f && pid'' == pingServer))
771+ ] >>= liftIO . assertBool "Expected False and (Just alreadyRegisteredPid) in RegisterReply"
772+
709 unregisterRemoteAsync nid1 "dead"
773 unregisterRemoteAsync nid1 "dead"
710 receiveWait [
774 receiveWait [
711− matchIf (\(RegisterReply label' False Nothing) ->
775+ matchIf (\(RegisterReply label' _ _) -> "dead" == label')
712− "dead" == label' && pid == pingServer)
776+ (\(RegisterReply _ f mPid) -> return (not f && isNothing mPid))
713− (\(RegisterReply _ _ _) -> return ()) ]
777+ ] >>= liftIO . assertBool "Expected False and Nothing in RegisterReply"
714− liftIO $ putMVar done ()
715−
716− takeMVar done
717
778
718testRemoteRegistryRemoteProcess :: TestTransport -> Assertion
779testRemoteRegistryRemoteProcess :: TestTransport -> Assertion
719testRemoteRegistryRemoteProcess TestTransport{..} = do
780testRemoteRegistryRemoteProcess TestTransport{..} = do
@@ -729,15 +790,15 @@ testRemoteRegistryRemoteProcess TestTransport{..} = do
729 receiveWait [
790 receiveWait [
730 matchIf (\(RegisterReply label' _ _) -> "ping" == label')
791 matchIf (\(RegisterReply label' _ _) -> "ping" == label')
731 (\(RegisterReply _ _ _) -> return ()) ]
792 (\(RegisterReply _ _ _) -> return ()) ]
732− Just pid <- whereisRemote nid1 "ping"
793+ pid <- verifyWhereIsRemote nid1 "ping"
733− True <- return $ pingServer == pid
794+ liftIO $ assertBool "Expected pingServer to match remote name" $ pingServer == pid
734 us <- getSelfPid
795 us <- getSelfPid
735 nsendRemote nid1 "ping" (Pong us)
796 nsendRemote nid1 "ping" (Pong us)
736− Ping pid' <- expect
797+ receiveWait [
737− True <- return $ pingServer == pid'
798+ match (\(Ping pid') -> return $ pingServer == pid')
738− liftIO $ putMVar done ()
799+ ] >>= liftIO . putMVar done
739
800
740− takeMVar done
801+ verifyClient "Expected Ping with ping server's ProcessId" done
741
802
742testSpawnLocal :: TestTransport -> Assertion
803testSpawnLocal :: TestTransport -> Assertion
743testSpawnLocal TestTransport{..} = do
804testSpawnLocal TestTransport{..} = do
@@ -751,15 +812,14 @@ testSpawnLocal TestTransport{..} = do
751 sport <- expect
812 sport <- expect
752 sendChan sport (1234 :: Int)
813 sendChan sport (1234 :: Int)
753
814
754− sport <- spawnChannelLocal $ \rport -> do
815+ sport <- spawnChannelLocal $
755− (1234 :: Int) <- receiveChan rport
816+ \rport -> (receiveChan rport :: Process Int) >>= send us
756− send us ()
757
817
758 send pid sport
818 send pid sport
759− () <- expect
760− liftIO $ putMVar done ()
819+ expect >>= liftIO . putMVar done
761
820
762− takeMVar done
821+ res <- takeMVar done
822+ assertBool "Expected 1234 :: Int" $ res == (1234 :: Int)
763
823
764testSpawnAsyncStrictness :: TestTransport -> Assertion
824testSpawnAsyncStrictness :: TestTransport -> Assertion
765testSpawnAsyncStrictness TestTransport{..} = do
825testSpawnAsyncStrictness TestTransport{..} = do
@@ -788,8 +848,9 @@ testReconnect TestTransport{..} = do
788 liftIO $ putMVar processA us
848 liftIO $ putMVar processA us
789 msg1 <- expect
849 msg1 <- expect
790 msg2 <- expect
850 msg2 <- expect
851+ liftIO $ do
791− True <- return $ msg1 == "message 1" && msg2 == "message 3"
852+ assertBool "messages did not match" $ msg1 == "message 1" && msg2 == "message 3"
792− liftIO $ putMVar sendTestOk ()
853+ putMVar sendTestOk ()
793
854
794 forkProcess node2 $ do
855 forkProcess node2 $ do
795 {-
856 {-
@@ -822,7 +883,7 @@ testReconnect TestTransport{..} = do
822 matchIf (\(RegisterReply label' _ _) -> "a" == label')
883 matchIf (\(RegisterReply label' _ _) -> "a" == label')
823 (\(RegisterReply _ _ _) -> return ()) ]
884 (\(RegisterReply _ _ _) -> return ()) ]
824
885
825− Just _ <- whereisRemote nid1 "a"
886+ _ <- whereisRemote nid1 "a"
826
887
827
888
828 -- Simulate network failure
889 -- Simulate network failure
@@ -841,9 +902,10 @@ testReconnect TestTransport{..} = do
841 (\(RegisterReply _ _ _) -> return ()) ]
902 (\(RegisterReply _ _ _) -> return ()) ]
842
903
843 -- Check
904 -- Check
844− Nothing <- whereisRemote nid1 "a" -- this will fail because the name is removed when the node is disconnected
905+ mPid <- whereisRemote nid1 "a" -- this will fail because the name is removed when the node is disconnected
906+ liftIO $ assertBool "Expected remote name to be lost" $ mPid == Nothing
845− Just _ <- whereisRemote nid1 "b" -- this will suceed because the value is set after thereconnect
907+ verifyWhereIsRemote nid1 "b" -- this will suceed because the value is set after thereconnect
846− Just _ <- whereisRemote nid1 "c"
908+ verifyWhereIsRemote nid1 "c"
847
909
848 liftIO $ putMVar registerTestOk ()
910 liftIO $ putMVar registerTestOk ()
849
911
@@ -884,19 +946,19 @@ testUSend usendPrim TestTransport{..} numMessages = do
884 isSorted _ = True
946 isSorted _ = True
885 -- The list can't be null since there are no failures after sending
947 -- The list can't be null since there are no failures after sending
886 -- the last message.
948 -- the last message.
887− True <- return $ isSorted msgs && not (null msgs)
949+ liftIO $ putMVar usendTestOk $ isSorted msgs && not (null msgs)
888− liftIO $ putMVar usendTestOk ()
889
950
890 forkProcess node2 $ do
951 forkProcess node2 $ do
891 them <- liftIO $ readMVar processA
952 them <- liftIO $ readMVar processA
892 getSelfPid >>= send them
953 getSelfPid >>= send them
893− () <- expect
954+ expect :: Process ()
894 forM_ [1..numMessages] $ \i -> do
955 forM_ [1..numMessages] $ \i -> do
895 liftIO $ testBreakConnection (nodeAddress nid1) (nodeAddress nid2)
956 liftIO $ testBreakConnection (nodeAddress nid1) (nodeAddress nid2)
896 usendPrim them i
957 usendPrim them i
897 liftIO (threadDelay 30000)
958 liftIO (threadDelay 30000)
898
959
899− takeMVar usendTestOk
960+ res <- takeMVar usendTestOk
961+ assertBool "Unexpected failure after sending last msg" res
900
962
901-- | Test 'matchAny'. This repeats the 'testMath' but with a proxy server
963-- | Test 'matchAny'. This repeats the 'testMath' but with a proxy server
902-- in between
964-- in between
@@ -922,14 +984,17 @@ testMatchAny TestTransport{..} = do
922 runProcess localNode $ do
984 runProcess localNode $ do
923 pid <- getSelfPid
985 pid <- getSelfPid
924 send mathServer (Add pid 1 2)
986 send mathServer (Add pid 1 2)
925− 3 <- expect :: Process Double
987+ three <- expect :: Process Double
926 send mathServer (Divide pid 8 2)
988 send mathServer (Divide pid 8 2)
927− 4 <- expect :: Process Double
989+ four <- expect :: Process Double
928 send mathServer (Divide pid 8 0)
990 send mathServer (Divide pid 8 0)
929− DivByZero <- expect
991+ divByZ <- expect
930− liftIO $ putMVar clientDone ()
992+ liftIO $ putMVar clientDone (three, four, divByZ)
931
993
932− takeMVar clientDone
994+ res <- takeMVar clientDone
995+ case res of
996+ (3, 4, DivByZero) -> return ()
997+ _ -> error "Unexpected result"
933
998
934-- | Test 'matchAny'. This repeats the 'testMath' but with a proxy server
999-- | Test 'matchAny'. This repeats the 'testMath' but with a proxy server
935-- in between, however we block 'Divide' requests ....
1000-- in between, however we block 'Divide' requests ....
@@ -956,12 +1021,13 @@ testMatchAnyHandle TestTransport{..} = do
956 runProcess localNode $ do
1021 runProcess localNode $ do
957 pid <- getSelfPid
1022 pid <- getSelfPid
958 send mathServer (Add pid 1 2)
1023 send mathServer (Add pid 1 2)
959− 3 <- expect :: Process Double
1024+ three <- expect :: Process Double
960 send mathServer (Divide pid 8 2)
1025 send mathServer (Divide pid 8 2)
961− Nothing <- (expectTimeout 100000) :: Process (Maybe Double)
1026+ res <- (expectTimeout 100000) :: Process (Maybe Double)
962− liftIO $ putMVar clientDone ()
1027+ liftIO $ putMVar clientDone $ three == 3 && res == Nothing
1028+
1029+ verifyClient "Expected Nothing (i.e. timeout)" clientDone
963
1030
964− takeMVar clientDone
965 where maybeForward :: ProcessId -> Message -> Process (Maybe ())
1031 where maybeForward :: ProcessId -> Message -> Process (Maybe ())
966 maybeForward s msg =
1032 maybeForward s msg =
967 handleMessage msg (\m@(Add _ _ _) -> send s m)
1033 handleMessage msg (\m@(Add _ _ _) -> send s m)
@@ -989,8 +1055,10 @@ testMatchAnyNoHandle TestTransport{..} = do
989 Just _ -> die "NONSENSE")
1055 Just _ -> die "NONSENSE")
990 ]
1056 ]
991 -- we *must* have removed the message from our mailbox though!!!
1057 -- we *must* have removed the message from our mailbox though!!!
992− Nothing <- receiveTimeout 100000 [ match (\(Add _ _ _) -> return ()) ]
1058+ res <- receiveTimeout 100000 [ match (\(Add _ _ _) -> return ()) ]
1059+ liftIO $ do
1060+ assertBool "Expected timeout!" $ res == Nothing
993− liftIO $ putMVar serverDone ()
1061+ putMVar serverDone ()
994 putMVar addr server
1062 putMVar addr server
995
1063
996 -- Client
1064 -- Client
@@ -1033,14 +1101,21 @@ testMatchAnyIf TestTransport{..} = do
1033 runProcess localNode $ do
1101 runProcess localNode $ do
1034 pid <- getSelfPid
1102 pid <- getSelfPid
1035 send server (pid, "foo")
1103 send server (pid, "foo")
1036− "foo" <- expect
1104+ foo <- expect
1037− send server (pid, "baz")
1105+ -- provoking what would be the wrong ordering is informative here...
1038− "baz" <- expect
1106+
1039 send server (pid, "bar")
1107 send server (pid, "bar")
1040− Nothing <- (expectTimeout 100000) :: Process (Maybe Double)
1108+ bar <- (expectTimeout 100000) :: Process (Maybe String) -- was Double o_O !?
1109+
1110+ send server (pid, "baz")
1111+ baz <- expect
1112+
1041− liftIO $ putMVar clientDone ()
1113+ liftIO $ putMVar clientDone (foo, bar, baz)
1114+
1115+ res <- takeMVar clientDone
1116+ let res' = res == ("foo", Nothing, "baz")
1117+ assertBool "Expected timeout due to type mismatch" res'
1042
1118
1043− takeMVar clientDone
1044 where tryHandleMessage :: Message -> Process (Maybe ())
1119 where tryHandleMessage :: Message -> Process (Maybe ())
1045 tryHandleMessage msg =
1120 tryHandleMessage msg =
1046 handleMessage msg (\(pid :: ProcessId, (m :: String))
1121 handleMessage msg (\(pid :: ProcessId, (m :: String))
@@ -1073,12 +1148,13 @@ testMatchMessageWithUnwrap TestTransport{..} = do
1073 runProcess localNode $ do
1148 runProcess localNode $ do
1074 pid <- getSelfPid
1149 pid <- getSelfPid
1075 send server (pid, wrapMessage ("foo" :: String))
1150 send server (pid, wrapMessage ("foo" :: String))
1076− "foo" <- expect
1151+ foo <- expect
1077 send server (pid, wrapMessage ("baz" :: String))
1152 send server (pid, wrapMessage ("baz" :: String))
1078− "baz" <- expect
1153+ baz <- expect
1079− liftIO $ putMVar clientDone ()
1154+ liftIO $ putMVar clientDone (foo, baz)
1080
1155
1081− takeMVar clientDone
1156+ res <- takeMVar clientDone
1157+ assertBool "Unexpected unwrapped results" $ res == ("foo", "baz")
1082
1158
1083-- Test 'receiveChanTimeout'
1159-- Test 'receiveChanTimeout'
1084testReceiveChanTimeout :: TestTransport -> Assertion
1160testReceiveChanTimeout :: TestTransport -> Assertion
@@ -1096,18 +1172,19 @@ testReceiveChanTimeout TestTransport{..} = do
1096
1172
1097 -- Wait for a message with a delay. No message arrives, we should get
1173 -- Wait for a message with a delay. No message arrives, we should get
1098 -- Nothing after the delay.
1174 -- Nothing after the delay.
1099− Nothing <- receiveChanTimeout 100000 rp
1175+ receiveChanTimeout 100000 rp >>= maybe (return ()) (const $ die "Expected Timeout")
1100
1176
1101 -- Let the sender know that it can send a message.
1177 -- Let the sender know that it can send a message.
1102 liftIO $ putMVar mvSender ()
1178 liftIO $ putMVar mvSender ()
1103
1179
1104 -- Wait for a message with a delay again. Now a message arrives after
1180 -- Wait for a message with a delay again. Now a message arrives after
1105 -- 0.1 seconds
1181 -- 0.1 seconds
1106− Just True <- receiveChanTimeout 20000000 rp
1182+ res <- receiveChanTimeout 20000000 rp >>= maybe (die "Timeout") return
1183+ liftIO $ assertBool "Expected result to be 'True'" res
1107
1184
1108 -- Wait for a message with zero timeout: non-blocking check. No message is
1185 -- Wait for a message with zero timeout: non-blocking check. No message is
1109 -- available, we get Nothing
1186 -- available, we get Nothing
1110− Nothing <- receiveChanTimeout 0 rp
1187+ receiveChanTimeout 0 rp >>= maybe (return ()) (const $ die "Expected Timeout")
1111
1188
1112 -- Let the sender know that it can send a message.
1189 -- Let the sender know that it can send a message.
1113 liftIO $ putMVar mvSender ()
1190 liftIO $ putMVar mvSender ()
@@ -1117,7 +1194,7 @@ testReceiveChanTimeout TestTransport{..} = do
1117 liftIO $ threadDelay 100000
1194 liftIO $ threadDelay 100000
1118 mb <- receiveChanTimeout 0 rp
1195 mb <- receiveChanTimeout 0 rp
1119 case mb of
1196 case mb of
1120− Just b -> do False <- return b; return ()
1197+ Just b -> do liftIO $ assertBool "Unexpected Message" $ not b
1121 _ -> loop
1198 _ -> loop
1122
1199
1123 liftIO $ putMVar done ()
1200 liftIO $ putMVar done ()
@@ -1154,8 +1231,8 @@ testReceiveChanFeatures TestTransport{..} = do
1154
1231
1155 rp1 <- mergePortsBiased [even <$> rpInt, rpBool]
1232 rp1 <- mergePortsBiased [even <$> rpInt, rpBool]
1156
1233
1157− True <- receiveChan rp1
1234+ receiveChan rp1 >>= liftIO . assertBool "Expected True"
1158− False <- receiveChan rp1
1235+ receiveChan rp1 >>= liftIO . assertBool "Expected False" . not
1159
1236
1160 -- Test Applicative instance
1237 -- Test Applicative instance
1161
1238
@@ -1164,7 +1241,7 @@ testReceiveChanFeatures TestTransport{..} = do
1164
1241
1165 let rp2 = pure (+) <*> rpInt <*> rpInt
1242 let rp2 = pure (+) <*> rpInt <*> rpInt
1166
1243
1167− 7 <- receiveChan rp2
1244+ receiveChan rp2 >>= liftIO . assertBool "Expected 7" . (== 7)
1168
1245
1169 -- Test Alternative instance
1246 -- Test Alternative instance
1170
1247
@@ -1173,8 +1250,8 @@ testReceiveChanFeatures TestTransport{..} = do
1173
1250
1174 let rp3 = (even <$> rpInt) <|> rpBool
1251 let rp3 = (even <$> rpInt) <|> rpBool
1175
1252
1176− False <- receiveChan rp3
1253+ receiveChan rp3 >>= liftIO . assertBool "Expected False" . not
1177− True <- receiveChan rp3
1254+ receiveChan rp3 >>= liftIO . assertBool "Expected True"
1178
1255
1179 -- Test Monad instance
1256 -- Test Monad instance
1180
1257
@@ -1188,17 +1265,86 @@ testReceiveChanFeatures TestTransport{..} = do
1188 then rpInt
1265 then rpInt
1189 else return 7
1266 else return 7
1190
1267
1191− 5 <- receiveChan rp4
1268+ receiveChan rp4 >>= liftIO . assertBool "Expected 5" . (== 5)
1192− 7 <- receiveChan rp4
1269+ receiveChan rp4 >>= liftIO . assertBool "Expected 7" . (== 7)
1193
1270
1194 liftIO $ putMVar done ()
1271 liftIO $ putMVar done ()
1195
1272
1196 takeMVar done
1273 takeMVar done
1197
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+
1198testKillLocal :: TestTransport -> Assertion
1345testKillLocal :: TestTransport -> Assertion
1199testKillLocal TestTransport{..} = do
1346testKillLocal TestTransport{..} = do
1200 localNode <- newLocalNode testTransport initRemoteTable
1347 localNode <- newLocalNode testTransport initRemoteTable
1201− done <- newEmptyMVar
1202
1348
1203 pid <- forkProcess localNode $ do
1349 pid <- forkProcess localNode $ do
1204 liftIO $ threadDelay 1000000
1350 liftIO $ threadDelay 1000000
@@ -1207,17 +1353,18 @@ testKillLocal TestTransport{..} = do
1207 ref <- monitor pid
1353 ref <- monitor pid
1208 us <- getSelfPid
1354 us <- getSelfPid
1209 kill pid "TestKill"
1355 kill pid "TestKill"
1210− ProcessMonitorNotification ref' pid' (DiedException ex) <- expect
1356+ mn <- expect
1357+ case mn of
1358+ ProcessMonitorNotification ref' pid' (DiedException ex) ->
1211− True <- return $ ref == ref' && pid == pid' && ex == "killed-by=" ++ show us ++ ",reason=TestKill"
1359+ case ref == ref' && pid == pid' && ex == "killed-by=" ++ show us ++ ",reason=TestKill" of
1212− liftIO $ putMVar done ()
1360+ True -> return ()
1213−
1361+ False -> die "Invalid ProcessMonitorNotification received"
1214− takeMVar done
1362+ _ -> die "Something went horribly wrong"
1215
1363
1216testKillRemote :: TestTransport -> Assertion
1364testKillRemote :: TestTransport -> Assertion
1217testKillRemote TestTransport{..} = do
1365testKillRemote TestTransport{..} = do
1218 node1 <- newLocalNode testTransport initRemoteTable
1366 node1 <- newLocalNode testTransport initRemoteTable
1219 node2 <- newLocalNode testTransport initRemoteTable
1367 node2 <- newLocalNode testTransport initRemoteTable
1220− done <- newEmptyMVar
1221
1368
1222 pid <- forkProcess node1 $ do
1369 pid <- forkProcess node1 $ do
1223 liftIO $ threadDelay 1000000
1370 liftIO $ threadDelay 1000000
@@ -1226,11 +1373,17 @@ testKillRemote TestTransport{..} = do
1226 ref <- monitor pid
1373 ref <- monitor pid
1227 us <- getSelfPid
1374 us <- getSelfPid
1228 kill pid "TestKill"
1375 kill pid "TestKill"
1376+ mn <- expect
1377+ case mn of
1229− ProcessMonitorNotification ref' pid' (DiedException reason) <- expect
1378+ ProcessMonitorNotification ref' pid' (DiedException reason) ->
1230− True <- return $ ref == ref' && pid == pid' && reason == "killed-by=" ++ show us ++ ",reason=TestKill"
1379+ case (ref == ref', pid == pid', reason == "killed-by=" ++ show us ++ ",reason=TestKill") of
1231− liftIO $ putMVar done ()
1380+ (True, True, True) -> return ()
1232−
1381+ (a, b, c) -> do
1233− takeMVar done
1382+ let a' = if a then "" else "Invalid ref"
1383+ let b' = if b then "" else "Invalid pid"
1384+ let c' = if c then "" else "Invalid message"
1385+ die $ unwords [a', b', c']
1386+ _ -> die "Received unexpected message"
1234
1387
1235testCatchesExit :: TestTransport -> Assertion
1388testCatchesExit :: TestTransport -> Assertion
1236testCatchesExit TestTransport{..} = do
1389testCatchesExit TestTransport{..} = do
@@ -1256,7 +1409,10 @@ testHandleMessageIf TestTransport{..} = do
1256 self <- getSelfPid
1409 self <- getSelfPid
1257 send self (5 :: Integer, 10 :: Integer)
1410 send self (5 :: Integer, 10 :: Integer)
1258 msg <- receiveWait [ matchMessage return ]
1411 msg <- receiveWait [ matchMessage return ]
1259− Nothing <- handleMessageIf msg (\() -> True) (\() -> die $ "whoops")
1412+ handleMessageIf msg
1413+ (\() -> True)
1414+ (\() -> die "whoops") >>= maybe (return ())
1415+ (const $ die "Expected Mismatch")
1260 handleMessageIf msg (\(x :: Integer, y :: Integer) -> x == 5 && y == 10)
1416 handleMessageIf msg (\(x :: Integer, y :: Integer) -> x == 5 && y == 10)
1261 (\input -> liftIO $ putMVar done input)
1417 (\input -> liftIO $ putMVar done input)
1262 return ()
1418 return ()
@@ -1301,8 +1457,11 @@ testDie TestTransport{..} = do
1301 (die ("foobar", 123 :: Int))
1457 (die ("foobar", 123 :: Int))
1302 `catchExit` \_from reason -> do
1458 `catchExit` \_from reason -> do
1303 -- TODO: should verify that 'from' has the right value
1459 -- TODO: should verify that 'from' has the right value
1304− True <- return $ reason == ("foobar", 123 :: Int)
1460+ let res = reason == ("foobar", 123 :: Int)
1305 liftIO $ putMVar done ()
1461 liftIO $ putMVar done ()
1462+ if res
1463+ then return ()
1464+ else die "Something went horribly wrong"
1306
1465
1307 takeMVar done
1466 takeMVar done
1308
1467
@@ -1316,8 +1475,11 @@ testPrettyExit TestTransport{..} = do
1316 `catch` \ex@(ProcessExitException from _) ->
1475 `catch` \ex@(ProcessExitException from _) ->
1317 let expected = "exit-from=" ++ (show from)
1476 let expected = "exit-from=" ++ (show from)
1318 in do
1477 in do
1319− True <- return $ (show ex) == expected
1478+ let res = (show ex) == expected
1320 liftIO $ putMVar done ()
1479 liftIO $ putMVar done ()
1480+ if res
1481+ then return ()
1482+ else die "Something went horribly wrong"
1321
1483
1322 takeMVar done
1484 takeMVar done
1323
1485
@@ -1333,17 +1495,26 @@ testExitLocal TestTransport{..} = do
1333 pid <- forkProcess localNode $ do
1495 pid <- forkProcess localNode $ do
1334 (liftIO (putMVar handlerSetUp ()) >> expect) `catchExit` \_from reason -> do
1496 (liftIO (putMVar handlerSetUp ()) >> expect) `catchExit` \_from reason -> do
1335 -- TODO: should verify that 'from' has the right value
1497 -- TODO: should verify that 'from' has the right value
1336− True <- return $ reason == "TestExit"
1498+ res <- return $ reason == "TestExit"
1337 liftIO $ putMVar supervisedDone ()
1499 liftIO $ putMVar supervisedDone ()
1500+ if res
1501+ then return ()
1502+ else die "Something went horribly wrong"
1338
1503
1339 runProcess localNode $ do
1504 runProcess localNode $ do
1340 liftIO $ takeMVar handlerSetUp
1505 liftIO $ takeMVar handlerSetUp
1341 ref <- monitor pid
1506 ref <- monitor pid
1342 exit pid "TestExit"
1507 exit pid "TestExit"
1343 -- This time the client catches the exception, so it dies normally
1508 -- This time the client catches the exception, so it dies normally
1509+ mn <- expect
1510+ case mn of
1344− ProcessMonitorNotification ref' pid' DiedNormal <- expect
1511+ ProcessMonitorNotification ref' pid' DiedNormal -> do
1345− True <- return $ ref == ref' && pid == pid'
1512+ let res = ref == ref' && pid == pid'
1346− liftIO $ putMVar supervisorDone ()
1513+ liftIO $ putMVar supervisorDone ()
1514+ if res
1515+ then return ()
1516+ else die "Something went horribly wrong"
1517+ _ -> die "Something went horribly wrong"
1347
1518
1348 takeMVar supervisedDone
1519 takeMVar supervisedDone
1349 takeMVar supervisorDone
1520 takeMVar supervisorDone
@@ -1359,128 +1530,97 @@ testExitRemote TestTransport{..} = do
1359 (receiveWait [] :: Process ()) -- block forever
1530 (receiveWait [] :: Process ()) -- block forever
1360 `catchExit` \_from reason -> do
1531 `catchExit` \_from reason -> do
1361 -- TODO: should verify that 'from' has the right value
1532 -- TODO: should verify that 'from' has the right value
1362− True <- return $ reason == "TestExit"
1533+ res <- return $ reason == "TestExit"
1363 liftIO $ putMVar supervisedDone ()
1534 liftIO $ putMVar supervisedDone ()
1535+ if res
1536+ then return ()
1537+ else die "Something went horribly wrong"
1364
1538
1365 runProcess node2 $ do
1539 runProcess node2 $ do
1366 ref <- monitor pid
1540 ref <- monitor pid
1367 exit pid "TestExit"
1541 exit pid "TestExit"
1542+ mn <- expect
1543+ case mn of
1368− ProcessMonitorNotification ref' pid' DiedNormal <- expect
1544+ ProcessMonitorNotification ref' pid' DiedNormal -> do
1369− True <- return $ ref == ref' && pid == pid'
1545+ res' <- return $ ref == ref' && pid == pid'
1370− liftIO $ putMVar supervisorDone ()
1546+ liftIO $ putMVar supervisorDone ()
1547+ if res'
1548+ then return ()
1549+ else die "Something went horribly wrong"
1550+ _ -> die "Something went horribly wrong"
1371
1551
1372 takeMVar supervisedDone
1552 takeMVar supervisedDone
1373 takeMVar supervisorDone
1553 takeMVar supervisorDone
1374
1554
1375testRegistryMonitoring :: TestTransport -> Assertion
1555testRegistryMonitoring :: TestTransport -> Assertion
1376testRegistryMonitoring TestTransport{..} = do
1556testRegistryMonitoring TestTransport{..} = do
1377− localNode <- newLocalNode testTransport initRemoteTable
1557+ node1 <- newLocalNode testTransport initRemoteTable
1378− remoteNode <- newLocalNode testTransport initRemoteTable
1558+ node2 <- newLocalNode testTransport initRemoteTable
1379− return ()
1380
1559
1381− -- Local process. Test if local process will be removed from
1560+ let nid = localNodeId node2
1382− -- registry when it dies.
1561+ pid <- forkProcess node1 $ do
1383− box <- newEmptyMVar
1562+ self <- getSelfPid
1384− runProcess localNode $ do
1563+ runUntilRegistered nid self
1385− pid <- spawnLocal $ do
1564+ say $ (show self) ++ " registered as " ++ regName
1386− expect
1565+ expect :: Process ()
1387− register "test" pid
1566+ say $ (show self) ++ " exiting normally"
1388− tpid <- whereis "test"
1389− if tpid == Just pid
1390− then do _ <- monitor pid
1391− send pid ()
1392− ProcessMonitorNotification{} <- expect
1393− tpid1 <- whereis "test"
1394− liftIO $ putMVar box (Nothing == tpid1)
1395− else liftIO $ putMVar box False
1396−
1397− takeMVar box >>= assertBool "expected local process to not be registered"
1398− return ()
1399−
1400− -- Remote process. Test if remote process entry is removed
1401− -- from registry when process dies.
1402− remote1 <- testRemote remoteNode
1403− runProcess localNode $
1404− let waitpoll = do
1405− w <- whereis "test" :: Process (Maybe ProcessId)
1406− forM_ w (const waitpoll)
1407− in do register "test" remote1
1408− -- [race-condition] invalid test if we force exit before waitpoll runs
1409− registered <- whereis "test"
1410− registered `shouldBe` (equalTo (Just remote1))
1411− send remote1 ()
1412− waitpoll
1413− return ()
1414− return ()
1415−
1416− -- Many labels. Test if all labels associated with process
1417− -- are removed from registry when it dies.
1418− remote2 <- testRemote remoteNode
1419− runProcess localNode $
1420− let waitpoll = do
1421− w1 <- whereis "test-3" :: Process (Maybe ProcessId)
1422− w2 <- whereis "test-4" :: Process (Maybe ProcessId)
1423− forM_ (w1 <|> w2) (const waitpoll)
1424− in do register "test-3" remote2
1425− register "test-4" remote2
1426− send remote2 ()
1427− waitpoll
1428− return ()
1429−
1430−{- XXX: waiting including patch for nsend for remote process
1431− remote3 <- testRemote remoteNode
1432− remote4 <- testRemote remoteNode
1433− -- test many labels
1434− runProcess localNode $ do
1435− register "test-3" remote3
1436− reregister "test-3" remote4
1437− send remote3 ()
1438− liftIO $ threadDelay 50000 -- XXX: racy
1439− monitor remote4
1440− nsend "test-3" ()
1441− ProcessMonitorNotification{} <- expect
1442− return ()
1443−-}
1444−
1445− -- Test registerRemoteAsync properties. Add a local process to
1446− -- remote registry and checks that it is removed
1447− -- when the process dies.
1448− remote5 <- testRemote remoteNode
1449− runProcess localNode $ do
1450− registerRemoteAsync (localNodeId remoteNode) "test" remote5
1451− receiveWait [
1452− match (\(RegisterReply _ True _) -> return ())
1453− ] >>= send remote5
1454− let waitpoll = do
1455− whereisRemoteAsync (localNodeId remoteNode) "test"
1456− receiveWait [
1457− match (\(WhereIsReply _ mr) -> forM_ mr (const waitpoll))
1458− ]
1459− waitpoll
1460
1567
1461− -- Add remote process to remote registry and checks if
1568+ runProcess node2 $ do
1462− -- entry is removed then process is dead.
1569+ register regName pid
1463− remote6 <- testRemote localNode
1570+ say $ regName ++ " registered to " ++ show pid
1464− runProcess localNode $ do
1571+ res <- whereis regName
1465− registerRemoteAsync (localNodeId remoteNode) "test" remote6
1572+ send pid ()
1466− receiveWait [
1573+ say $ " sent finish signal to " ++ show pid
1467− match (\(RegisterReply _ True _) -> return ())
1574+ us <- getSelfPid
1468− ] >>= send remote6
1575+ liftIO $ assertBool "expected (Just pid)" $ res == (Just pid)
1469− let waitpoll = do
1576+
1470− whereisRemoteAsync (localNodeId remoteNode) "test"
1577+
1471− receiveWait [
1578+ -- This delay isn't essential!
1472− match (\(WhereIsReply _ mr) -> forM_ mr (const waitpoll))
1579+ -- The test case passes perfectly fine without it (feel free to comment out
1473− ]
1580+ -- and see), however waiting a few seconds here, makes it much more likely
1474− waitpoll
1581+ -- that in delayUntilMaybeUnregistered we will hit the match case right
1475− where
1582+ -- away, and thus not be subjected to a 20 second delay. The value of 4
1476− testRemote node = do
1583+ -- seconds appears to work optimally on osx and across several linux distros
1477− -- test many labels
1584+ -- running in virtual machines (which is essentially what we do in CI)
1478− pidBox <- newEmptyMVar
1585+ void $ receiveTimeout 4000000 [ matchAny return ]
1479− forkProcess node $ do
1586+
1480− us <- getSelfPid
1587+ -- This delay doesn't serve much purpose in the happy path, however if some
1481− liftIO $ putMVar pidBox us
1588+ -- future patch breaks the cooperative behaviour of node controllers viz
1482− expect :: Process ()
1589+ -- remote process registration and notification taking place via ncEffectDied,
1590+ -- there would be the possibility of a race in the test case should we attempt
1591+ -- to evaluate `whereis regName` on node2 right away. In case the name is still
1592+ -- erroneously registered, observing the 20 second delay (or lack of), could at
1593+ -- least give a hint that something is wrong, and we give up our time slice
1594+ -- so that there's a higher change the registrations have been cleaned up
1595+ -- in either case.
1596+ runProcess node2 $ delayUntilMaybeUnregistered nid pid
1597+
1598+ regHere <- newEmptyMVar
1599+ runProcess node2 $ whereis regName >>= liftIO . putMVar regHere
1483− takeMVar pidBox
1600+ res <- takeMVar regHere
1601+ case res of
1602+ Nothing -> return ()
1603+ Just pid -> assertBool ("expected Nothing, but got " ++ show pid) False
1604+
1605+ where
1606+ runUntilRegistered nid us = do
1607+ whereisRemoteAsync nid regName
1608+ receiveWait [
1609+ matchIf (\(WhereIsReply n (Just p)) -> n == regName && p == us)
1610+ (const $ return ())
1611+ ]
1612+
1613+ delayUntilMaybeUnregistered nid p = do
1614+ whereisRemoteAsync nid regName
1615+ res <- receiveTimeout 20000000 {- 20 sec delay -} [
1616+ matchIf (\(WhereIsReply n p) -> n == regName && isNothing p)
1617+ (const $ return ())
1618+ ]
1619+ case res of
1620+ Just () -> return ()
1621+ Nothing -> delayUntilMaybeUnregistered nid p
1622+
1623+ regName = "testRegisterRemote"
1484
1624
1485testUnsafeSend :: TestTransport -> Assertion
1625testUnsafeSend :: TestTransport -> Assertion
1486testUnsafeSend TestTransport{..} = do
1626testUnsafeSend TestTransport{..} = do
@@ -1497,8 +1637,7 @@ testUnsafeSend TestTransport{..} = do
1497 void $ forkProcess localNode $ do
1637 void $ forkProcess localNode $ do
1498 serverPid <- liftIO $ takeMVar serverAddr
1638 serverPid <- liftIO $ takeMVar serverAddr
1499 getSelfPid >>= unsafeSend serverPid
1639 getSelfPid >>= unsafeSend serverPid
1500− () <- expect
1501− liftIO $ putMVar clientDone ()
1640+ expect >>= liftIO . putMVar clientDone
1502
1641
1503 takeMVar clientDone
1642 takeMVar clientDone
1504
1643
@@ -1517,8 +1656,7 @@ testUnsafeUSend TestTransport{..} = do
1517 void $ forkProcess localNode $ do
1656 void $ forkProcess localNode $ do
1518 serverPid <- liftIO $ takeMVar serverAddr
1657 serverPid <- liftIO $ takeMVar serverAddr
1519 getSelfPid >>= unsafeUSend serverPid
1658 getSelfPid >>= unsafeUSend serverPid
1520− () <- expect
1521− liftIO $ putMVar clientDone ()
1659+ expect >>= liftIO . putMVar clientDone
1522
1660
1523 takeMVar clientDone
1661 takeMVar clientDone
1524
1662
@@ -1529,8 +1667,7 @@ testUnsafeNSend TestTransport{..} = do
1529 localNode <- newLocalNode testTransport initRemoteTable
1667 localNode <- newLocalNode testTransport initRemoteTable
1530
1668
1531 pid <- forkProcess localNode $ do
1669 pid <- forkProcess localNode $ do
1532− () <- expect
1533− liftIO $ putMVar clientDone ()
1670+ expect >>= liftIO . putMVar clientDone
1534
1671
1535 void $ runProcess localNode $ do
1672 void $ runProcess localNode $ do
1536 register "foobar" pid
1673 register "foobar" pid
@@ -1548,8 +1685,7 @@ testUnsafeNSendRemote TestTransport{..} = do
1548 _ <- forkProcess localNode1 $ do
1685 _ <- forkProcess localNode1 $ do
1549 getSelfPid >>= register "foobar"
1686 getSelfPid >>= register "foobar"
1550 liftIO $ putMVar clientDone ()
1687 liftIO $ putMVar clientDone ()
1551− () <- expect
1552− liftIO $ putMVar clientDone ()
1688+ expect >>= liftIO . putMVar clientDone
1553
1689
1554 takeMVar clientDone
1690 takeMVar clientDone
1555 void $ runProcess localNode2 $ do
1691 void $ runProcess localNode2 $ do
@@ -1573,7 +1709,7 @@ testUnsafeSendChan TestTransport{..} = do
1573 serverPid <- liftIO $ takeMVar serverAddr
1709 serverPid <- liftIO $ takeMVar serverAddr
1574 (sp, rp) <- newChan
1710 (sp, rp) <- newChan
1575 unsafeSend serverPid sp
1711 unsafeSend serverPid sp
1576− () <- receiveChan rp
1712+ receiveChan rp :: Process ()
1577 liftIO $ putMVar clientDone ()
1713 liftIO $ putMVar clientDone ()
1578
1714
1579 takeMVar clientDone
1715 takeMVar clientDone
@@ -1587,8 +1723,8 @@ testCallLocal TestTransport{..} = do
1587 runProcess node $ do
1723 runProcess node $ do
1588 r <- (/=) <$> getSelfPid <*> callLocal getSelfPid
1724 r <- (/=) <$> getSelfPid <*> callLocal getSelfPid
1589 liftIO $ putMVar result r
1725 liftIO $ putMVar result r
1726+
1590− True <- takeMVar result
1727+ takeMVar result >>= assertBool "Expected 'True'"
1591− return ()
1592
1728
1593 -- Testing that when callLocal is interrupted, the worker is interrupted.
1729 -- Testing that when callLocal is interrupted, the worker is interrupted.
1594 ibox <- newIORef False
1730 ibox <- newIORef False
@@ -1605,10 +1741,9 @@ testCallLocal TestTransport{..} = do
1605 (send keeper ())
1741 (send keeper ())
1606 caller <- expect
1742 caller <- expect
1607 exit caller "test"
1743 exit caller "test"
1608− () <- expect
1744+ expect :: Process ()
1609− return ()
1745+
1610− True <- readIORef ibox
1746+ readIORef ibox >>= assertBool "Expected 'True'"
1611− return ()
1612
1747
1613 -- Testing that when the worker raises an exception, the exception is propagated to the parent.
1748 -- Testing that when the worker raises an exception, the exception is propagated to the parent.
1614 ibox2 <- newIORef False
1749 ibox2 <- newIORef False
@@ -1617,8 +1752,8 @@ testCallLocal TestTransport{..} = do
1617 liftIO $ writeIORef ibox2 $ case r of
1752 liftIO $ writeIORef ibox2 $ case r of
1618 Left (ErrorCall "e") -> True
1753 Left (ErrorCall "e") -> True
1619 _ -> False
1754 _ -> False
1755+
1620− True <- readIORef ibox
1756+ readIORef ibox >>= assertBool "Expected 'True'"
1621− return ()
1622
1757
1623 -- Test that caller waits for the worker in correct situation
1758 -- Test that caller waits for the worker in correct situation
1624 ibox3 <- newIORef False
1759 ibox3 <- newIORef False
@@ -1635,8 +1770,8 @@ testCallLocal TestTransport{..} = do
1635 liftIO $ putMVar result3 =<< readIORef ibox3
1770 liftIO $ putMVar result3 =<< readIORef ibox3
1636 worker <- expect
1771 worker <- expect
1637 send worker ()
1772 send worker ()
1773+
1638− True <- takeMVar result3
1774+ takeMVar result3 >>= assertBool "Expected 'True'"
1639− return ()
1640
1775
1641 -- Test that caller waits for the worker in case when caller gets an exception
1776 -- Test that caller waits for the worker in case when caller gets an exception
1642 ibox4 <- newIORef False
1777 ibox4 <- newIORef False
@@ -1652,13 +1787,9 @@ testCallLocal TestTransport{..} = do
1652 `finally` (liftIO $ putMVar result4 =<< readIORef ibox4)
1787 `finally` (liftIO $ putMVar result4 =<< readIORef ibox4)
1653 caller <- expect
1788 caller <- expect
1654 exit caller "hi!"
1789 exit caller "hi!"
1655− True <- takeMVar result4
1656− return ()
1657− -- XXX: Testing that when mask_ $ callLocal p runs p in masked state.
1658−
1659−
1660−
1661
1790
1791+ takeMVar result4 >>= assertBool "Expected 'True'"
1792+ -- XXX: Testing that when mask_ $ callLocal p runs p in masked state.
1662
1793
1663tests :: TestTransport -> IO [Test]
1794tests :: TestTransport -> IO [Test]
1664tests testtrans = return [
1795tests testtrans = return [
@@ -1683,6 +1814,7 @@ tests testtrans = return [
1683 , testCase "MatchMessageUnwrap" (testMatchMessageWithUnwrap testtrans)
1814 , testCase "MatchMessageUnwrap" (testMatchMessageWithUnwrap testtrans)
1684 , testCase "ReceiveChanTimeout" (testReceiveChanTimeout testtrans)
1815 , testCase "ReceiveChanTimeout" (testReceiveChanTimeout testtrans)
1685 , testCase "ReceiveChanFeatures" (testReceiveChanFeatures testtrans)
1816 , testCase "ReceiveChanFeatures" (testReceiveChanFeatures testtrans)
1817+ , testCase "ChanLifecycle" (testChanLifecycle testtrans)
1686 , testCase "KillLocal" (testKillLocal testtrans)
1818 , testCase "KillLocal" (testKillLocal testtrans)
1687 , testCase "KillRemote" (testKillRemote testtrans)
1819 , testCase "KillRemote" (testKillRemote testtrans)
1688 , testCase "Die" (testDie testtrans)
1820 , testCase "Die" (testDie testtrans)
@@ -1692,7 +1824,7 @@ tests testtrans = return [
1692 , testCase "MaskRestoreScope" (testMaskRestoreScope testtrans)
1824 , testCase "MaskRestoreScope" (testMaskRestoreScope testtrans)
1693 , testCase "ExitLocal" (testExitLocal testtrans)
1825 , testCase "ExitLocal" (testExitLocal testtrans)
1694 , testCase "ExitRemote" (testExitRemote testtrans)
1826 , testCase "ExitRemote" (testExitRemote testtrans)
1695− , testCase "TestRegistryMonitor" (testRegistryMonitoring testtrans)
1827+ , testCase "RegistryMonitoring" (testRegistryMonitoring testtrans)
1696 , testCase "TextCallLocal" (testCallLocal testtrans)
1828 , testCase "TextCallLocal" (testCallLocal testtrans)
1697 -- Unsafe Primitives
1829 -- Unsafe Primitives
1698 , testCase "TestUnsafeSend" (testUnsafeSend testtrans)
1830 , testCase "TestUnsafeSend" (testUnsafeSend testtrans)
@@ -1745,17 +1877,3 @@ tests testtrans = return [
1745 , testCase "MonitorUnreachable" (testMonitorUnreachable testtrans True False)
1877 , testCase "MonitorUnreachable" (testMonitorUnreachable testtrans True False)
1746 ]
1878 ]
1747 ]
1879 ]
1748−
1749−syncBreakConnection :: (NT.EndPointAddress -> NT.EndPointAddress -> IO ()) -> LocalNode -> LocalNode -> IO ()
1750−syncBreakConnection breakConnection nid0 nid1 = do
1751− m <- newEmptyMVar
1752− _ <- forkProcess nid1 $ getSelfPid >>= liftIO . putMVar m
1753− runProcess nid0 $ do
1754− them <- liftIO $ takeMVar m
1755− pinger <- spawnLocal $ forever $ send them ()
1756− _ <- monitorNode (localNodeId nid1)
1757− liftIO $ breakConnection (nodeAddress $ localNodeId nid0)
1758− (nodeAddress $ localNodeId nid1)
1759− NodeMonitorNotification _ _ _ <- expect
1760− kill pinger "finished"
1761− return ()
distributed-process-tests/src/Control/Distributed/Process/Tests/Closure.hs+84−45
@@ -169,8 +169,11 @@ testUnclosure TestTransport{..} rtable = do
169 node <- newLocalNode testTransport rtable
169 node <- newLocalNode testTransport rtable
170 done <- newEmptyMVar
170 done <- newEmptyMVar
171 forkProcess node $ do
171 forkProcess node $ do
172− 120 <- join . unClosure $ factorialClosure 5
172+ i <- join . unClosure $ factorialClosure 5
173 liftIO $ putMVar done ()
173 liftIO $ putMVar done ()
174+ if i == 720
175+ then return ()
176+ else error "Something went horribly wrong"
174 takeMVar done
177 takeMVar done
175
178
176testBind :: TestTransport -> RemoteTable -> Assertion
179testBind :: TestTransport -> RemoteTable -> Assertion
@@ -180,8 +183,11 @@ testBind TestTransport{..} rtable = do
180 runProcess node $ do
183 runProcess node $ do
181 us <- getSelfPid
184 us <- getSelfPid
182 join . unClosure $ sendFac 6 us
185 join . unClosure $ sendFac 6 us
183− (720 :: Int) <- expect
186+ (i :: Int) <- expect
184 liftIO $ putMVar done ()
187 liftIO $ putMVar done ()
188+ if i == 720
189+ then return ()
190+ else error "Something went horribly wrong"
185 takeMVar done
191 takeMVar done
186
192
187testSendPureClosure :: TestTransport -> RemoteTable -> Assertion
193testSendPureClosure :: TestTransport -> RemoteTable -> Assertion
@@ -194,7 +200,7 @@ testSendPureClosure TestTransport{..} rtable = do
194 addr <- forkProcess node $ do
200 addr <- forkProcess node $ do
195 cl <- expect
201 cl <- expect
196 fn <- unClosure cl :: Process (Int -> Int)
202 fn <- unClosure cl :: Process (Int -> Int)
197− 13 <- return $ fn 6
203+ (_ :: Int) <- return $ fn 6
198 liftIO $ putMVar serverDone ()
204 liftIO $ putMVar serverDone ()
199 putMVar serverAddr addr
205 putMVar serverAddr addr
200
206
@@ -218,8 +224,11 @@ testSendIOClosure TestTransport{..} rtable = do
218 liftIO $ do
224 liftIO $ do
219 someMVar <- newEmptyMVar
225 someMVar <- newEmptyMVar
220 io someMVar
226 io someMVar
221− 5 <- readMVar someMVar
227+ i <- readMVar someMVar
222 putMVar serverDone ()
228 putMVar serverDone ()
229+ if i == 5
230+ then return ()
231+ else error "Something went horribly wrong"
223 putMVar serverAddr addr
232 putMVar serverAddr addr
224
233
225 forkIO $ do
234 forkIO $ do
@@ -248,8 +257,10 @@ testSendProcClosure TestTransport{..} rtable = do
248 runProcess node $ do
257 runProcess node $ do
249 pid <- getSelfPid
258 pid <- getSelfPid
250 send theirAddr (cpSend $(mkStatic 'sdictInt) pid)
259 send theirAddr (cpSend $(mkStatic 'sdictInt) pid)
251− 5 <- expect :: Process Int
260+ i <- expect :: Process Int
261+ if i == 5
252− liftIO $ putMVar clientDone ()
262+ then liftIO $ putMVar clientDone ()
263+ else error "Something went horribly wrong"
253
264
254 takeMVar clientDone
265 takeMVar clientDone
255
266
@@ -269,8 +280,9 @@ testSpawn TestTransport{..} rtable = do
269 pid <- getSelfPid
280 pid <- getSelfPid
270 pid' <- spawn nid (sendPidClosure pid)
281 pid' <- spawn nid (sendPidClosure pid)
271 pid'' <- expect
282 pid'' <- expect
272− True <- return $ pid' == pid''
283+ if pid' == pid''
273− liftIO $ putMVar clientDone ()
284+ then liftIO $ putMVar clientDone ()
285+ else error "Something went horribly wrong"
274
286
275 takeMVar clientDone
287 takeMVar clientDone
276
288
@@ -294,8 +306,9 @@ testSpawnRace TestTransport{..} rtable = do
294 spawnLocal $ spawn (localNodeId node2) (sendPidClosure pid) >>= send pid
306 spawnLocal $ spawn (localNodeId node2) (sendPidClosure pid) >>= send pid
295 pid' <- expect :: Process ProcessId
307 pid' <- expect :: Process ProcessId
296 pid'' <- expect :: Process ProcessId
308 pid'' <- expect :: Process ProcessId
297− True <- return $ pid' == pid''
309+ if pid' == pid''
298− return ()
310+ then return ()
311+ else error "Something went horribly wrong"
299
312
300 where
313 where
301
314
@@ -332,8 +345,10 @@ testCall TestTransport{..} rtable = do
332 node <- newLocalNode testTransport rtable
345 node <- newLocalNode testTransport rtable
333 nid <- readMVar serverNodeAddr
346 nid <- readMVar serverNodeAddr
334 runProcess node $ do
347 runProcess node $ do
335− (120 :: Int) <- call $(mkStatic 'sdictInt) nid (factorialClosure 5)
348+ (a :: Int) <- call $(mkStatic 'sdictInt) nid (factorialClosure 5)
349+ if a == 120
336− liftIO $ putMVar clientDone ()
350+ then liftIO $ putMVar clientDone ()
351+ else error "something went horribly wrong"
337
352
338 takeMVar clientDone
353 takeMVar clientDone
339
354
@@ -350,8 +365,10 @@ testCallBind TestTransport{..} rtable = do
350 node <- newLocalNode testTransport rtable
365 node <- newLocalNode testTransport rtable
351 nid <- readMVar serverNodeAddr
366 nid <- readMVar serverNodeAddr
352 runProcess node $ do
367 runProcess node $ do
353− (120 :: Int) <- call $(mkStatic 'sdictInt) nid (factorial' 5)
368+ (a :: Int) <- call $(mkStatic 'sdictInt) nid (factorial' 5)
369+ if a == 120
354− liftIO $ putMVar clientDone ()
370+ then liftIO $ putMVar clientDone ()
371+ else error "Something went horribly wrong"
355
372
356 takeMVar clientDone
373 takeMVar clientDone
357
374
@@ -362,9 +379,11 @@ testSeq TestTransport{..} rtable = do
362 runProcess node $ do
379 runProcess node $ do
363 us <- getSelfPid
380 us <- getSelfPid
364 join . unClosure $ sendFac 5 us `seqCP` sendFac 6 us
381 join . unClosure $ sendFac 5 us `seqCP` sendFac 6 us
365− 120 :: Int <- expect
382+ a :: Int <- expect
366− 720 :: Int <- expect
383+ b :: Int <- expect
384+ if a == 120 && b == 720
367− liftIO $ putMVar done ()
385+ then liftIO $ putMVar done ()
386+ else error "Something went horribly wrong"
368 takeMVar done
387 takeMVar done
369
388
370-- Test 'spawnSupervised'
389-- Test 'spawnSupervised'
@@ -393,25 +412,33 @@ testSpawnSupervised TestTransport{..} rtable = do
393 throw supervisorDeath
412 throw supervisorDeath
394
413
395 forkProcess node2 $ do
414 forkProcess node2 $ do
396− [super, child] <- liftIO $ mapM readMVar [superPid, childPid]
415+ res <- liftIO $ mapM readMVar [superPid, childPid]
416+ case res of
417+ [super, child] -> do
397− ref <- monitor child
418+ ref <- monitor child
398− self <- getSelfPid
419+ self <- getSelfPid
399− let waitForMOrL = do
420+ let waitForMOrL = do
400− liftIO $ threadDelay 10000
421+ liftIO $ threadDelay 10000
401− mpinfo <- getProcessInfo child
422+ mpinfo <- getProcessInfo child
402− case mpinfo of
423+ case mpinfo of
403− Nothing -> waitForMOrL
424+ Nothing -> waitForMOrL
404− Just pinfo ->
425+ Just pinfo ->
405− unless (isJust $ lookup self (infoMonitors pinfo)) waitForMOrL
426+ unless (isJust $ lookup self (infoMonitors pinfo)) waitForMOrL
406− waitForMOrL
427+ waitForMOrL
407− liftIO $ putMVar linkUp ()
428+ liftIO $ putMVar linkUp ()
408− -- because monitor message was sent before message to process
429+ -- because monitor message was sent before message to process
409− -- we hope that it will be processed before
430+ -- we hope that it will be processed before
431+ res' <- expect
432+ case res' of
410− ProcessMonitorNotification ref' pid' (DiedException e) <- expect
433+ (ProcessMonitorNotification ref' pid' (DiedException e)) ->
411− True <- return $ ref' == ref
412− && pid' == child
434+ if (ref' == ref && pid' == child &&
435+ e == show (ProcessLinkException super
413− && e == show (ProcessLinkException super (DiedException (show supervisorDeath)))
436+ (DiedException (show supervisorDeath))))
414− liftIO $ putMVar thirdProcessDone ()
437+ then liftIO $ putMVar thirdProcessDone ()
438+ else error "Something went horribly wrong"
439+ _ -> error "Something went horribly wrong"
440+
441+ _ -> die $ "Something went horribly wrong"
415
442
416 takeMVar thirdProcessDone
443 takeMVar thirdProcessDone
417 where
444 where
@@ -426,8 +453,10 @@ testSpawnInvalid TestTransport{..} rtable = do
426 (pid, ref) <- spawnMonitor (localNodeId node) (closure (staticLabel "ThisDoesNotExist") empty)
453 (pid, ref) <- spawnMonitor (localNodeId node) (closure (staticLabel "ThisDoesNotExist") empty)
427 ProcessMonitorNotification ref' pid' _reason <- expect
454 ProcessMonitorNotification ref' pid' _reason <- expect
428 -- Depending on the exact interleaving, reason might be NoProc or the exception thrown by the absence of the static closure
455 -- Depending on the exact interleaving, reason might be NoProc or the exception thrown by the absence of the static closure
429− True <- return $ ref' == ref && pid == pid'
456+ res <- return $ ref' == ref && pid == pid'
457+ if res == True
430− liftIO $ putMVar done ()
458+ then liftIO $ putMVar done ()
459+ else error "Something went horribly wrong"
431 takeMVar done
460 takeMVar done
432
461
433testClosureExpect :: TestTransport -> RemoteTable -> Assertion
462testClosureExpect :: TestTransport -> RemoteTable -> Assertion
@@ -439,8 +468,10 @@ testClosureExpect TestTransport{..} rtable = do
439 us <- getSelfPid
468 us <- getSelfPid
440 them <- spawn nodeId $ cpExpect $(mkStatic 'sdictInt) `bindCP` cpSend $(mkStatic 'sdictInt) us
469 them <- spawn nodeId $ cpExpect $(mkStatic 'sdictInt) `bindCP` cpSend $(mkStatic 'sdictInt) us
441 send them (1234 :: Int)
470 send them (1234 :: Int)
442− (1234 :: Int) <- expect
471+ (res :: Int) <- expect
472+ if res == 1234
443− liftIO $ putMVar done ()
473+ then liftIO $ putMVar done ()
474+ else error "Something went horribly wrong"
444 takeMVar done
475 takeMVar done
445
476
446testSpawnChannel :: TestTransport -> RemoteTable -> Assertion
477testSpawnChannel :: TestTransport -> RemoteTable -> Assertion
@@ -465,8 +496,10 @@ testTDict TestTransport{..} rtable = do
465 done <- newEmptyMVar
496 done <- newEmptyMVar
466 [node1, node2] <- replicateM 2 $ newLocalNode testTransport rtable
497 [node1, node2] <- replicateM 2 $ newLocalNode testTransport rtable
467 forkProcess node1 $ do
498 forkProcess node1 $ do
468− True <- call $(functionTDict 'isPrime) (localNodeId node2) ($(mkClosure 'isPrime) (79 :: Integer))
499+ res <- call $(functionTDict 'isPrime) (localNodeId node2) ($(mkClosure 'isPrime) (79 :: Integer))
500+ if res == True
469− liftIO $ putMVar done ()
501+ then liftIO $ putMVar done ()
502+ else error "Something went horribly wrong..."
470 takeMVar done
503 takeMVar done
471
504
472testFib :: TestTransport -> RemoteTable -> Assertion
505testFib :: TestTransport -> RemoteTable -> Assertion
@@ -477,8 +510,11 @@ testFib TestTransport{..} rtable = do
477 forkProcess (head nodes) $ do
510 forkProcess (head nodes) $ do
478 (sport, rport) <- newChan
511 (sport, rport) <- newChan
479 spawnLocal $ dfib (map localNodeId nodes, sport, 10)
512 spawnLocal $ dfib (map localNodeId nodes, sport, 10)
480− 55 <- receiveChan rport :: Process Integer
513+ ff <- receiveChan rport :: Process Integer
481 liftIO $ putMVar done ()
514 liftIO $ putMVar done ()
515+ if ff /= 55
516+ then die $ "Something went horribly wrong"
517+ else return ()
482
518
483 takeMVar done
519 takeMVar done
484
520
@@ -486,7 +522,7 @@ testSpawnReconnect :: TestTransport -> RemoteTable -> Assertion
486testSpawnReconnect testtrans@TestTransport{..} rtable = do
522testSpawnReconnect testtrans@TestTransport{..} rtable = do
487 [node1, node2] <- replicateM 2 $ newLocalNode testTransport rtable
523 [node1, node2] <- replicateM 2 $ newLocalNode testTransport rtable
488 let nid1 = localNodeId node1
524 let nid1 = localNodeId node1
489− nid2 = localNodeId node2
525+ -- nid2 = localNodeId node2
490 done <- newEmptyMVar
526 done <- newEmptyMVar
491 iv <- newMVar (0 :: Int)
527 iv <- newMVar (0 :: Int)
492
528
@@ -503,9 +539,12 @@ testSpawnReconnect testtrans@TestTransport{..} rtable = do
503 liftIO $ threadDelay 100000
539 liftIO $ threadDelay 100000
504
540
505 count <- liftIO $ takeMVar iv
541 count <- liftIO $ takeMVar iv
506− True <- return $ count == 2 || count == 3 -- It depends on which message we get first in 'spawn'
542+ res <- return $ count == 2 || count == 3 -- It depends on which message we get first in 'spawn'
507
543
508 liftIO $ putMVar done ()
544 liftIO $ putMVar done ()
545+ if res /= True
546+ then error "Something went horribly wrong"
547+ else return ()
509
548
510 takeMVar done
549 takeMVar done
511
550
distributed-process-tests/src/Control/Distributed/Process/Tests/Internal/Utils.hs+16−5
@@ -20,6 +20,7 @@ module Control.Distributed.Process.Tests.Internal.Utils
20 -- ping !
20 -- ping !
21 , Ping(Ping)
21 , Ping(Ping)
22 , ping
22 , ping
23+ , pause
23 , shouldBe
24 , shouldBe
24 , shouldMatch
25 , shouldMatch
25 , shouldContain
26 , shouldContain
@@ -73,15 +74,16 @@ import Control.Concurrent
73import Control.Concurrent.MVar
74import Control.Concurrent.MVar
74 ( putMVar
75 ( putMVar
75 )
76 )
76−import Control.Distributed.Process
77+import Control.Distributed.Process hiding (finally, catch)
77import Control.Distributed.Process.Node
78import Control.Distributed.Process.Node
78import Control.Distributed.Process.Serializable()
79import Control.Distributed.Process.Serializable()
79
80
80import Control.Exception (AsyncException(ThreadKilled), SomeException)
81import Control.Exception (AsyncException(ThreadKilled), SomeException)
81−import Control.Monad (forever)
82+import Control.Monad (forever, void)
83+import Control.Monad.Catch (finally, catch)
82import Control.Monad.STM (atomically)
84import Control.Monad.STM (atomically)
83import Control.Rematch hiding (match)
85import Control.Rematch hiding (match)
84−import Control.Rematch.Run
86+import Control.Rematch.Run
85import Data.Binary
87import Data.Binary
86import Data.Typeable (Typeable)
88import Data.Typeable (Typeable)
87
89
@@ -89,6 +91,7 @@ import Test.HUnit (Assertion, assertFailure)
89import Test.HUnit.Base (assertBool)
91import Test.HUnit.Base (assertBool)
90
92
91import GHC.Generics
93import GHC.Generics
94+import System.Timeout (timeout)
92
95
93-- | A mutable cell containing a test result.
96-- | A mutable cell containing a test result.
94type TestResult a = MVar a
97type TestResult a = MVar a
@@ -107,10 +110,18 @@ data TestProcessControl = Stop | Go | Report ProcessId
107
110
108instance Binary TestProcessControl where
111instance Binary TestProcessControl where
109
112
113+data Private = Private
114+ deriving (Typeable, Generic)
115+instance Binary Private where
116+
110-- | Does exactly what it says on the tin, doing so in the @Process@ monad.
117-- | Does exactly what it says on the tin, doing so in the @Process@ monad.
111noop :: Process ()
118noop :: Process ()
112noop = return ()
119noop = return ()
113
120
121+pause :: Int -> Process ()
122+pause delay =
123+ void $ receiveTimeout delay [ match (\Private -> return ()) ]
124+
114synchronisedAssertion :: Eq a
125synchronisedAssertion :: Eq a
115 => String
126 => String
116 -> LocalNode
127 -> LocalNode
@@ -223,10 +234,10 @@ testProcessReport pid = do
223tryRunProcess :: LocalNode -> Process () -> IO ()
234tryRunProcess :: LocalNode -> Process () -> IO ()
224tryRunProcess node p = do
235tryRunProcess node p = do
225 tid <- liftIO myThreadId
236 tid <- liftIO myThreadId
237+ void $ timeout (1000000 * 60 * 5 :: Int) $
226− runProcess node $ catch p (\e -> liftIO $ throwTo tid (e::SomeException))
238+ runProcess node $ catch p (\e -> liftIO $ throwTo tid (e::SomeException))
227
239
228tryForkProcess :: LocalNode -> Process () -> IO ProcessId
240tryForkProcess :: LocalNode -> Process () -> IO ProcessId
229tryForkProcess node p = do
241tryForkProcess node p = do
230 tid <- liftIO myThreadId
242 tid <- liftIO myThreadId
231 forkProcess node $ catch p (\e -> liftIO $ throwTo tid (e::SomeException))
243 forkProcess node $ catch p (\e -> liftIO $ throwTo tid (e::SomeException))
232−
distributed-process-tests/src/Control/Distributed/Process/Tests/Mx.hs+348−126
@@ -1,12 +1,23 @@
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−
8+import Control.Distributed.Process hiding (bracket, finally, try)
7−import Control.Concurrent (threadDelay)
8−import Control.Distributed.Process
9+import Control.Distributed.Process.Internal.Types
10+ ( ProcessExitException(..)
11+ , unsafeCreateUnencodedMessage
12+ )
9import Control.Distributed.Process.Node
13import Control.Distributed.Process.Node
14+import qualified Control.Distributed.Process.UnsafePrimitives as Unsafe
15+ ( send
16+ , nsend
17+ , nsendRemote
18+ , usend
19+ , sendChan
20+ )
10import Control.Distributed.Process.Management
21import Control.Distributed.Process.Management
11 ( MxEvent(..)
22 ( MxEvent(..)
12 , MxAgentId(..)
23 , MxAgentId(..)
@@ -21,15 +32,15 @@ import Control.Distributed.Process.Management
21 , mxUpdateLocal
32 , mxUpdateLocal
22 , mxNotify
33 , mxNotify
23 , mxBroadcast
34 , mxBroadcast
24− , mxGetId
25 )
35 )
26−import Control.Monad (void)
36+import Control.Monad (void, unless)
37+import Control.Monad.Catch(finally, bracket, try)
27import Control.Rematch (equalTo)
38import Control.Rematch (equalTo)
28import Data.Binary
39import Data.Binary
29−import Data.List (find, sort)
40+import Data.List (find, sort, intercalate)
30−import Data.Maybe (isJust)
41+import Data.Maybe (isJust, fromJust, isNothing, fromMaybe, catMaybes)
31import Data.Typeable
42import Data.Typeable
32−import GHC.Generics
43+import GHC.Generics hiding (from)
33#if ! MIN_VERSION_base(4,6,0)
44#if ! MIN_VERSION_base(4,6,0)
34import Prelude hiding (catch, log)
45import Prelude hiding (catch, log)
35#endif
46#endif
@@ -45,7 +56,17 @@ data Publish = Publish
45
56
46instance Binary Publish where
57instance Binary Publish where
47
58
59+awaitExit :: ProcessId -> Process ()
60+awaitExit pid =
61+ withMonitorRef pid $ \ref -> do
62+ receiveWait
63+ [ matchIf (\(ProcessMonitorNotification r _ _) -> r == ref)
64+ (\_ -> return ())
65+ ]
66+ where
67+ withMonitorRef p = bracket (monitor p) unmonitor
68+
48−testAgentBroadcast :: TestResult () -> Process ()
69+testAgentBroadcast :: TestResult (Maybe ()) -> Process ()
49testAgentBroadcast result = do
70testAgentBroadcast result = do
50 (resultSP, resultRP) <- newChan :: Process (SendPort (), ReceivePort ())
71 (resultSP, resultRP) <- newChan :: Process (SendPort (), ReceivePort ())
51
72
@@ -60,7 +81,7 @@ testAgentBroadcast result = do
60 mxNotify ()
81 mxNotify ()
61 -- 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
62 -- 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.
63− stash result =<< receiveChan resultRP
84+ stash result =<< receiveChanTimeout 10000000 resultRP
64
85
65 kill publisher "finished"
86 kill publisher "finished"
66 kill consumer "finished"
87 kill consumer "finished"
@@ -68,7 +89,7 @@ testAgentBroadcast result = do
68testAgentDualInput :: TestResult (Maybe Int) -> Process ()
89testAgentDualInput :: TestResult (Maybe Int) -> Process ()
69testAgentDualInput result = do
90testAgentDualInput result = do
70 (sp, rp) <- newChan
91 (sp, rp) <- newChan
71− _ <- mxAgent (MxAgentId "sum-agent") (0 :: Int) [
92+ s <- mxAgent (MxAgentId "sum-agent") (0 :: Int) [
72 mxSink $ (\(i :: Int) -> do
93 mxSink $ (\(i :: Int) -> do
73 mxSetLocal . (+i) =<< mxGetLocal
94 mxSetLocal . (+i) =<< mxGetLocal
74 i' <- mxGetLocal
95 i' <- mxGetLocal
@@ -78,6 +99,8 @@ testAgentDualInput result = do
78 else mxReady)
99 else mxReady)
79 ]
100 ]
80
101
102+ mRef <- monitor s
103+
81 mxNotify (1 :: Int)
104 mxNotify (1 :: Int)
82 nsend "sum-agent" (3 :: Int)
105 nsend "sum-agent" (3 :: Int)
83 mxNotify (2 :: Int)
106 mxNotify (2 :: Int)
@@ -85,6 +108,10 @@ testAgentDualInput result = do
85 mxNotify (5 :: Int)
108 mxNotify (5 :: Int)
86
109
87 stash result =<< receiveChanTimeout 10000000 rp
110 stash result =<< receiveChanTimeout 10000000 rp
111+ died <- receiveTimeout 10000000 [
112+ matchIf (\(ProcessMonitorNotification r _ _) -> r == mRef) (const $ return True)
113+ ]
114+ died `shouldBe` equalTo (Just True)
88
115
89testAgentPrioritisation :: TestResult [String] -> Process ()
116testAgentPrioritisation :: TestResult [String] -> Process ()
90testAgentPrioritisation result = do
117testAgentPrioritisation result = do
@@ -96,12 +123,12 @@ testAgentPrioritisation result = do
96
123
97 let name = "prioritising-agent"
124 let name = "prioritising-agent"
98 (sp, rp) <- newChan
125 (sp, rp) <- newChan
99− void $ mxAgent (MxAgentId name) ["first"] [
126+ s <- mxAgent (MxAgentId name) ["first"] [
100− mxSink (\(s :: String) -> do
127+ mxSink (\(s :: String) -> do
101− mxUpdateLocal ((s:))
128+ mxUpdateLocal (s:)
102 st <- mxGetLocal
129 st <- mxGetLocal
103 case length st of
130 case length st of
104− n | n == 5 -> do liftMX $ sendChan sp st
131+ n | n == 5 -> do liftMX $ sendChan sp (sort st)
105 mxDeactivate "finished"
132 mxDeactivate "finished"
106 _ -> mxReceive -- go to the mailbox
133 _ -> mxReceive -- go to the mailbox
107 )
134 )
@@ -112,7 +139,8 @@ testAgentPrioritisation result = do
112 mxNotify "fourth"
139 mxNotify "fourth"
113 nsend name "fifth"
140 nsend name "fifth"
114
141
115− stash result . sort =<< receiveChan rp
142+ stash result . fromMaybe [] =<< receiveChanTimeout 10000000 rp
143+ awaitExit s
116
144
117testAgentMailboxHandling :: TestResult (Maybe ()) -> Process ()
145testAgentMailboxHandling :: TestResult (Maybe ()) -> Process ()
118testAgentMailboxHandling result = do
146testAgentMailboxHandling result = do
@@ -128,7 +156,16 @@ testAgentMailboxHandling result = do
128
156
129testAgentEventHandling :: TestResult Bool -> Process ()
157testAgentEventHandling :: TestResult Bool -> Process ()
130testAgentEventHandling result = do
158testAgentEventHandling result = do
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"
166+
131 let initState = [] :: [MxEvent]
167 let initState = [] :: [MxEvent]
168+ (rc, rs) <- newChan
132 agentPid <- mxAgent (MxAgentId "lifecycle-listener-agent") initState [
169 agentPid <- mxAgent (MxAgentId "lifecycle-listener-agent") initState [
133 (mxSink $ \ev -> do
170 (mxSink $ \ev -> do
134 st <- mxGetLocal
171 st <- mxGetLocal
@@ -137,7 +174,7 @@ testAgentEventHandling result = do
137 (MxSpawned _) -> mxSetLocal (ev:st)
174 (MxSpawned _) -> mxSetLocal (ev:st)
138 (MxProcessDied _ _) -> mxSetLocal (ev:st)
175 (MxProcessDied _ _) -> mxSetLocal (ev:st)
139 _ -> return ()
176 _ -> return ()
140− act >> mxReady),
177+ act >> (liftMX $ sendChan rc ()) >> mxReady),
141 (mxSink $ \(ev, sp :: SendPort Bool) -> do
178 (mxSink $ \(ev, sp :: SendPort Bool) -> do
142 st <- mxGetLocal
179 st <- mxGetLocal
143 let found =
180 let found =
@@ -157,164 +194,349 @@ testAgentEventHandling result = do
157 mxReady)
194 mxReady)
158 ]
195 ]
159
196
160− _ <- monitor agentPid
197+ -- TODO: yes, this is racy, but we're at the mercy of the scheduler here...
198+ faff 2000000 rs
199+
161− (sp, rp) <- newChan
200+ (sp, rp) <- newChan :: Process (SendPort (), ReceivePort ())
201+
162− pid <- spawnLocal $ sendChan sp ()
202+ pid <- spawnLocal $ expect >>= sendChan sp
163− () <- receiveChan rp
164
203
165 -- By waiting for a monitor notification, we have a
204 -- By waiting for a monitor notification, we have a
166− -- higher probably that the agent has seen
205+ -- higher probably that the agent has seen the spawn and died events
167 monitor pid
206 monitor pid
207+
208+ send pid ()
209+ rct <- receiveChanTimeout 10000000 rp
210+ unless (isJust rct) $ die "No response on channel"
211+
168− receiveWait [ match (\(ProcessMonitorNotification _ _ _) -> return ()) ]
212+ pmn <- receiveTimeout 2000000 [ match (\ProcessMonitorNotification{} -> return ()) ]
213+ unless (isJust pmn) $ die "No monitor notification arrived"
214+
215+ -- TODO: yes, this is racy, but we're at the mercy of the scheduler here...
216+ faff 2000000 rs
169
217
170 (replyTo, reply) <- newChan :: Process (SendPort Bool, ReceivePort Bool)
218 (replyTo, reply) <- newChan :: Process (SendPort Bool, ReceivePort Bool)
171 mxNotify (MxSpawned pid, replyTo)
219 mxNotify (MxSpawned pid, replyTo)
172 mxNotify (MxProcessDied pid DiedNormal, replyTo)
220 mxNotify (MxProcessDied pid DiedNormal, replyTo)
173
221
174− seenAlive <- receiveChan reply
222+ seenAlive <- receiveChanTimeout 10000000 reply
175− seenDead <- receiveChan reply
223+ seenDead <- receiveChanTimeout 10000000 reply
176−
177− stash result $ seenAlive && seenDead
178
224
179−testMxRegEvents :: Process ()
225+ stash result $ foldr (&&) True $ catMaybes [seenAlive, seenDead]
180−testMxRegEvents = do
226+ kill timer "test-complete"
227+ kill agentPid "test-complete"
228+ where
229+ faff delay port = do
230+ res <- receiveChanTimeout delay port
231+ unless (isNothing res) $ pause delay
181
232
233+testMxRegEvents :: TestResult () -> Process ()
234+testMxRegEvents result = do
182 {- This test only deals with the local case, to ensure that we are being
235 {- This test only deals with the local case, to ensure that we are being
183 notified in the expected order - the remote cases related to the
236 notified in the expected order - the remote cases related to the
184 behaviour of the node controller are contained in the CH test suite. -}
237 behaviour of the node controller are contained in the CH test suite. -}
238+ ensure (stash result ()) $ do
239+ let label = "testMxRegEvents"
240+ let agentLabel = "mxRegEvents-agent"
241+ let delay = 1000000
242+ (regChan, regSink) <- newChan
243+ (unRegChan, unRegSink) <- newChan
244+ agent <- mxAgent (MxAgentId agentLabel) () [
245+ mxSink $ \ev -> do
246+ case ev of
247+ MxRegistered pid label'
248+ | label' == label -> liftMX $ sendChan regChan (label', pid)
249+ MxUnRegistered pid label'
250+ | label' == label -> liftMX $ sendChan unRegChan (label', pid)
251+ _ -> return ()
252+ mxReady
253+ ]
185
254
186− let label = "testMxRegEvents"
187− let agentLabel = "mxRegEvents-agent"
188− let delay = 1000000
189− (regChan, regSink) <- newChan
190− (unRegChan, unRegSink) <- newChan
191− agent <- mxAgent (MxAgentId agentLabel) () [
192− mxSink $ \ev -> do
193− case ev of
194− MxRegistered pid label
195− | label /= agentLabel -> liftMX $ sendChan regChan (label, pid)
196− MxUnRegistered pid label
197− | label /= agentLabel -> liftMX $ sendChan unRegChan (label, pid)
198− _ -> return ()
199− mxReady
200− ]
201−
202− p1 <- spawnLocal expect
255+ p1 <- spawnLocal expect
203− p2 <- spawnLocal expect
256+ p2 <- spawnLocal expect
204−
257+
205− register label p1
258+ register label p1
206− reg1 <- receiveChanTimeout delay regSink
259+ reg1 <- receiveChanTimeout delay regSink
207− reg1 `shouldBe` equalTo (Just (label, p1))
260+ reg1 `shouldBe` equalTo (Just (label, p1))
208−
261+
209− unregister label
262+ unregister label
210− unreg1 <- receiveChanTimeout delay unRegSink
263+ unreg1 <- receiveChanTimeout delay unRegSink
211− unreg1 `shouldBe` equalTo (Just (label, p1))
264+ unreg1 `shouldBe` equalTo (Just (label, p1))
212−
265+
213− register label p2
266+ register label p2
214− reg2 <- receiveChanTimeout delay regSink
267+ reg2 <- receiveChanTimeout delay regSink
215− reg2 `shouldBe` equalTo (Just (label, p2))
268+ reg2 `shouldBe` equalTo (Just (label, p2))
216−
269+
217− reregister label p1
270+ reregister label p1
218− unreg2 <- receiveChanTimeout delay unRegSink
271+ unreg2 <- receiveChanTimeout delay unRegSink
219− unreg2 `shouldBe` equalTo (Just (label, p2))
272+ unreg2 `shouldBe` equalTo (Just (label, p2))
220−
273+
221− reg3 <- receiveChanTimeout delay regSink
274+ reg3 <- receiveChanTimeout delay regSink
222− reg3 `shouldBe` equalTo (Just (label, p1))
275+ reg3 `shouldBe` equalTo (Just (label, p1))
223−
276+
224− mapM_ (flip kill $ "test-complete") [agent, p1, p2]
277+ mapM_ (flip kill $ "test-complete") [agent, p1, p2]
225−
278+
226−testMxRegMon :: LocalNode -> Process ()
279+testMxRegMon :: LocalNode -> TestResult () -> Process ()
227−testMxRegMon remoteNode = do
280+testMxRegMon remoteNode result = do
228−
281+ ensure (stash result ()) $ do
229− -- ensure that when a registered process dies, we get a notification that
282+ -- ensure that when a registered process dies, we get a notification that
230− -- it has been unregistered as well as seeing the name get removed
283+ -- it has been unregistered as well as seeing the name get removed
284+ let label1 = "aaaaa"
285+ let label2 = "bbbbb"
286+ let isValid l = l == label1 || l == label2
287+ let agentLabel = "mxRegMon-agent"
288+ let delay = 1000000
289+ (regChan, regSink) <- newChan
290+ (unRegChan, unRegSink) <- newChan
291+ agent <- mxAgent (MxAgentId agentLabel) () [
292+ mxSink $ \ev -> do
293+ case ev of
294+ MxRegistered pid label
295+ | isValid label -> liftMX $ sendChan regChan (label, pid)
296+ MxUnRegistered pid label
297+ | isValid label -> liftMX $ sendChan unRegChan (label, pid)
298+ _ -> return ()
299+ mxReady
300+ ]
231
301
232− let label1 = "aaaaa"
302+ (sp, rp) <- newChan
233− let label2 = "bbbbb"
303+ liftIO $ forkProcess remoteNode $ do
234− let isValid l = l == label1 || l == label2
304+ getSelfPid >>= sendChan sp
235− let agentLabel = "mxRegMon-agent"
305+ expect :: Process ()
236− let delay = 1000000
306+
237− (regChan, regSink) <- newChan
307+ p1 <- receiveChan rp
238− (unRegChan, unRegSink) <- newChan
308+
239− agent <- mxAgent (MxAgentId agentLabel) () [
309+ register label1 p1
310+ reg1 <- receiveChanTimeout delay regSink
311+ reg1 `shouldBe` equalTo (Just (label1, p1))
312+
313+ register label2 p1
314+ reg2 <- receiveChanTimeout delay regSink
315+ reg2 `shouldBe` equalTo (Just (label2, p1))
316+
317+ n1 <- whereis label1
318+ n1 `shouldBe` equalTo (Just p1)
319+
320+ n2 <- whereis label2
321+ n2 `shouldBe` equalTo (Just p1)
322+
323+ kill p1 "goodbye"
324+
325+ unreg1 <- receiveChanTimeout delay unRegSink
326+ unreg2 <- receiveChanTimeout delay unRegSink
327+
328+ let evts = [unreg1, unreg2]
329+ -- we can't rely on the order of the values in the node controller's
330+ -- map (it's either racy to do so, or no such guarantee exists for Data.Map),
331+ -- so we simply verify that we received the un-registration events we expect
332+ evts `shouldContain` (Just (label1, p1))
333+ evts `shouldContain` (Just (label2, p1))
334+
335+ kill agent "test-complete"
336+
337+ensure :: Process () -> Process () -> Process ()
338+ensure = flip finally
339+
340+testNSend :: (String -> () -> Process ())
341+ -> Maybe LocalNode
342+ -> Process ()
343+testNSend op n = do
344+ us <- getSelfPid
345+ let delay = 5000000
346+ let label = "testMxSend" ++ (show us)
347+ let isValid = isValidLabel n label
348+
349+ testMxSend n label $ \p1 sink -> do
350+ register label p1
351+ reg1 <- receiveChanTimeout delay sink
352+ case reg1 of
353+ Just (MxRegistered pd lb)
354+ | pd == p1 && isValid lb -> return ()
355+ _ -> die $ "Reg-Failed: " ++ show reg1
356+
357+ op label ()
358+
359+ sent <- receiveChanTimeout delay sink
360+ case sent of
361+ Just (MxSentToName lb by _)
362+ | by == us && isValid lb -> return True
363+ _ -> die $ "Send-Failed: " ++ show sent
364+
365+ where
366+ isValidLabel nd l1 l2
367+ | l2 == l2 = True
368+ | isJust nd = l2 == l1 ++ "@" ++ show (localNodeId $ fromJust nd)
369+ | otherwise = False
370+
371+testSend :: (ProcessId -> () -> Process ())
372+ -> Maybe LocalNode
373+ -> Process ()
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+
405+ -- initiate a send
406+ op ch' ()
407+
408+ -- verify the management event
409+ sent <- receiveChanTimeout 5000000 sink
410+ case sent of
411+ Just (MxSentToPort sId spId _)
412+ | sId == us && spId == sendPortId ch' -> return True
413+ _ -> return False
414+
415+type SendTest = ProcessId -> ReceivePort MxEvent -> Process Bool
416+
417+testMxSend :: Maybe LocalNode -> String -> SendTest -> Process ()
418+testMxSend mNode label test = do
419+ us <- getSelfPid
420+ (sp, rp) <- newChan
421+ (chan, sink) <- newChan
422+ agent <- mxAgent (MxAgentId $ agentLabel us) () [
240 mxSink $ \ev -> do
423 mxSink $ \ev -> do
241 case ev of
424 case ev of
242− MxRegistered pid label
425+ m@(MxSentToPort _ cid _)
243− | isValid label -> liftMX $ sendChan regChan (label, pid)
426+ | cid /= sendPortId chan
244− MxUnRegistered pid label
427+ && cid /= sendPortId sp -> liftMX $ sendChan chan m
245− | isValid label -> liftMX $ sendChan unRegChan (label, pid)
428+ m@(MxSent _ fromPid _)
429+ | fromPid == us -> liftMX $ sendChan chan m
430+ m@(MxSentToName _ fromPid _)
431+ | fromPid == us -> liftMX $ sendChan chan m
432+ m@(MxRegistered _ name)
433+ | name == label -> liftMX $ sendChan chan m
246 _ -> return ()
434 _ -> return ()
247 mxReady
435 mxReady
248 ]
436 ]
249
437
250− (sp, rp) <- newChan
438+ case mNode of
251− liftIO $ forkProcess remoteNode $ do
439+ Nothing -> void $ spawnLocal (proc sp)
252− getSelfPid >>= sendChan sp
440+ Just remoteNode -> void $ liftIO $ forkProcess remoteNode $ proc sp
253− expect :: Process ()
254−
255− p1 <- receiveChan rp
256
441
257− register label1 p1
442+ p1 <- receiveChanTimeout 2000000 rp
258− reg1 <- receiveChanTimeout delay regSink
443+ unless (isJust p1) $ die "Timed out waiting for ProcessId"
259− reg1 `shouldBe` equalTo (Just (label1, p1))
444+ res <- try $ test (fromJust p1) sink
260
445
261− register label2 p1
446+ kill agent "bye"
262− reg2 <- receiveChanTimeout delay regSink
447+ kill (fromJust p1) "bye"
263− reg2 `shouldBe` equalTo (Just (label2, p1))
264
448
265− n1 <- whereis label1
449+ case res of
266− n1 `shouldBe` equalTo (Just p1)
450+ Left (ProcessExitException _ m) -> (liftIO $ putStrLn $ "SomeException-" ++ show m) >> die m
451+ Right tr -> tr `shouldBe` equalTo True
267
452
268− n2 <- whereis label2
269− n2 `shouldBe` equalTo (Just p1)
270
453
271− kill p1 "goodbye"
454+ where
455+ agentLabel s = "mx-unsafe-check-agent-" ++ show s
456+ proc sp' = getSelfPid >>= sendChan sp' >> go Nothing
272
457
273− unreg1 <- receiveChanTimeout delay unRegSink
458+ go :: Maybe (ReceivePort ()) -> Process ()
274− unreg2 <- receiveChanTimeout delay unRegSink
459+ go Nothing = receiveTimeout 5000000 [ match replyChannel ] >>= go
460+ go c@(Just c') = receiveWait [ matchChan c' return ] >> go c
275
461
276− let evts = [unreg1, unreg2]
462+ replyChannel p' = do
277− -- we can't rely on the order of the values in the node controller's
463+ (s, r) <- newChan
278− -- map (it's either racy to do so, or no such guarantee exists for Data.Map),
464+ send p' s
279− -- so we simply verify that we received the un-registration events we expect
465+ return r
280− evts `shouldContain` (Just (label1, p1))
281− evts `shouldContain` (Just (label2, p1))
282−
283− kill agent "test-complete"
284
466
285tests :: TestTransport -> IO [Test]
467tests :: TestTransport -> IO [Test]
286tests TestTransport{..} = do
468tests TestTransport{..} = do
287 node1 <- newLocalNode testTransport initRemoteTable
469 node1 <- newLocalNode testTransport initRemoteTable
288 node2 <- newLocalNode testTransport initRemoteTable
470 node2 <- newLocalNode testTransport initRemoteTable
289 return [
471 return [
290− testGroup "Mx Agents" [
472+ testGroup "MxAgents" [
291− testCase "Event Handling"
473+ testCase "EventHandling"
292 (delayedAssertion
474 (delayedAssertion
293 "expected True, but events where not as expected"
475 "expected True, but events where not as expected"
294 node1 True testAgentEventHandling)
476 node1 True testAgentEventHandling)
295− , testCase "Inter-Agent Broadcast"
477+ , testCase "InterAgentBroadcast"
296 (delayedAssertion
478 (delayedAssertion
297 "expected (), but no broadcast was received"
479 "expected (), but no broadcast was received"
298− node1 () testAgentBroadcast)
480+ node1 (Just ()) testAgentBroadcast)
299− , testCase "Agent Mailbox Handling"
481+ , testCase "AgentMailboxHandling"
300 (delayedAssertion
482 (delayedAssertion
301 "expected (Just ()), but no regular (mailbox) input was handled"
483 "expected (Just ()), but no regular (mailbox) input was handled"
302 node1 (Just ()) testAgentMailboxHandling)
484 node1 (Just ()) testAgentMailboxHandling)
303− , testCase "Agent Dual Input Handling"
485+ , testCase "AgentDualInputHandling"
304 (delayedAssertion
486 (delayedAssertion
305 "expected sum = 15, but the result was Nothing"
487 "expected sum = 15, but the result was Nothing"
306 node1 (Just 15 :: Maybe Int) testAgentDualInput)
488 node1 (Just 15 :: Maybe Int) testAgentDualInput)
307− , testCase "Agent Input Prioritisation"
489+ , testCase "AgentInputPrioritisation"
308 (delayedAssertion
490 (delayedAssertion
309 "expected [first, second, third, fourth, fifth], but result diverged"
491 "expected [first, second, third, fourth, fifth], but result diverged"
310 node1 (sort ["first", "second",
492 node1 (sort ["first", "second",
311 "third", "fourth",
493 "third", "fourth",
312 "fifth"]) testAgentPrioritisation)
494 "fifth"]) testAgentPrioritisation)
313 ]
495 ]
314− , testGroup "Mx Events" [
496+ , testGroup "MxEvents" [
315− testCase "Name Registration Events"
497+ testCase "NameRegistrationEvents"
316− (runProcess node1 testMxRegEvents)
498+ (delayedAssertion
317− , testCase "Post Death Name UnRegistration Events"
499+ "expected registration events to map to the correct ProcessId"
318− (runProcess node1 (testMxRegMon node2))
500+ node1 () testMxRegEvents)
501+ , testCase "PostDeathNameUnRegistrationEvents"
502+ (delayedAssertion
503+ "expected process deaths to result in unregistration events"
504+ node1 () (testMxRegMon node2))
505+ , testGroup "SendEvents" $ buildTestCases node1 node2
319 ]
506 ]
320 ]
507 ]
508+ where
509+ buildTestCases n1 n2 = let nid = localNodeId n2 in build n1 n2 [
510+ ("NSend", [
511+ ("nsend", testNSend nsend)
512+ , ("Unsafe.nsend", testNSend Unsafe.nsend)
513+ , ("nsendRemote", testNSend (nsendRemote nid))
514+ , ("Unsafe.nsendRemote", testNSend (Unsafe.nsendRemote nid))
515+ ])
516+ , ("Send", [
517+ ("send", testSend send)
518+ , ("Unsafe.send", testSend Unsafe.send)
519+ , ("usend", testSend usend)
520+ , ("Unsafe.usend", testSend Unsafe.usend)
521+ , ("sendChan", testChan sendChan)
522+ , ("Unsafe.sendChan", testChan Unsafe.sendChan)
523+ ])
524+ , ("Forward", [
525+ ("forward", testSend (\p m -> forward (unsafeCreateUnencodedMessage m) p))
526+ , ("uforward", testSend (\p m -> uforward (unsafeCreateUnencodedMessage m) p))
527+ ])
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
539+ ]
540+ | (groupName, groupCases) <- specs
541+ , (caseSuffix, caseNode) <- [("RemotePid", Just ln), ("LocalPid", Nothing)]
542+ ]
distributed-process-tests/src/Control/Distributed/Process/Tests/Receive.hs+7−8
@@ -10,13 +10,12 @@ import Network.Transport.Test (TestTransport(..))
10
10
11import Network.Transport (Transport)
11import Network.Transport (Transport)
12import Control.Distributed.Process
12import Control.Distributed.Process
13−import Control.Distributed.Process.Closure
14import Control.Distributed.Process.Node
13import Control.Distributed.Process.Node
15
14
16import Control.Monad
15import Control.Monad
17
16
18import Test.HUnit (Assertion, (@?=))
17import Test.HUnit (Assertion, (@?=))
19−import Test.Framework (Test, defaultMain)
18+import Test.Framework (Test)
20import Test.Framework.Providers.HUnit (testCase)
19import Test.Framework.Providers.HUnit (testCase)
21
20
22-- Tests:
21-- Tests:
@@ -43,7 +42,7 @@ recTest2 :: ReceivePort ()
43 -> SendPort String
42 -> SendPort String
44 -> ReceivePort String -> ReceivePort String
43 -> ReceivePort String -> ReceivePort String
45 -> Process ()
44 -> Process ()
46−recTest2 wait sync r1 r2 = do
45+recTest2 wait sync r1 _ = do
47 forever $ do
46 forever $ do
48 receiveChan wait
47 receiveChan wait
49 r <- receiveWait
48 r <- receiveWait
@@ -56,7 +55,7 @@ recTest3 :: ReceivePort ()
56 -> SendPort String
55 -> SendPort String
57 -> ReceivePort String -> ReceivePort String
56 -> ReceivePort String -> ReceivePort String
58 -> Process ()
57 -> Process ()
59−recTest3 wait sync r1 r2 = do
58+recTest3 wait sync r1 _ = do
60 forever $ do
59 forever $ do
61 receiveChan wait
60 receiveChan wait
62 r <- receiveWait
61 r <- receiveWait
@@ -69,7 +68,7 @@ recTest4 :: ReceivePort ()
69 -> SendPort String
68 -> SendPort String
70 -> ReceivePort String -> ReceivePort String
69 -> ReceivePort String -> ReceivePort String
71 -> Process ()
70 -> Process ()
72−recTest4 wait sync r1 r2 = do
71+recTest4 wait sync r1 _ = do
73 forever $ do
72 forever $ do
74 receiveChan wait
73 receiveChan wait
75 r <- receiveWait
74 r <- receiveWait
@@ -83,11 +82,11 @@ master :: Process ()
83master = do
82master = do
84 (waits,waitr) <- newChan
83 (waits,waitr) <- newChan
85 (syncs,syncr) <- newChan
84 (syncs,syncr) <- newChan
86− let go expect = do
85+ let go expected = do
87 sendChan waits ()
86 sendChan waits ()
88 r <- receiveChan syncr
87 r <- receiveChan syncr
89− liftIO $ print (r,expect, r == expect)
88+ liftIO $ print (r, expected, r == expected)
90− liftIO $ r @?= expect
89+ liftIO $ r @?= expected
91
90
92 liftIO $ putStrLn "---- Test 1 ----"
91 liftIO $ putStrLn "---- Test 1 ----"
93 (s1,r1) <- newChan
92 (s1,r1) <- newChan
distributed-process-tests/src/Control/Distributed/Process/Tests/Stats.hs+11−7
@@ -29,7 +29,7 @@ import Test.Framework.Providers.HUnit (testCase)
29
29
30testLocalDeadProcessInfo :: TestResult (Maybe ProcessInfo) -> Process ()
30testLocalDeadProcessInfo :: TestResult (Maybe ProcessInfo) -> Process ()
31testLocalDeadProcessInfo result = do
31testLocalDeadProcessInfo result = do
32− pid <- spawnLocal $ do "finish" <- expect; return ()
32+ pid <- spawnLocal $ do (_ :: String) <- expect; return ()
33 mref <- monitor pid
33 mref <- monitor pid
34 send pid "finish"
34 send pid "finish"
35 _ <- receiveWait [
35 _ <- receiveWait [
@@ -52,8 +52,10 @@ testLocalLiveProcessInfo result = do
52 link self
52 link self
53 mRef <- monitor self
53 mRef <- monitor self
54 stash mon mRef
54 stash mon mRef
55− "die" <- expect
55+ res <- expect
56− return ()
56+ case res of
57+ "die" -> return ()
58+ _ -> die $ "unexpected message received: " ++ res
57
59
58 monRef <- liftIO $ takeMVar mon
60 monRef <- liftIO $ takeMVar mon
59
61
@@ -84,9 +86,9 @@ testRemoteLiveProcessInfo TestTransport{..} node1 = do
84 -- our send op shouldn't overtake link or monitor requests AFAICT
86 -- our send op shouldn't overtake link or monitor requests AFAICT
85 -- so a little table tennis should get us synchronised properly
87 -- so a little table tennis should get us synchronised properly
86 send serverPid (self, "ping")
88 send serverPid (self, "ping")
87− "pong" <- expect
89+ pong <- expect
88 pInfo <- getProcessInfo serverPid
90 pInfo <- getProcessInfo serverPid
89− stash result $ pInfo /= Nothing
91+ stash result $ pong == "pong" && pInfo /= Nothing
90 where
92 where
91 launchRemote :: MVar ProcessId -> IO ()
93 launchRemote :: MVar ProcessId -> IO ()
92 launchRemote locMV = do
94 launchRemote locMV = do
@@ -97,8 +99,10 @@ testRemoteLiveProcessInfo TestTransport{..} node1 = do
97 _ <- receiveWait [
99 _ <- receiveWait [
98 match (\(pid, "ping") -> send pid "pong")
100 match (\(pid, "ping") -> send pid "pong")
99 ]
101 ]
100− "stop" <- expect
102+ res <- expect
101− return ()
103+ case res of
104+ "stop" -> return ()
105+ _ -> die $ "unexpected message received: " ++ res
102 return ()
106 return ()
103
107
104 withActiveRemote :: LocalNode
108 withActiveRemote :: LocalNode
distributed-process-tests/src/Control/Distributed/Process/Tests/Tracing.hs+23−11
@@ -125,9 +125,11 @@ testTraceSending result = do
125 (\ev ->
125 (\ev ->
126 case ev of
126 case ev of
127 (MxSent to from msg) -> do
127 (MxSent to from msg) -> do
128− (Just s) <- unwrapMessage msg :: Process (Maybe String)
128+ mS <- unwrapMessage msg :: Process (Maybe String)
129− stash res (to == pid && from == self && s == "hello there")
129+ case mS of
130− stash res (to == pid && from == self)
130+ (Just s) -> do stash res (to == pid && from == self && s == "hello there")
131+ stash res (to == pid && from == self)
132+ _ -> die "failed state invariant, message type unmatched..."
131 _ ->
133 _ ->
132 return ()) $ do
134 return ()) $ do
133 send pid "hello there"
135 send pid "hello there"
@@ -246,12 +248,8 @@ testRemoteTraceRelay TestTransport{..} result =
246 -- Here we set up that relay, and then wait for a signal
248 -- Here we set up that relay, and then wait for a signal
247 -- that the tracer (on node1) has seen the expected
249 -- that the tracer (on node1) has seen the expected
248 -- MxSpawned message, at which point we're finished
250 -- MxSpawned message, at which point we're finished
249− (Just log') <- whereis "logger"
250− pid <- liftIO $ forkProcess node2 $ do
251− logRelay <- spawnLocal $ relay log'
252− reregister "logger" logRelay
253− getSelfNode >>= stash mvNid >> (expect :: Process ())
254
251
252+ pid <- splinchLogger node2 mvNid
255 nid <- liftIO $ takeMVar mvNid
253 nid <- liftIO $ takeMVar mvNid
256 mref <- monitor pid
254 mref <- monitor pid
257 observedPid <- liftIO $ newEmptyMVar
255 observedPid <- liftIO $ newEmptyMVar
@@ -291,6 +289,17 @@ testRemoteTraceRelay TestTransport{..} result =
291 -- and just to be polite...
289 -- and just to be polite...
292 liftIO $ closeLocalNode node2
290 liftIO $ closeLocalNode node2
293
291
292+ where
293+ splinchLogger n2 mv = do
294+ mLog <- whereis "logger"
295+ case mLog of
296+ Nothing -> die "no logger registered"
297+ Just log' -> do liftIO $ forkProcess n2 $ do
298+ logRelay <- spawnLocal $ relay log'
299+ reregister "logger" logRelay
300+ getSelfNode >>= stash mv >> (expect :: Process ())
301+
302+
294-- | Sets the value of an environment variable while executing the given IO
303-- | Sets the value of an environment variable while executing the given IO
295-- computation and restores the preceeding value upon completion.
304-- computation and restores the preceeding value upon completion.
296withEnv :: String -> String -> IO a -> IO a
305withEnv :: String -> String -> IO a -> IO a
@@ -319,9 +328,12 @@ testSystemLoggerMsg t action interestingMessage =
319 -- Wait for the trace message.
328 -- Wait for the trace message.
320 receiveWait [ matchIf interestingMessage' $ const $ return () ]
329 receiveWait [ matchIf interestingMessage' $ const $ return () ]
321 -- Only one interesting message should arrive.
330 -- Only one interesting message should arrive.
322− Nothing <- receiveTimeout 100000
331+ expectedTimeout <- receiveTimeout
323− [ matchIf interestingMessage' $ const $ return () ]
332+ 100000
324− return ()
333+ [ matchIf interestingMessage' $ const $ return () ]
334+ case expectedTimeout of
335+ Nothing -> return ()
336+ Just _ -> die "Unexpected message arrived..."
325
337
326
338
327-- | Tests that one and only one trace message is produced when a message is
339-- | Tests that one and only one trace message is produced when a message is
distributed-process-tests/tests/runInMemory.hs−1
@@ -8,7 +8,6 @@ import Network.Transport.Test (TestTransport(..))
8import Network.Transport.InMemory
8import Network.Transport.InMemory
9import Test.Framework (defaultMainWithArgs)
9import Test.Framework (defaultMainWithArgs)
10
10
11−import Control.Concurrent (threadDelay)
12import System.Environment (getArgs)
11import System.Environment (getArgs)
13
12
14main :: IO ()
13main :: IO ()
distributed-process-tests/tests/runTCP.hs+2−2
@@ -10,6 +10,7 @@ import Network.Transport.TCP
10 ( createTransportExposeInternals
10 ( createTransportExposeInternals
11 , TransportInternals(socketBetween)
11 , TransportInternals(socketBetween)
12 , defaultTCPParameters
12 , defaultTCPParameters
13+ , defaultTCPAddr
13 , TCPParameters(..)
14 , TCPParameters(..)
14 )
15 )
15import Test.Framework (defaultMainWithArgs)
16import Test.Framework (defaultMainWithArgs)
@@ -24,8 +25,7 @@ main = do
24 hSetBuffering stdout LineBuffering
25 hSetBuffering stdout LineBuffering
25 hSetBuffering stderr LineBuffering
26 hSetBuffering stderr LineBuffering
26 Right (transport, internals) <-
27 Right (transport, internals) <-
27− createTransportExposeInternals "127.0.0.1" "8080"
28+ createTransportExposeInternals (defaultTCPAddr "127.0.0.1" "8080")
28− (\sn -> ("127.0.0.1", sn))
29 defaultTCPParameters { transportConnectTimeout = Just 3000000 }
29 defaultTCPParameters { transportConnectTimeout = Just 3000000 }
30 ts <- tests TestTransport
30 ts <- tests TestTransport
31 { testTransport = transport
31 { testTransport = transport
distributed-process.cabal+2−2
@@ -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
@@ -21,7 +21,7 @@ Description: This is an implementation of Cloud Haskell, as described in
21
21
22 You will probably also want to install a Cloud Haskell backend such
22 You will probably also want to install a Cloud Haskell backend such
23 as distributed-process-simplelocalnet.
23 as distributed-process-simplelocalnet.
24−Tested-With: GHC==7.6.3 GHC==7.8.4 GHC==7.10.3 GHC==8.0.1
24+Tested-With: GHC==7.10.3 GHC==8.0.2 GHC==8.2.2 GHC==8.4.4
25Category: Control
25Category: Control
26extra-source-files: ChangeLog
26extra-source-files: ChangeLog
27
27
src/Control/Distributed/Process/Internal/Primitives.hs+59−43
@@ -254,17 +254,15 @@ send them msg = do
254 node = processNode proc
254 node = processNode proc
255 nodeId = localNodeId node
255 nodeId = localNodeId node
256 destNode = (processNodeId them) in do
256 destNode = (processNodeId them) in do
257− case destNode == nodeId of
258− True -> sendLocal them msg
259− False -> liftIO $ sendMessage (processNode proc)
260− (ProcessIdentifier (processId proc))
261− (ProcessIdentifier them)
262− NoImplicitReconnect
263− msg
264− -- We do not fire the trace event until after the sending is complete;
265− -- In the remote case, 'sendMessage' can block in the networking stack.
266 liftIO $ traceEvent (localEventBus node)
257 liftIO $ traceEvent (localEventBus node)
267 (MxSent them us (createUnencodedMessage msg))
258 (MxSent them us (createUnencodedMessage msg))
259+ if destNode == nodeId
260+ then sendLocal them msg
261+ else liftIO $ sendMessage (processNode proc)
262+ (ProcessIdentifier (processId proc))
263+ (ProcessIdentifier them)
264+ NoImplicitReconnect
265+ msg
268
266
269-- | /Unsafe/ variant of 'send'. This function makes /no/ attempt to serialize
267-- | /Unsafe/ variant of 'send'. This function makes /no/ attempt to serialize
270-- and (in the case when the destination process resides on the same local
268-- and (in the case when the destination process resides on the same local
@@ -284,9 +282,12 @@ unsafeSend = Unsafe.send
284--
282--
285usend :: Serializable a => ProcessId -> a -> Process ()
283usend :: Serializable a => ProcessId -> a -> Process ()
286usend them msg = do
284usend them msg = do
287− here <- getSelfNode
285+ proc <- ask
288 let there = processNodeId them
286 let there = processNodeId them
289− if here == there
287+ let (us, node) = (processId proc, processNode proc)
288+ let msg' = wrapMessage msg
289+ liftIO $ traceEvent (localEventBus node) (MxSent them us msg')
290+ if localNodeId (processNode proc) == there
290 then sendLocal them msg
291 then sendLocal them msg
291 else sendCtrlMsg (Just there) $ UnreliableSend (processLocalId them)
292 else sendCtrlMsg (Just there) $ UnreliableSend (processLocalId them)
292 (createMessage msg)
293 (createMessage msg)
@@ -306,7 +307,13 @@ expect = receiveWait [match return]
306-- Channels --
307-- Channels --
307--------------------------------------------------------------------------------
308--------------------------------------------------------------------------------
308
309
309−-- | 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+--
310newChan :: Serializable a => Process (SendPort a, ReceivePort a)
317newChan :: Serializable a => Process (SendPort a, ReceivePort a)
311newChan = do
318newChan = do
312 proc <- ask
319 proc <- ask
@@ -334,13 +341,16 @@ newChan = do
334sendChan :: Serializable a => SendPort a -> a -> Process ()
341sendChan :: Serializable a => SendPort a -> a -> Process ()
335sendChan (SendPort cid) msg = do
342sendChan (SendPort cid) msg = do
336 proc <- ask
343 proc <- ask
337− let node = localNodeId (processNode proc)
344+ let node = processNode proc
338− destNode = processNodeId (sendPortProcessId cid) in do
345+ pid = processId proc
339− case destNode == node of
346+ us = localNodeId node
347+ them = processNodeId (sendPortProcessId cid) in do
348+ liftIO $ traceEvent (localEventBus node) (MxSentToPort pid cid $ wrapMessage msg)
349+ case them == us of
340 True -> sendChanLocal cid msg
350 True -> sendChanLocal cid msg
341 False -> do
351 False -> do
342− liftIO $ sendBinary (processNode proc)
352+ liftIO $ sendBinary node
343− (ProcessIdentifier (processId proc))
353+ (ProcessIdentifier pid)
344 (SendPortIdentifier cid)
354 (SendPortIdentifier cid)
345 NoImplicitReconnect
355 NoImplicitReconnect
346 msg
356 msg
@@ -401,8 +411,11 @@ newtype Match b = Match { unMatch :: MatchOn Message (Process b) }
401receiveWait :: [Match b] -> Process b
411receiveWait :: [Match b] -> Process b
402receiveWait ms = do
412receiveWait ms = do
403 queue <- processQueue <$> ask
413 queue <- processQueue <$> ask
404− Just proc <- liftIO $ dequeue queue Blocking (map unMatch ms)
414+ mProc <- liftIO $ dequeue queue Blocking (map unMatch ms)
405− proc
415+ case mProc of
416+ Just proc' -> proc'
417+ Nothing -> die $ "System Invariant Violation: CQueue.hs returned `Nothing` "
418+ ++ "in the absence of a timeout value."
406
419
407-- | Like 'receiveWait' but with a timeout.
420-- | Like 'receiveWait' but with a timeout.
408--
421--
@@ -481,17 +494,14 @@ forward msg them = do
481 us = processId proc
494 us = processId proc
482 nid = localNodeId node
495 nid = localNodeId node
483 destNode = (processNodeId them) in do
496 destNode = (processNodeId them) in do
484− case destNode == nid of
497+ liftIO $ traceEvent (localEventBus node) (MxSent them us msg)
485− True -> sendCtrlMsg Nothing (LocalSend them msg)
498+ if destNode == nid
486− False -> liftIO $ sendPayload (processNode proc)
499+ then sendCtrlMsg Nothing (LocalSend them msg)
487− (ProcessIdentifier (processId proc))
500+ else liftIO $ sendPayload (processNode proc)
488− (ProcessIdentifier them)
501+ (ProcessIdentifier (processId proc))
489− NoImplicitReconnect
502+ (ProcessIdentifier them)
490− (messageToPayload msg)
503+ NoImplicitReconnect
491− -- We do not fire the trace event until after the sending is complete;
504+ (messageToPayload msg)
492− -- In the remote case, 'sendMessage' can block in the networking stack.
493− liftIO $ traceEvent (localEventBus node)
494− (MxSent them us msg)
495
505
496-- | Forward a raw 'Message' to the given 'ProcessId'.
506-- | Forward a raw 'Message' to the given 'ProcessId'.
497--
507--
@@ -505,14 +515,10 @@ uforward msg them = do
505 us = processId proc
515 us = processId proc
506 nid = localNodeId node
516 nid = localNodeId node
507 destNode = (processNodeId them) in do
517 destNode = (processNodeId them) in do
508− case destNode == nid of
518+ liftIO $ traceEvent (localEventBus node) (MxSent them us msg)
509− True -> sendCtrlMsg Nothing (LocalSend them msg)
519+ if destNode == nid
510− False -> sendCtrlMsg (Just destNode) $ UnreliableSend (processLocalId them)
520+ then sendCtrlMsg Nothing (LocalSend them msg)
511− msg
521+ else sendCtrlMsg (Just destNode) $ UnreliableSend (processLocalId them) msg
512− -- We do not fire the trace event until after the sending is complete;
513− -- In the remote case, 'sendCtrlMsg' can block in the networking stack.
514− liftIO $ traceEvent (localEventBus node)
515− (MxSent them us msg)
516
522
517-- | Wrap a 'Serializable' value in a 'Message'. Note that 'Message's are
523-- | Wrap a 'Serializable' value in a 'Message'. Note that 'Message's are
518-- 'Serializable' - like the datum they contain - but also note, deserialising
524-- 'Serializable' - like the datum they contain - but also note, deserialising
@@ -1221,8 +1227,12 @@ whereisRemoteAsync nid label = do
1221
1227
1222-- | Named send to a process in the local registry (asynchronous)
1228-- | Named send to a process in the local registry (asynchronous)
1223nsend :: Serializable a => String -> a -> Process ()
1229nsend :: Serializable a => String -> a -> Process ()
1224−nsend label msg =
1230+nsend label msg = do
1225− sendCtrlMsg Nothing (NamedSend label (createUnencodedMessage msg))
1231+ proc <- ask
1232+ let msg' = createUnencodedMessage msg
1233+ liftIO $ traceEvent (localEventBus (processNode proc))
1234+ (MxSentToName label (processId proc) msg')
1235+ sendCtrlMsg Nothing (NamedSend label msg')
1226
1236
1227-- | Named send to a process in the local registry (asynchronous).
1237-- | Named send to a process in the local registry (asynchronous).
1228-- This function makes /no/ attempt to serialize and (in the case when the
1238-- This function makes /no/ attempt to serialize and (in the case when the
@@ -1234,9 +1244,15 @@ unsafeNSend = Unsafe.nsend
1234-- | Named send to a process in a remote registry (asynchronous)
1244-- | Named send to a process in a remote registry (asynchronous)
1235nsendRemote :: Serializable a => NodeId -> String -> a -> Process ()
1245nsendRemote :: Serializable a => NodeId -> String -> a -> Process ()
1236nsendRemote nid label msg = do
1246nsendRemote nid label msg = do
1237− here <- getSelfNode
1247+ proc <- ask
1238− if here == nid then nsend label msg
1248+ let us = processId proc
1239− else sendCtrlMsg (Just nid) (NamedSend label (createMessage msg))
1249+ let node = processNode proc
1250+ if localNodeId node == nid
1251+ then nsend label msg
1252+ else let lbl = label ++ "@" ++ show nid in do
1253+ liftIO $ traceEvent (localEventBus node)
1254+ (MxSentToName lbl us (wrapMessage msg))
1255+ sendCtrlMsg (Just nid) (NamedSend label (createMessage msg))
1240
1256
1241-- | Named send to a process in a remote registry (asynchronous)
1257-- | Named send to a process in a remote registry (asynchronous)
1242-- This function makes /no/ attempt to serialize and (in the case when the
1258-- This function makes /no/ attempt to serialize and (in the case when the
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/Trace/Primitives.hs+8−2
@@ -33,6 +33,7 @@ import Control.Distributed.Process.Internal.Primitives
33 ( whereis
33 ( whereis
34 , newChan
34 , newChan
35 , receiveChan
35 , receiveChan
36+ , die
36 )
37 )
37import Control.Distributed.Process.Management.Internal.Trace.Types
38import Control.Distributed.Process.Management.Internal.Trace.Types
38 ( TraceArg(..)
39 ( TraceArg(..)
@@ -168,6 +169,11 @@ withRegisteredTracer act = do
168 withLocalTracer $ \t -> liftIO $ Tracer.getCurrentTraceClient t sp
169 withLocalTracer $ \t -> liftIO $ Tracer.getCurrentTraceClient t sp
169 currentTracer <- receiveChan rp
170 currentTracer <- receiveChan rp
170 case currentTracer of
171 case currentTracer of
171− Nothing -> do { (Just p') <- whereis "tracer.initial"; act p' }
172+ Nothing -> do mTP <- whereis "tracer.initial"
173+ -- NB: this should NOT ever happen, but forcing pattern matches
174+ -- is not considered cool in later versions of MonadFail
175+ case mTP of
176+ Just p' -> act p'
177+ Nothing -> die $ "System Invariant Violation: Tracer Process "
178+ ++ "Name Not Found (whereis tracer.initial)"
172 (Just p) -> act p
179 (Just p) -> act p
173−
src/Control/Distributed/Process/Management/Internal/Types.hs+7
@@ -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
@@ -57,8 +58,14 @@ data MxEvent =
57 -- ^ fired whenever a node /dies/ (i.e., the connection is broken/disconnected)
58 -- ^ fired whenever a node /dies/ (i.e., the connection is broken/disconnected)
58 | MxSent ProcessId ProcessId Message
59 | MxSent ProcessId ProcessId Message
59 -- ^ fired whenever a message is sent from a local process
60 -- ^ fired whenever a message is sent from a local process
61+ | MxSentToName String ProcessId Message
62+ -- ^ fired whenever a named send occurs
63+ | MxSentToPort ProcessId SendPortId Message
64+ -- ^ fired whenever a sendChan occurs
60 | MxReceived ProcessId Message
65 | MxReceived ProcessId Message
61 -- ^ 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
62 | MxConnected ConnectionId EndPointAddress
69 | MxConnected ConnectionId EndPointAddress
63 -- ^ fired when a network-transport connection is first established
70 -- ^ fired when a network-transport connection is first established
64 | MxDisconnected ConnectionId EndPointAddress
71 | MxDisconnected ConnectionId EndPointAddress
src/Control/Distributed/Process/Node.hs+17−57
@@ -186,15 +186,8 @@ import Control.Distributed.Process.Internal.Types
186import Control.Distributed.Process.Management.Internal.Agent
186import Control.Distributed.Process.Management.Internal.Agent
187 ( mxAgentController
187 ( mxAgentController
188 )
188 )
189−import Control.Distributed.Process.Management
189+import Control.Distributed.Process.Management.Internal.Types
190− ( mxAgent
190+ ( MxEvent(..)
191− , mxSink
192− , liftMX
193− , mxUpdateLocal
194− , mxGetLocal
195− , mxReady
196− , MxEvent(..)
197− , MxAgentId(..)
198 )
191 )
199import qualified Control.Distributed.Process.Management.Internal.Trace.Remote as Trace
192import qualified Control.Distributed.Process.Management.Internal.Trace.Remote as Trace
200 ( remoteTable
193 ( remoteTable
@@ -323,44 +316,6 @@ startDefaultTracer node' = do
323
316
324-- TODO: we need a better mechanism for defining and registering services
317-- TODO: we need a better mechanism for defining and registering services
325
318
326−{- note [registry monitoring agent]
327− This agent listens for 'MxRegistered' and 'MxUnRegistered' events and tracks
328− all labels for remote 'ProcessId's that are stored in the registry.
329−
330− When a remote process is registered, the agent starts monitoring it until it
331− is unregistered. We can safely ignore ProcessMonitorNotification's, since the
332− node controller is obligated to issue an MxUnRegistered event for any labels
333− connected to a dead process. Thus, our only responsibility here is to ensure
334− that we set up monitors for locally registered processes, and tear them down
335− once unregistration occurs.
336− -}
337−
338−registryMonitorAgent :: Process ProcessId
339−registryMonitorAgent = do
340− nid <- getSelfNode
341− mxAgent registryMonitorAgentId (Map.empty :: Map String MonitorRef)
342− [ mxSink $ \ev -> do
343− case ev of
344− MxRegistered pid label
345− | processNodeId pid /= nid -> do
346− mref <- liftMX $ monitor pid
347− mxUpdateLocal (Map.insert label mref)
348− MxUnRegistered pid label
349− | processNodeId pid /= nid -> do
350− mrefs <- mxGetLocal
351− forM_ (label `Map.lookup` mrefs) $ \mref -> do
352− liftMX $ unmonitorAsync mref
353− mxUpdateLocal (Map.delete label)
354− _ -> return ()
355− mxReady
356− -- [note: no need to remove async answers from mailbox]
357− -- The framework simply discards any input you don't have a handler for.
358− -- See Management.hs `runAgent` for details.
359− ]
360− where
361− registryMonitorAgentId :: MxAgentId
362− registryMonitorAgentId = MxAgentId "service.registry.monitoring"
363−
364-- | Start and register the service processes on a node
319-- | Start and register the service processes on a node
365startServiceProcesses :: LocalNode -> IO ()
320startServiceProcesses :: LocalNode -> IO ()
366startServiceProcesses node = do
321startServiceProcesses node = do
@@ -537,7 +492,7 @@ type IncomingConnection = (NT.EndPointAddress, IncomingTarget)
537data IncomingTarget =
492data IncomingTarget =
538 Uninit
493 Uninit
539 | ToProc ProcessId (Weak (CQueue Message))
494 | ToProc ProcessId (Weak (CQueue Message))
540− | ToChan TypedChannel
495+ | ToChan SendPortId TypedChannel
541 | ToNode
496 | ToNode
542
497
543data ConnectionState = ConnectionState {
498data ConnectionState = ConnectionState {
@@ -592,13 +547,17 @@ handleIncomingMessages node = go initConnectionState
592 enqueue queue msg -- 'enqueue' is strict
547 enqueue queue msg -- 'enqueue' is strict
593 trace node (MxReceived pid msg)
548 trace node (MxReceived pid msg)
594 go st
549 go st
595− Just (_, ToChan (TypedChannel chan')) -> do
550+ Just (_, ToChan chId (TypedChannel chan')) -> do
596 mChan <- deRefWeak chan'
551 mChan <- deRefWeak chan'
597 -- 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
598 -- the channel and we simply ignore the incoming message
553 -- the channel and we simply ignore the incoming message
599− forM_ mChan $ \chan -> atomically $
554+ forM_ mChan $ \chan -> do
600− -- We make sure the message is fully decoded when it is enqueued
555+ msg' <- atomically $ do
601− writeTQueue chan $! decode (BSL.fromChunks payload)
556+ msg <- return $! decode (BSL.fromChunks payload)
557+ -- We make sure the message is fully decoded when it is enqueued
558+ writeTQueue chan msg
559+ return msg
560+ trace node $ MxReceivedPort chId $ unsafeCreateUnencodedMessage msg'
602 go st
561 go st
603 Just (_, ToNode) -> do
562 Just (_, ToNode) -> do
604 let ctrlMsg = decode . BSL.fromChunks $ payload
563 let ctrlMsg = decode . BSL.fromChunks $ payload
@@ -625,7 +584,7 @@ handleIncomingMessages node = go initConnectionState
625 mChannel <- withMVar (processState proc) $ return . (^. typedChannelWithId lcid)
584 mChannel <- withMVar (processState proc) $ return . (^. typedChannelWithId lcid)
626 case mChannel of
585 case mChannel of
627 Just channel ->
586 Just channel ->
628− go (incomingAt cid ^= Just (src, ToChan channel) $ st)
587+ go (incomingAt cid ^= Just (src, ToChan chId channel) $ st)
629 Nothing ->
588 Nothing ->
630 invalidRequest cid st $
589 invalidRequest cid st $
631 "incoming attempt to connect to unknown channel of"
590 "incoming attempt to connect to unknown channel of"
@@ -1095,11 +1054,12 @@ ncEffectLocalPortSend from msg = do
1095 -- 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
1096 -- the channel and we simply ignore the incoming message - this
1055 -- the channel and we simply ignore the incoming message - this
1097 ch <- deRefWeak chan'
1056 ch <- deRefWeak chan'
1098− forM_ ch $ \chan -> deliverChan msg chan
1057+ forM_ ch $ \chan -> deliverChan node from msg chan
1099− where deliverChan :: forall a . Message -> TQueue a -> IO ()
1058+ where deliverChan :: forall a . LocalNode -> SendPortId -> Message -> TQueue a -> IO ()
1100− deliverChan (UnencodedMessage _ raw) chan' =
1059+ deliverChan n p (UnencodedMessage _ raw) chan' = do
1101 atomically $ writeTQueue chan' ((unsafeCoerce raw) :: a)
1060 atomically $ writeTQueue chan' ((unsafeCoerce raw) :: a)
1102− deliverChan (EncodedMessage _ _) _ =
1061+ trace n (MxReceivedPort p $ unsafeCreateUnencodedMessage raw)
1062+ deliverChan _ _ (EncodedMessage _ _) _ =
1103 -- this will not happen unless someone screws with Primitives.hs
1063 -- this will not happen unless someone screws with Primitives.hs
1104 error "invalid local channel delivery"
1064 error "invalid local channel delivery"
1105
1065
src/Control/Distributed/Process/UnsafePrimitives.hs+82−33
@@ -32,7 +32,24 @@
32-- the /normal/ strategy).
32-- the /normal/ strategy).
33--
33--
34-- Use of the functions in this module can potentially change the runtime
34-- Use of the functions in this module can potentially change the runtime
35−-- behaviour of your application. You have been warned!
35+-- behaviour of your application. In addition, messages passed between Cloud
36+-- Haskell processes are written to a tracing infrastructure on the local node,
37+-- to provide improved introspection and debugging facilities for complex actor
38+-- based systems. This module makes no attempt to force evaluation in these
39+-- cases either, thus evaluation problems in passed data structures could not
40+-- only crash your processes, but could also bring down critical internal
41+-- services on which the node relies to function correctly.
42+--
43+-- If you wish to repudiate such issues, you are advised to consider the use
44+-- of NFSerialisable in the distributed-process-extras package, which type
45+-- class brings NFData into scope along with Serializable, such that we can
46+-- force evaluation. Intended for use with modules such as this one, this
47+-- approach guarantees correct evaluatedness in terms of @NFData@. Please note
48+-- however, that we /cannot/ guarantee that an @NFData@ instance will behave the
49+-- same way as a @Binary@ one with regards evaluation, so it is still possible
50+-- to introduce unexpected behaviour by using /unsafe/ primitives in this way.
51+--
52+-- You have been warned!
36--
53--
37-- This module is exported so that you can replace the use of Cloud Haskell's
54-- This module is exported so that you can replace the use of Cloud Haskell's
38-- /safe/ messaging primitives. If you want to use both variants, then you can
55-- /safe/ messaging primitives. If you want to use both variants, then you can
@@ -55,7 +72,12 @@ import Control.Distributed.Process.Internal.Messaging
55 , sendBinary
72 , sendBinary
56 , sendCtrlMsg
73 , sendCtrlMsg
57 )
74 )
58−
75+import Control.Distributed.Process.Management.Internal.Types
76+ ( MxEvent(..)
77+ )
78+import Control.Distributed.Process.Management.Internal.Trace.Types
79+ ( traceEvent
80+ )
59import Control.Distributed.Process.Internal.Types
81import Control.Distributed.Process.Internal.Types
60 ( ProcessId(..)
82 ( ProcessId(..)
61 , NodeId(..)
83 , NodeId(..)
@@ -79,34 +101,48 @@ import Control.Monad.Reader (ask)
79
101
80-- | Named send to a process in the local registry (asynchronous)
102-- | Named send to a process in the local registry (asynchronous)
81nsend :: Serializable a => String -> a -> Process ()
103nsend :: Serializable a => String -> a -> Process ()
82−nsend label msg =
104+nsend label msg = do
83− sendCtrlMsg Nothing (NamedSend label (unsafeCreateUnencodedMessage msg))
105+ proc <- ask
106+ let us = processId proc
107+ let msg' = wrapMessage msg
108+ -- see [note: tracing]
109+ liftIO $ traceEvent (localEventBus (processNode proc))
110+ (MxSentToName label us msg')
111+ sendCtrlMsg Nothing (NamedSend label msg')
84
112
85-- | Named send to a process in a remote registry (asynchronous)
113-- | Named send to a process in a remote registry (asynchronous)
86nsendRemote :: Serializable a => NodeId -> String -> a -> Process ()
114nsendRemote :: Serializable a => NodeId -> String -> a -> Process ()
87nsendRemote nid label msg = do
115nsendRemote nid label msg = do
88 proc <- ask
116 proc <- ask
89− if localNodeId (processNode proc) == nid
117+ let us = processId proc
118+ let node = processNode proc
119+ if localNodeId node == nid
90 then nsend label msg
120 then nsend label msg
91− else sendCtrlMsg (Just nid) (NamedSend label (createMessage msg))
121+ else
122+ let lbl = label ++ "@" ++ show nid in do
123+ -- see [note: tracing] NB: this is a remote call to another NC...
124+ liftIO $ traceEvent (localEventBus node)
125+ (MxSentToName lbl us (wrapMessage msg))
126+ sendCtrlMsg (Just nid) (NamedSend label (createMessage msg))
92
127
93-- | Send a message
128-- | Send a message
94send :: Serializable a => ProcessId -> a -> Process ()
129send :: Serializable a => ProcessId -> a -> Process ()
95send them msg = do
130send them msg = do
96 proc <- ask
131 proc <- ask
97 let node = localNodeId (processNode proc)
132 let node = localNodeId (processNode proc)
98− destNode = (processNodeId them) in do
133+ destNode = (processNodeId them)
99− case destNode == node of
134+ us = (processId proc)
100− True -> unsafeSendLocal them msg
135+ msg' = wrapMessage msg in do
101− False -> liftIO $ sendMessage (processNode proc)
136+ -- see [note: tracing]
102− (ProcessIdentifier (processId proc))
137+ liftIO $ traceEvent (localEventBus (processNode proc))
103− (ProcessIdentifier them)
138+ (MxSent them us msg')
104− NoImplicitReconnect
139+ if destNode == node
105− msg
140+ then sendCtrlMsg Nothing $ LocalSend them msg'
106− where
141+ else liftIO $ sendMessage (processNode proc)
107− unsafeSendLocal :: (Serializable a) => ProcessId -> a -> Process ()
142+ (ProcessIdentifier (processId proc))
108− unsafeSendLocal pid msg' =
143+ (ProcessIdentifier them)
109− sendCtrlMsg Nothing $ LocalSend pid (unsafeCreateUnencodedMessage msg')
144+ NoImplicitReconnect
145+ msg
110
146
111-- | Send a message unreliably.
147-- | Send a message unreliably.
112--
148--
@@ -121,30 +157,43 @@ usend :: Serializable a => ProcessId -> a -> Process ()
121usend them msg = do
157usend them msg = do
122 proc <- ask
158 proc <- ask
123 let there = processNodeId them
159 let there = processNodeId them
160+ let (us, node) = (processId proc, processNode proc)
161+ let msg' = wrapMessage msg
162+ -- see [note: tracing]
163+ liftIO $ traceEvent (localEventBus node) (MxSent them us msg')
124 if localNodeId (processNode proc) == there
164 if localNodeId (processNode proc) == there
125− then sendCtrlMsg Nothing $
165+ then sendCtrlMsg Nothing $ LocalSend them msg'
126− LocalSend them (unsafeCreateUnencodedMessage msg)
127 else sendCtrlMsg (Just there) $ UnreliableSend (processLocalId them)
166 else sendCtrlMsg (Just there) $ UnreliableSend (processLocalId them)
128 (createMessage msg)
167 (createMessage msg)
129
168
169+-- [note: tracing]
170+-- Note that tracing writes to the local node's control channel, and this
171+-- module explicitly specifies to its clients that it does unsafe message
172+-- encoding. The same is true for the messages it puts onto the Management
173+-- event bus, however we do *not* want unevaluated thunks hitting the event
174+-- bus control thread. Hence the word /Unsafe/ in this module's name!
175+--
176+
130-- | Send a message on a typed channel
177-- | Send a message on a typed channel
131sendChan :: Serializable a => SendPort a -> a -> Process ()
178sendChan :: Serializable a => SendPort a -> a -> Process ()
132sendChan (SendPort cid) msg = do
179sendChan (SendPort cid) msg = do
133 proc <- ask
180 proc <- ask
134− let node = localNodeId (processNode proc)
181+ let node = processNode proc
135− destNode = processNodeId (sendPortProcessId cid) in do
182+ pid = processId proc
136− case destNode == node of
183+ us = localNodeId node
137− True -> unsafeSendChanLocal cid msg
184+ them = processNodeId (sendPortProcessId cid)
138− False -> do
185+ msg' = wrapMessage msg in do
139− liftIO $ sendBinary (processNode proc)
186+ liftIO $ traceEvent (localEventBus node) (MxSentToPort pid cid msg')
140− (ProcessIdentifier (processId proc))
187+ if them == us
141− (SendPortIdentifier cid)
188+ then unsafeSendChanLocal cid msg' -- NB: we wrap to P.Message !!!
142− NoImplicitReconnect
189+ else liftIO $ sendBinary node
143− msg
190+ (ProcessIdentifier pid)
191+ (SendPortIdentifier cid)
192+ NoImplicitReconnect
193+ msg
144 where
194 where
145− unsafeSendChanLocal :: (Serializable a) => SendPortId -> a -> Process ()
195+ unsafeSendChanLocal :: SendPortId -> Message -> Process ()
146− unsafeSendChanLocal spId msg' =
196+ unsafeSendChanLocal p m = sendCtrlMsg Nothing $ LocalPortSend p m
147− sendCtrlMsg Nothing $ LocalPortSend spId (unsafeCreateUnencodedMessage msg')
148
197
149-- | Create an unencoded @Message@ for any @Serializable@ type.
198-- | Create an unencoded @Message@ for any @Serializable@ type.
150wrapMessage :: Serializable a => a -> Message
199wrapMessage :: Serializable a => a -> Message
stack-ghc-7.10.3.yaml+5−1
@@ -3,14 +3,18 @@ resolver: nightly-2016-03-08
3packages:
3packages:
4- '.'
4- '.'
5- distributed-process-tests/
5- distributed-process-tests/
6+- location:
7+ git: https://github.com/haskell-distributed/network-transport-tcp.git
8+ commit: d87d6f55697a94a1fbf211ff9c1bb769a1e129cd
9+ extra-dep: true
6
10
7extra-deps:
11extra-deps:
8- rank1dynamic-0.4.0
12- rank1dynamic-0.4.0
9- distributed-static-0.3.8
13- distributed-static-0.3.8
10- network-transport-0.5.2
14- network-transport-0.5.2
11−- network-transport-tcp-0.6.0
12- network-transport-inmemory-0.5.2
15- network-transport-inmemory-0.5.2
13- rematch-0.2.0.0
16- rematch-0.2.0.0
17+- exceptions-0.8.2.1 # test dependency
14
18
15flags:
19flags:
16 distributed-process-tests:
20 distributed-process-tests:
stack-ghc-8.0.2.yaml+8−2
@@ -1,16 +1,22 @@
1−resolver: lts-9.0
1+resolver: lts-9.21
2+# added -location for network-transport-tcp-0.6.0
3+# since c7fd79 broke the client facing API for createTransport :/
2
4
3packages:
5packages:
4- '.'
6- '.'
5- distributed-process-tests/
7- distributed-process-tests/
8+- location:
9+ git: https://github.com/haskell-distributed/network-transport-tcp.git
10+ commit: d87d6f55697a94a1fbf211ff9c1bb769a1e129cd
11+ extra-dep: true
6
12
7extra-deps:
13extra-deps:
8- rank1dynamic-0.4.0
14- rank1dynamic-0.4.0
9- distributed-static-0.3.8
15- distributed-static-0.3.8
10- network-transport-0.5.2
16- network-transport-0.5.2
11−- network-transport-tcp-0.6.0
12- network-transport-inmemory-0.5.2
17- network-transport-inmemory-0.5.2
13- rematch-0.2.0.0
18- rematch-0.2.0.0
19+# - exceptions-0.8.2.1 # test dependency
14
20
15flags:
21flags:
16 distributed-process-tests:
22 distributed-process-tests:
stack-ghc-8.2.2.yamlnew file+21
@@ -0,0 +1,21 @@
1+resolver: lts-11.22
2+
3+packages:
4+- '.'
5+- distributed-process-tests/
6+- location:
7+ git: https://github.com/haskell-distributed/network-transport-tcp.git
8+ commit: d87d6f55697a94a1fbf211ff9c1bb769a1e129cd
9+ extra-dep: true
10+
11+extra-deps:
12+- rank1dynamic-0.4.0
13+- distributed-static-0.3.8
14+- network-transport-0.5.2
15+- network-transport-inmemory-0.5.2
16+- rematch-0.2.0.0
17+# - exceptions-0.8.2.1 # test dependency
18+
19+flags:
20+ distributed-process-tests:
21+ tcp: true
stack-ghc-8.4.4.yamlnew file+20
@@ -0,0 +1,20 @@
1+resolver: lts-12.18
2+
3+packages:
4+- '.'
5+- distributed-process-tests/
6+- location:
7+ git: https://github.com/haskell-distributed/network-transport-tcp.git
8+ commit: d87d6f55697a94a1fbf211ff9c1bb769a1e129cd
9+ extra-dep: true
10+
11+extra-deps:
12+- rank1dynamic-0.4.0
13+- distributed-static-0.3.8
14+- network-transport-0.5.2
15+- network-transport-inmemory-0.5.2
16+- rematch-0.2.0.0
17+
18+flags:
19+ distributed-process-tests:
20+ tcp: true
stack-nightly.yamlnew file+26
@@ -0,0 +1,26 @@
1+resolver: nightly
2+
3+packages:
4+- '.'
5+- distributed-process-tests/
6+- location:
7+ git: https://github.com/haskell-distributed/distributed-static.git
8+ commit: c51e6aadd3a44c044e96a8e986c11d3a94b45b11
9+ extra-dep: true
10+- location:
11+ git: https://github.com/haskell-distributed/network-transport-tcp.git
12+ commit: d58250e2388b86a1865ac4139373cbdc106e72e1
13+ extra-dep: true
14+- location:
15+ git: https://github.com/haskell-distributed/network-transport-inmemory.git
16+ commit: 0ce97924f15a8c1570b2355151834305c9bd2a28
17+ extra-dep: true
18+
19+extra-deps:
20+- rank1dynamic-0.4.0
21+- network-transport-0.5.2
22+- rematch-0.2.0.0
23+
24+flags:
25+ distributed-process-tests:
26+ tcp: true
stack.yaml+7−2
@@ -1,14 +1,19 @@
1−resolver: nightly-2017-08-20
1+resolver: lts-12.18 # Use GHC 8.4.4
2+
3+# TODO: we can enable nightly-2018-11-11 once PR #319 is merged
2
4
3packages:
5packages:
4- '.'
6- '.'
5- distributed-process-tests/
7- distributed-process-tests/
8+- location:
9+ git: https://github.com/haskell-distributed/network-transport-tcp.git
10+ commit: d87d6f55697a94a1fbf211ff9c1bb769a1e129cd
11+ extra-dep: true
6
12
7extra-deps:
13extra-deps:
8- rank1dynamic-0.4.0
14- rank1dynamic-0.4.0
9- distributed-static-0.3.8
15- distributed-static-0.3.8
10- network-transport-0.5.2
16- network-transport-0.5.2
11−- network-transport-tcp-0.6.0
12- network-transport-inmemory-0.5.2
17- network-transport-inmemory-0.5.2
13- rematch-0.2.0.0
18- rematch-0.2.0.0
14
19