git.iko.soy/distributed-process
on master help sign in
More error handling
10fef45Edsko de Vries
distributed-process/src/Control/Distributed/Process.hs+21−17
@@ -423,7 +423,8 @@ handleIncomingMessages node = go [] Map.empty
423 NT.ConnectionClosed cid ->
423 NT.ConnectionClosed cid ->
424 go (List.delete cid halfOpenConns) (Map.delete cid openConns)
424 go (List.delete cid halfOpenConns) (Map.delete cid openConns)
425 NT.ErrorEvent _ ->
425 NT.ErrorEvent _ ->
426− fail "handleIncomingMessages: TODO 3"
426+ -- fail "handleIncomingMessages: TODO 3"
427+ go halfOpenConns openConns
427 NT.EndPointClosed ->
428 NT.EndPointClosed ->
428 return ()
429 return ()
429 NT.ReceivedMulticast _ _ ->
430 NT.ReceivedMulticast _ _ ->
@@ -437,26 +438,26 @@ handleIncomingMessages node = go [] Map.empty
437--------------------------------------------------------------------------------
438--------------------------------------------------------------------------------
438
439
439getConnectionTo :: ProcessId -> Process (Maybe NT.Connection)
440getConnectionTo :: ProcessId -> Process (Maybe NT.Connection)
440−getConnectionTo pid = do
441+getConnectionTo them = do
441 ourState <- processState <$> ask
442 ourState <- processState <$> ask
442− mConn <- liftIO $ withMVar ourState $ return . (^. connectionTo pid)
443+ mConn <- liftIO $ withMVar ourState $ return . (^. connectionTo them)
443 case mConn of
444 case mConn of
444 Just conn -> return . Just $ conn
445 Just conn -> return . Just $ conn
445− Nothing -> createConnectionTo pid
446+ Nothing -> createConnectionTo them
446
447
447createConnectionTo :: ProcessId -> Process (Maybe NT.Connection)
448createConnectionTo :: ProcessId -> Process (Maybe NT.Connection)
448−createConnectionTo pid = do
449+createConnectionTo them = do
449 proc <- ask
450 proc <- ask
450 mConn <- liftIO $ NT.connect (localEndPoint . processNode $ proc)
451 mConn <- liftIO $ NT.connect (localEndPoint . processNode $ proc)
451− (processAddress pid)
452+ (processAddress them)
452 NT.ReliableOrdered
453 NT.ReliableOrdered
453 NT.defaultConnectHints
454 NT.defaultConnectHints
454 case mConn of
455 case mConn of
455 Right conn -> do
456 Right conn -> do
456 mConn' <- liftIO $ modifyMVar (processState proc) $ \st ->
457 mConn' <- liftIO $ modifyMVar (processState proc) $ \st ->
457− case st ^. connectionTo pid of
458+ case st ^. connectionTo them of
458 Just conn' -> return (st, Just conn')
459 Just conn' -> return (st, Just conn')
459− Nothing -> return (connectionTo pid ^= Just conn $ st, Nothing)
460+ Nothing -> return (connectionTo them ^= Just conn $ st, Nothing)
460 case mConn' of
461 case mConn' of
461 Just conn' -> do
462 Just conn' -> do
462 -- Somebody else already created a connection while we weren't looking
463 -- Somebody else already created a connection while we weren't looking
@@ -467,27 +468,30 @@ createConnectionTo pid = do
467 liftIO $ NT.close conn
468 liftIO $ NT.close conn
468 return . Just $ conn'
469 return . Just $ conn'
469 Nothing -> do
470 Nothing -> do
470− sendBinary pid conn $ lpidToPayload (processLocalId pid)
471+ sendBinary them conn $ lpidToPayload (processLocalId them)
471 return . Just $ conn
472 return . Just $ conn
472 Left _err -> do
473 Left _err -> do
473 -- TODO: should probably pass this error to remoteProcessFailed
474 -- TODO: should probably pass this error to remoteProcessFailed
474− remoteProcessFailed pid
475+ remoteProcessFailed them
475 return Nothing
476 return Nothing
476
477
477sendBinary :: ProcessId -> NT.Connection -> [BSS.ByteString] -> Process ()
478sendBinary :: ProcessId -> NT.Connection -> [BSS.ByteString] -> Process ()
478−sendBinary pid conn payload = do
479+sendBinary them conn payload = do
479 result <- liftIO $ NT.send conn payload
480 result <- liftIO $ NT.send conn payload
480 case result of
481 case result of
481 Right () -> return ()
482 Right () -> return ()
482− Left err -> do
483+ Left _ ->
484+ {-
485+ -- TODO: put into unreachable state rather than Nothing
483− ourState <- processState <$> ask
486+ -- ourState <- processState <$> ask
484 liftIO $ modifyMVar_ ourState $
487 liftIO $ modifyMVar_ ourState $
485− return . (connectionTo pid ^= Nothing)
488+ return . (connectionTo them ^= Nothing)
486− liftIO $ throwIO err
489+ -}
490+ remoteProcessFailed them
487
491
488sendMessage :: ProcessId -> NT.Connection -> Message -> Process ()
492sendMessage :: ProcessId -> NT.Connection -> Message -> Process ()
489−sendMessage pid conn (Message fp enc) =
493+sendMessage them conn (Message fp enc) =
490− sendBinary pid conn $ encodeFingerprint fp : BSL.toChunks enc
494+ sendBinary them conn $ encodeFingerprint fp : BSL.toChunks enc
491
495
492payloadToMessage :: [BSS.ByteString] -> Message
496payloadToMessage :: [BSS.ByteString] -> Message
493payloadToMessage payload = Message fp msg
497payloadToMessage payload = Message fp msg
distributed-process/tests/TestCH.hs+44−4
@@ -3,7 +3,7 @@ module Main where
3import Prelude hiding (catch)
3import Prelude hiding (catch)
4import Data.Binary (Binary)
4import Data.Binary (Binary)
5import Data.Typeable (Typeable)
5import Data.Typeable (Typeable)
6−import Control.Concurrent (forkIO)
6+import Control.Concurrent (forkIO, threadDelay)
7import Control.Concurrent.MVar ( newEmptyMVar
7import Control.Concurrent.MVar ( newEmptyMVar
8 , putMVar
8 , putMVar
9 , takeMVar
9 , takeMVar
@@ -11,6 +11,7 @@ import Control.Concurrent.MVar ( newEmptyMVar
11 )
11 )
12import Control.Monad (replicateM_)
12import Control.Monad (replicateM_)
13import Control.Distributed.Process
13import Control.Distributed.Process
14+import Control.Monad.IO.Class (liftIO)
14import qualified Network.Transport as NT (Transport)
15import qualified Network.Transport as NT (Transport)
15import Network.Transport.TCP (createTransport, defaultTCPParameters)
16import Network.Transport.TCP (createTransport, defaultTCPParameters)
16import TestAuxiliary
17import TestAuxiliary
@@ -57,7 +58,6 @@ testPing transport = do
57
58
58 putMVar clientDone ()
59 putMVar clientDone ()
59
60
60−
61 takeMVar clientDone
61 takeMVar clientDone
62
62
63-- | Set up monitoring, send first message to an unreachable node
63-- | Set up monitoring, send first message to an unreachable node
@@ -109,11 +109,51 @@ testMonitor2 transport = do
109
109
110 takeMVar done
110 takeMVar done
111
111
112+-- The first send succeeds, connection is set up, but then the second send
113+-- fails.
114+--
115+-- TODO: should we specify that we receive exactly one notification per process
116+-- failure?
117+testMonitor3 :: NT.Transport -> IO ()
118+testMonitor3 transport = do
119+ firstSend <- newEmptyMVar
120+ serverAddr <- newEmptyMVar
121+ serverDead <- newEmptyMVar
122+ done <- newEmptyMVar
123+
124+ forkIO $ do
125+ localNode <- newLocalNode transport
126+ -- TODO: what happens when processes terminate?
127+ addr <- forkProcess localNode $ return ()
128+ putMVar serverAddr addr
129+ readMVar firstSend
130+ closeLocalNode localNode
131+ threadDelay 10000 -- Give the TCP layer a chance to actually close the socket
132+ putMVar serverDead ()
133+
134+ forkIO $ do
135+ localNode <- newLocalNode transport
136+ theirAddr <- readMVar serverAddr
137+ runProcess localNode $ do
138+ monitor theirAddr MaMonitor
139+ send theirAddr "Hi"
140+ liftIO $ putMVar firstSend () >> readMVar serverDead
141+ send theirAddr "Ho"
142+ ProcessMonitorException pid SrNoPing <- expect
143+ True <- return $ pid == theirAddr
144+ return ()
145+ putMVar done ()
146+
147+ takeMVar done
148+
149+-- TODO: test/specify normal process termination
150+
112main :: IO ()
151main :: IO ()
113main = do
152main = do
114 Right transport <- createTransport "127.0.0.1" "8080" defaultTCPParameters
153 Right transport <- createTransport "127.0.0.1" "8080" defaultTCPParameters
115 runTests
154 runTests
116− [ ("Ping", testPing transport)
155+ [ -- ("Ping", testPing transport)
117− , ("Monitor1", testMonitor1 transport)
156+ ("Monitor1", testMonitor1 transport)
118 , ("Monitor2", testMonitor2 transport)
157 , ("Monitor2", testMonitor2 transport)
158+ , ("Monitor3", testMonitor3 transport)
119 ]
159 ]