@@ -25,18 +25,13 @@ import Control.Concurrent.MVar
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 Control.Monad.Catch (try, catch, finally, mask, onException)
28+import qualified Control.Exception as Ex (catch)
29−import Control.Applicative ((<|>))
29+import Control.Applicative ((<$>), (<*>), pure, (<|>))
30import qualified Network.Transport as NT (closeEndPoint, EndPointAddress)
30import qualified Network.Transport as NT (closeEndPoint, EndPointAddress)
31−import Control.Distributed.Process hiding
31+import Control.Distributed.Process
38import Control.Distributed.Process.Internal.Types
32import Control.Distributed.Process.Internal.Types
33+ ( NodeId(nodeAddress)
39− ( LocalNode(localEndPoint)
34+ , LocalNode(localEndPoint)
40 , ProcessExitException(..)
35 , ProcessExitException(..)
41 , nullProcessId
36 , nullProcessId
42 , createUnencodedMessage
37 , createUnencodedMessage
@@ -44,13 +39,13 @@ import Control.Distributed.Process.Internal.Types
44import Control.Distributed.Process.Node
39import Control.Distributed.Process.Node
45import Control.Distributed.Process.Debug
40import Control.Distributed.Process.Debug
46import Control.Distributed.Process.Management.Internal.Types
41import Control.Distributed.Process.Management.Internal.Types
47−import Control.Distributed.Process.Tests.Internal.Utils (shouldBe, pause)
42+import Control.Distributed.Process.Tests.Internal.Utils (shouldBe)
48import Control.Distributed.Process.Serializable (Serializable)
43import Control.Distributed.Process.Serializable (Serializable)
49−import Data.Maybe (isNothing, isJust)
44+
50import Test.HUnit (Assertion, assertBool, assertFailure)
45import Test.HUnit (Assertion, assertBool, assertFailure)
51import Test.Framework (Test, testGroup)
46import Test.Framework (Test, testGroup)
52import Test.Framework.Providers.HUnit (testCase)
47import Test.Framework.Providers.HUnit (testCase)
53−import Control.Rematch hiding (match, isNothing, isJust)
48+import Control.Rematch hiding (match)
54import Control.Rematch.Run (Match(..))
49import Control.Rematch.Run (Match(..))
56newtype Ping = Ping ProcessId
51newtype Ping = Ping ProcessId
@@ -73,7 +68,7 @@ expectThat a matcher = case res of
73forkTry :: IO () -> IO ThreadId
68forkTry :: IO () -> IO ThreadId
74forkTry p = do
69forkTry p = do
75 tid <- myThreadId
70 tid <- myThreadId
76− forkIO $ catch p (\e -> throwTo tid (e :: SomeException))
71+ forkIO $ Ex.catch p (\e -> throwTo tid (e :: SomeException))
78-- | The ping server from the paper
73-- | The ping server from the paper
79ping :: Process ()
74ping :: Process ()
@@ -87,9 +82,10 @@ verifyClient :: String -> MVar Bool -> IO ()
87verifyClient s b = takeMVar b >>= assertBool s
82verifyClient s b = takeMVar b >>= assertBool s
89expectPing :: MVar Bool -> Process ()
84expectPing :: MVar Bool -> Process ()
90−expectPing mv = expect >>= liftIO . putMVar mv . checkPing
85+expectPing mv = expect >>= liftIO . putMVar mv . checkPing
92 checkPing (Ping _) = True
87 checkPing (Ping _) = True
94-- | Quick and dirty synchronous version of whereisRemoteAsync
90-- | Quick and dirty synchronous version of whereisRemoteAsync
95whereisRemote :: NodeId -> String -> Process (Maybe ProcessId)
91whereisRemote :: NodeId -> String -> Process (Maybe ProcessId)
@@ -239,10 +235,10 @@ testPing TestTransport{..} = do
239 pid <- getSelfPid
235 pid <- getSelfPid
240 replicateM_ numPings $ do
236 replicateM_ numPings $ do
241 send pingServer (Pong pid)
237 send pingServer (Pong pid)
242− p <- expectTimeout 3000000
238+ p <- expect
243 case p of
239 case p of
244− Just (Ping _) -> return ()
240+ Ping _ -> return ()
245− Nothing -> die "Failed to receive Ping"
241+ _ -> die "Unexpected message"
247 putMVar clientDone ()
243 putMVar clientDone ()
@@ -559,7 +555,7 @@ testMergeChannels TestTransport{..} = do
559 ss <- liftIO $ mapM readMVar vs
555 ss <- liftIO $ mapM readMVar vs
560 case ss of
556 case ss of
561 [sa, sb, sc] ->
557 [sa, sb, sc] ->
562− mapM_ ((>> pause 10000) . uncurry sendChan)
558+ mapM_ ((>> liftIO (threadDelay 10000)) . uncurry sendChan)
563 [ -- a, b, c
559 [ -- a, b, c
564 (sa, 'a')
560 (sa, 'a')
565 , (sb, 'b')
561 , (sb, 'b')
@@ -735,6 +731,7 @@ testRemoteRegistry :: TestTransport -> Assertion
735testRemoteRegistry TestTransport{..} = do
731testRemoteRegistry TestTransport{..} = do
736 node1 <- newLocalNode testTransport initRemoteTable
732 node1 <- newLocalNode testTransport initRemoteTable
737 node2 <- newLocalNode testTransport initRemoteTable
733 node2 <- newLocalNode testTransport initRemoteTable
734+ done <- newEmptyMVar
739 pingServer <- forkProcess node1 ping
736 pingServer <- forkProcess node1 ping
740 deadProcess <- forkProcess node1 (return ())
737 deadProcess <- forkProcess node1 (return ())
@@ -749,7 +746,6 @@ testRemoteRegistry TestTransport{..} = do
750 pid <- verifyWhereIsRemote nid1 "ping"
747 pid <- verifyWhereIsRemote nid1 "ping"
751 liftIO $ assertBool "Expected pindServer to match pid" $ pingServer == pid
748 liftIO $ assertBool "Expected pindServer to match pid" $ pingServer == pid
753 us <- getSelfPid
749 us <- getSelfPid
754 nsendRemote nid1 "ping" (Pong us)
750 nsendRemote nid1 "ping" (Pong us)
755 receiveWait [
751 receiveWait [
@@ -759,22 +755,22 @@ testRemoteRegistry TestTransport{..} = do
759 -- test that if process was not registered Nothing is returned
755 -- test that if process was not registered Nothing is returned
760 -- in owner field.
756 -- in owner field.
761 registerRemoteAsync nid1 "dead" deadProcess
757 registerRemoteAsync nid1 "dead" deadProcess
762− receiveWait [ matchIf (\(RegisterReply label' f mPid) -> "dead" == label')
759+ matchIf (\(RegisterReply label' False Nothing) -> "dead" == label')
763− (\(RegisterReply _ f mPid) -> return (not f && isNothing mPid))
760+ (\(RegisterReply _ _ _) -> return ()) ]
764− ] >>= liftIO . assertBool "Expected False Nothing in RegisterReply"
766 registerRemoteAsync nid1 "ping" deadProcess
761 registerRemoteAsync nid1 "ping" deadProcess
767 receiveWait [
762 receiveWait [
768− matchIf (\(RegisterReply label' False mPid) ->
763+ matchIf (\(RegisterReply label' False (Just pid)) ->
769− "ping" == label' && isJust mPid)
764+ "ping" == label' && pid == pingServer)
770− (\(RegisterReply _ f (Just pid'')) -> return (not f && pid'' == pingServer))
765+ (\(RegisterReply _ _ _) -> return ()) ]
771− ] >>= liftIO . assertBool "Expected False and (Just alreadyRegisteredPid) in RegisterReply"
773 unregisterRemoteAsync nid1 "dead"
766 unregisterRemoteAsync nid1 "dead"
774 receiveWait [
767 receiveWait [
775− matchIf (\(RegisterReply label' _ _) -> "dead" == label')
768+ matchIf (\(RegisterReply label' False Nothing) ->
769+ "dead" == label' && pid == pingServer)
776− (\(RegisterReply _ f mPid) -> return (not f && isNothing mPid))
770+ (\(RegisterReply _ _ _) -> return ()) ]
777− ] >>= liftIO . assertBool "Expected False and Nothing in RegisterReply"
771+ liftIO $ putMVar done ()
779testRemoteRegistryRemoteProcess :: TestTransport -> Assertion
775testRemoteRegistryRemoteProcess :: TestTransport -> Assertion
780testRemoteRegistryRemoteProcess TestTransport{..} = do
776testRemoteRegistryRemoteProcess TestTransport{..} = do
@@ -946,7 +942,9 @@ testUSend usendPrim TestTransport{..} numMessages = do
946 isSorted _ = True
942 isSorted _ = True
947 -- The list can't be null since there are no failures after sending
943 -- The list can't be null since there are no failures after sending
948 -- the last message.
944 -- the last message.
949− liftIO $ putMVar usendTestOk $ isSorted msgs && not (null msgs)
945+ liftIO $ do
946+ assertBool "Unexpected failure after sending last msg" $ isSorted msgs && not (null msgs)
947+ putMVar usendTestOk ()
951 forkProcess node2 $ do
949 forkProcess node2 $ do
952 them <- liftIO $ readMVar processA
950 them <- liftIO $ readMVar processA
@@ -957,8 +955,7 @@ testUSend usendPrim TestTransport{..} numMessages = do
957 usendPrim them i
955 usendPrim them i
958 liftIO (threadDelay 30000)
956 liftIO (threadDelay 30000)
960− res <- takeMVar usendTestOk
958+ takeMVar usendTestOk
961− assertBool "Unexpected failure after sending last msg" res
963-- | Test 'matchAny'. This repeats the 'testMath' but with a proxy server
960-- | Test 'matchAny'. This repeats the 'testMath' but with a proxy server
964-- in between
961-- in between
@@ -1272,76 +1269,6 @@ testReceiveChanFeatures TestTransport{..} = do
1273 takeMVar done
1270 takeMVar done
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
1282− pid <- spawnLocal $ do tCh <- newChan :: Process (SendPort (), ReceivePort ())
1283− liftIO $ putMVar tchMV tCh
1284− expect :: Process ()
1287− expect :: Process ()
1289− mRefPid <- monitor pid
1291− cPid <- spawnLocal $ do
1292− (sp', rp) <- liftIO $ takeMVar tchMV
1293− -- say "picked up our test channel"
1295− -- say "told pid to continue"
1296− res <- receiveChanTimeout delay rp
1298− Nothing -> say "initial chan () missing!" >> (liftIO $ putMVar result False)
1299− Just () -> do mr <- monitor pid
1301− -- say "sending pid a second () will cause it to exit"
1304− -- say "make sure we see a DOWN notification for pid having stopped"
1305− receiveWait [ match (\(_ :: ProcessMonitorNotification) -> return ()) ]
1307− -- now that pid has died, the send port should be useless...
1308− liftIO $ putMVar tchMV (sp', rp)
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
1316− mRefCPid <- monitor cPid
1319− [ matchIf (\(ProcessMonitorNotification r _ _) -> r == mRefPid)
1323− -- say "seen first pid die..."
1325− (sendPort, _) <- liftIO $ takeMVar tchMV
1326− sendChan sendPort ()
1327− -- say "sent () after owning pid died"
1329− -- let cPid know we've written to the channel...
1333− [ matchIf (\(ProcessMonitorNotification r _ _) -> r == mRefCPid)
1337− -- say "seen both pids die now..."
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
1345testKillLocal :: TestTransport -> Assertion
1272testKillLocal :: TestTransport -> Assertion
1346testKillLocal TestTransport{..} = do
1273testKillLocal TestTransport{..} = do
1347 localNode <- newLocalNode testTransport initRemoteTable
1274 localNode <- newLocalNode testTransport initRemoteTable
@@ -1554,73 +1481,113 @@ testExitRemote TestTransport{..} = do
1555testRegistryMonitoring :: TestTransport -> Assertion
1482testRegistryMonitoring :: TestTransport -> Assertion
1556testRegistryMonitoring TestTransport{..} = do
1483testRegistryMonitoring TestTransport{..} = do
1557− node1 <- newLocalNode testTransport initRemoteTable
1484+ localNode <- newLocalNode testTransport initRemoteTable
1558− node2 <- newLocalNode testTransport initRemoteTable
1485+ remoteNode <- newLocalNode testTransport initRemoteTable
1560− let nid = localNodeId node2
1561− pid <- forkProcess node1 $ do
1563− runUntilRegistered nid self
1564− say $ (show self) ++ " registered as " ++ regName
1565− expect :: Process ()
1566− say $ (show self) ++ " exiting normally"
1568− runProcess node2 $ do
1569− register regName pid
1570− say $ regName ++ " registered to " ++ show pid
1571− res <- whereis regName
1573− say $ " sent finish signal to " ++ show pid
1575− liftIO $ assertBool "expected (Just pid)" $ res == (Just pid)
1578− -- This delay isn't essential!
1579− -- The test case passes perfectly fine without it (feel free to comment out
1580− -- and see), however waiting a few seconds here, makes it much more likely
1581− -- that in delayUntilMaybeUnregistered we will hit the match case right
1582− -- away, and thus not be subjected to a 20 second delay. The value of 4
1583− -- seconds appears to work optimally on osx and across several linux distros
1584− -- running in virtual machines (which is essentially what we do in CI)
1585− void $ receiveTimeout 4000000 [ matchAny return ]
1587− -- This delay doesn't serve much purpose in the happy path, however if some
1588− -- future patch breaks the cooperative behaviour of node controllers viz
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
1596− runProcess node2 $ delayUntilMaybeUnregistered nid pid
1598− regHere <- newEmptyMVar
1599− runProcess node2 $ whereis regName >>= liftIO . putMVar regHere
1600− res <- takeMVar regHere
1602− Nothing -> return ()
1603− Just pid -> assertBool ("expected Nothing, but got " ++ show pid) False
1605− where
1488+ -- Local process. Test if local process will be removed from
1606− runUntilRegistered nid us = do
1489+ -- registry when it dies.
1607− whereisRemoteAsync nid regName
1490+ box <- newEmptyMVar
1608− receiveWait [
1491+ runProcess localNode $ do
1609− matchIf (\(WhereIsReply n (Just p)) -> n == regName && p == us)
1492+ pid <- spawnLocal $ do
1610− (const $ return ())
1493+ expect
1611− ]
1494+ register "test" pid
1612−
1495+ tpid <- whereis "test"
1613− delayUntilMaybeUnregistered nid p = do
1496+ if tpid == Just pid
1614− whereisRemoteAsync nid regName
1497+ then do _ <- monitor pid
1615− res <- receiveTimeout 20000000 {- 20 sec delay -} [
1498+ send pid ()
1616− matchIf (\(WhereIsReply n p) -> n == regName && isNothing p)
1499+ ProcessMonitorNotification{} <- expect
1617− (const $ return ())
1500+ tpid1 <- whereis "test"
1618− ]
1501+ liftIO $ putMVar box (Nothing == tpid1)
1619− case res of
1502+ else liftIO $ putMVar box False
1620− Just () -> return ()
1503+
1621− Nothing -> delayUntilMaybeUnregistered nid p
1504+ takeMVar box >>= assertBool "expected local process to not be registered"
1507+ -- Remote process. Test if remote process entry is removed
1508+ -- from registry when process dies.
1509+ remote1 <- testRemote remoteNode
1510+ runProcess localNode $
1512+ w <- whereis "test" :: Process (Maybe ProcessId)
1513+ forM_ w (const waitpoll)
1514+ in do register "test" remote1
1515+ -- [race-condition] invalid test if we force exit before waitpoll runs
1516+ registered <- whereis "test"
1517+ registered `shouldBe` (equalTo (Just remote1))
1523+ -- Many labels. Test if all labels associated with process
1524+ -- are removed from registry when it dies.
1525+ remote2 <- testRemote remoteNode
1526+ runProcess localNode $
1528+ w1 <- whereis "test-3" :: Process (Maybe ProcessId)
1529+ w2 <- whereis "test-4" :: Process (Maybe ProcessId)
1530+ forM_ (w1 <|> w2) (const waitpoll)
1531+ in do register "test-3" remote2
1532+ register "test-4" remote2
1537+{- XXX: waiting including patch for nsend for remote process
1538+ remote3 <- testRemote remoteNode
1539+ remote4 <- testRemote remoteNode
1540+ -- test many labels
1541+ runProcess localNode $ do
1542+ register "test-3" remote3
1543+ reregister "test-3" remote4
1545+ liftIO $ threadDelay 50000 -- XXX: racy
1548+ ProcessMonitorNotification{} <- expect
1552+ -- Test registerRemoteAsync properties. Add a local process to
1553+ -- remote registry and checks that it is removed
1554+ -- when the process dies.
1555+ remote5 <- testRemote remoteNode
1556+ runProcess localNode $ do
1557+ registerRemoteAsync (localNodeId remoteNode) "test" remote5
1559+ match (\(RegisterReply _ True _) -> return ())
1562+ whereisRemoteAsync (localNodeId remoteNode) "test"
1564+ match (\(WhereIsReply _ mr) -> forM_ mr (const waitpoll))
1623− regName = "testRegisterRemote"
1568+ -- Add remote process to remote registry and checks if
1569+ -- entry is removed then process is dead.
1570+ remote6 <- testRemote localNode
1571+ runProcess localNode $ do
1572+ registerRemoteAsync (localNodeId remoteNode) "test" remote6
1574+ match (\(RegisterReply _ True _) -> return ())
1577+ whereisRemoteAsync (localNodeId remoteNode) "test"
1579+ match (\(WhereIsReply _ mr) -> forM_ mr (const waitpoll))
1583+ testRemote node = do
1584+ -- test many labels
1585+ pidBox <- newEmptyMVar
1586+ forkProcess node $ do
1588+ liftIO $ putMVar pidBox us
1589+ expect :: Process ()
1625testUnsafeSend :: TestTransport -> Assertion
1592testUnsafeSend :: TestTransport -> Assertion
1626testUnsafeSend TestTransport{..} = do
1593testUnsafeSend TestTransport{..} = do
@@ -1814,7 +1781,6 @@ tests testtrans = return [
1814 , testCase "MatchMessageUnwrap" (testMatchMessageWithUnwrap testtrans)
1781 , testCase "MatchMessageUnwrap" (testMatchMessageWithUnwrap testtrans)
1815 , testCase "ReceiveChanTimeout" (testReceiveChanTimeout testtrans)
1782 , testCase "ReceiveChanTimeout" (testReceiveChanTimeout testtrans)
1816 , testCase "ReceiveChanFeatures" (testReceiveChanFeatures testtrans)
1783 , testCase "ReceiveChanFeatures" (testReceiveChanFeatures testtrans)
1817− , testCase "ChanLifecycle" (testChanLifecycle testtrans)
1818 , testCase "KillLocal" (testKillLocal testtrans)
1784 , testCase "KillLocal" (testKillLocal testtrans)
1819 , testCase "KillRemote" (testKillRemote testtrans)
1785 , testCase "KillRemote" (testKillRemote testtrans)
1820 , testCase "Die" (testDie testtrans)
1786 , testCase "Die" (testDie testtrans)
@@ -1824,7 +1790,7 @@ tests testtrans = return [
1824 , testCase "MaskRestoreScope" (testMaskRestoreScope testtrans)
1790 , testCase "MaskRestoreScope" (testMaskRestoreScope testtrans)
1825 , testCase "ExitLocal" (testExitLocal testtrans)
1791 , testCase "ExitLocal" (testExitLocal testtrans)
1826 , testCase "ExitRemote" (testExitRemote testtrans)
1792 , testCase "ExitRemote" (testExitRemote testtrans)
1827− , testCase "RegistryMonitoring" (testRegistryMonitoring testtrans)
1793+ , testCase "TestRegistryMonitor" (testRegistryMonitoring testtrans)
1828 , testCase "TextCallLocal" (testCallLocal testtrans)
1794 , testCase "TextCallLocal" (testCallLocal testtrans)
1829 -- Unsafe Primitives
1795 -- Unsafe Primitives
1830 , testCase "TestUnsafeSend" (testUnsafeSend testtrans)
1796 , testCase "TestUnsafeSend" (testUnsafeSend testtrans)