git.iko.soy/distributed-process
on master help sign in
Support linking
commit119c4113e795ea9f7113524ad26476b42c6933ce authorEdsko de Vries <edsko@edsko.net> dateThu 14 Jun 2012, 11:50 (14 years ago) parent24262d0
4 files changed+245−166
distributed-process/src/Control/Distributed/Process.hs+59−41 distributed-process/src/Control/Distributed/Process/Internal.hs+25−12 distributed-process/src/Control/Distributed/Process/Internal/NodeController.hs+72−47 distributed-process/tests/TestCH.hs+89−66
distributed-process/src/Control/Distributed/Process.hs+59−41
@@ -65,19 +65,23 @@ module Control.Distributed.Process
6565 , getSelfPid
6666 -- * Monitoring and linking
6767 , monitor
68− , MonitorReply(..)
68+ , MonitorNotification(..)
69+ , MonitorRef
70+ , LinkException(..)
6971 , DiedReason(..)
72+ , link
7073 -- * Initialization
7174 , newLocalNode
7275 , forkProcess
7376 , runProcess
7477 -- * Auxiliary API
7578 , closeLocalNode
76− , pcatch
79+ , catch
7780 , expectTimeout
7881 ) where
7982
8083import Prelude hiding (catch)
84+import System.IO (fixIO)
8185import qualified Data.ByteString as BSS (ByteString)
8286import qualified Data.ByteString.Lazy as BSL (fromChunks)
8387import Data.Binary (decode)
@@ -91,7 +95,8 @@ import Control.Monad (void)
9195import Control.Monad.Reader (MonadReader(..), ReaderT, runReaderT)
9296import Control.Monad.IO.Class (MonadIO, liftIO)
9397import Control.Applicative ((<$>))
94−import Control.Exception (Exception, throwIO, catch, SomeException)
98+import Control.Exception (Exception, throwIO, SomeException)
99+import qualified Control.Exception as Exception (catch)
95100import Control.Concurrent (forkIO)
96101import Control.Concurrent.MVar ( newMVar
97102 , withMVar
@@ -136,7 +141,8 @@ import Control.Distributed.Process.Internal ( NodeId(..)
136141 , LocalProcessState(..)
137142 , Message(..)
138143 , MonitorRef
139− , MonitorReply(..)
144+ , MonitorNotification(..)
145+ , LinkException(..)
140146 , DiedReason(..)
141147 , NCMsg(..)
142148 , ProcessSignal(..)
@@ -186,13 +192,19 @@ monitor :: ProcessId -> Process MonitorRef
186192monitor them = do
187193 us <- getSelfPid
188194 monitorRef <- getMonitorRef
189− ourCtrlChan <- localCtrlChan . processNode <$> ask
190− liftIO . writeChan ourCtrlChan $ NCMsg
191− { ctrlMsgSender = Left us
192− , ctrlMsgSignal = Monitor them monitorRef
193− }
195+ postCtrlMsg NCMsg { ctrlMsgSender = Left us
196+ , ctrlMsgSignal = Monitor them monitorRef
197+ }
194198 return monitorRef
195199
200+-- | Link to a remote process
201+link :: ProcessId -> Process ()
202+link them = do
203+ us <- getSelfPid
204+ postCtrlMsg NCMsg { ctrlMsgSender = Left us
205+ , ctrlMsgSignal = Link them
206+ }
207+
196208--------------------------------------------------------------------------------
197209-- Auxiliary API --
198210--------------------------------------------------------------------------------
@@ -205,11 +217,10 @@ closeLocalNode node =
205217 NT.closeEndPoint (localEndPoint node)
206218
207219-- Catch exceptions within a process
208−-- TODO: should this be called simply 'catch'?
209−pcatch :: Exception e => Process a -> (e -> Process a) -> Process a
210−pcatch p h = do
220+catch :: Exception e => Process a -> (e -> Process a) -> Process a
221+catch p h = do
211222 run <- runLocalProcess <$> ask
212− liftIO $ catch (run p) (run . h)
223+ liftIO $ Exception.catch (run p) (run . h)
213224
214225expectTimeout :: forall a. Serializable a => Int -> Process (Maybe a)
215226expectTimeout timeout = do
@@ -253,33 +264,27 @@ runProcess node proc = do
253264 takeMVar done
254265
255266forkProcess :: LocalNode -> Process () -> IO ProcessId
256−forkProcess node proc = do
257− queue <- newCQueue
258− lproc <- modifyMVar (localState node) $ \st -> do
259− let lpid = LocalProcessId { lpidCounter = st ^. localPidCounter
260− , lpidUnique = st ^. localPidUnique
261− }
262− let pid = ProcessId { processNodeId = localNodeId node
263− , processLocalId = lpid
264− }
265− pst <- newMVar LocalProcessState { _connections = Map.empty
266− }
267− let lproc = LocalProcess { processQueue = queue
268− , processNode = node
269− , processId = pid
270− , processState = pst
271− }
272− -- TODO: if the counter overflows we should pick a new unique
273− return ( (localProcessWithId lpid ^= Just lproc)
274− . (localPidCounter ^: (+ 1))
275− $ st
276− , lproc
277− )
278− void . forkIO $ do
279− let pid = processId lproc
280− lpid = processLocalId pid
281− reason <- catch (runLocalProcess lproc proc >> return DiedNormal)
282− (return . DiedException . (show :: SomeException -> String))
267+forkProcess node proc = modifyMVar (localState node) $ \st -> do
268+ let lpid = LocalProcessId { lpidCounter = st ^. localPidCounter
269+ , lpidUnique = st ^. localPidUnique
270+ }
271+ let pid = ProcessId { processNodeId = localNodeId node
272+ , processLocalId = lpid
273+ }
274+ pst <- newMVar LocalProcessState { _connections = Map.empty
275+ }
276+ queue <- newCQueue
277+ (_, lproc) <- fixIO $ \ ~(tid, _) -> do
278+ let lproc = LocalProcess { processQueue = queue
279+ , processNode = node
280+ , processId = pid
281+ , processState = pst
282+ , processThread = tid
283+ }
284+ tid' <- forkIO $ do
285+ reason <- Exception.catch
286+ (runLocalProcess lproc proc >> return DiedNormal)
287+ (return . DiedException . (show :: SomeException -> String))
283288 -- [Unified: Table 4, rules termination and exiting]
284289 modifyMVar_ (localState node) $
285290 return . (localProcessWithId lpid ^= Nothing)
@@ -287,7 +292,14 @@ forkProcess node proc = do
287292 { ctrlMsgSender = Left pid
288293 , ctrlMsgSignal = Died (Left pid) reason
289294 }
290− return (processId lproc)
295+ return (tid', lproc)
296+
297+ -- TODO: if the counter overflows we should pick a new unique
298+ return ( (localProcessWithId lpid ^= Just lproc)
299+ . (localPidCounter ^: (+ 1))
300+ $ st
301+ , pid
302+ )
291303
292304handleIncomingMessages :: LocalNode -> IO ()
293305handleIncomingMessages node = go [] Map.empty Set.empty
@@ -424,3 +436,9 @@ getMonitorRef = do
424436-- This is most definitely NOT exported
425437runLocalProcess :: LocalProcess -> Process a -> IO a
426438runLocalProcess lproc proc = runReaderT (unProcess proc) lproc
439+
440+-- | Post a control message on the local node controller
441+postCtrlMsg :: NCMsg -> Process ()
442+postCtrlMsg msg = do
443+ ctrlChan <- localCtrlChan . processNode <$> ask
444+ liftIO $ writeChan ctrlChan msg
distributed-process/src/Control/Distributed/Process/Internal.hs+25−12
@@ -10,9 +10,10 @@ module Control.Distributed.Process.Internal
1010 , LocalProcessState(..)
1111 -- * Messages
1212 , Message(..)
13− -- * Monitoring
13+ -- * Monitoring and linking
1414 , MonitorRef
15− , MonitorReply(..)
15+ , MonitorNotification(..)
16+ , LinkException(..)
1617 , DiedReason(..)
1718 -- * Node controller data types
1819 , Identifier
@@ -34,9 +35,11 @@ module Control.Distributed.Process.Internal
3435 , connectionTo
3536 ) where
3637
38+import Control.Concurrent (ThreadId)
3739import Control.Concurrent.MVar (MVar)
3840import Control.Concurrent.Chan (Chan)
3941import Control.Category ((>>>))
42+import Control.Exception (Exception)
4043import Data.Map (Map)
4144import Data.Int (Int32)
4245import Data.Typeable (Typeable)
@@ -95,10 +98,11 @@ data LocalNodeState = LocalNodeState
9598
9699-- | Processes running on our local node
97100data LocalProcess = LocalProcess
98− { processQueue :: CQueue Message
99− , processNode :: LocalNode
100− , processId :: ProcessId
101− , processState :: MVar LocalProcessState
101+ { processQueue :: CQueue Message
102+ , processNode :: LocalNode
103+ , processId :: ProcessId
104+ , processState :: MVar LocalProcessState
105+ , processThread :: ThreadId
102106 }
103107
104108-- | Local process state
@@ -116,9 +120,15 @@ data Message = Message
116120type MonitorRef = Int32
117121
118122-- | Messages sent by monitors
119−data MonitorReply = ProcessDied MonitorRef ProcessId DiedReason
123+data MonitorNotification = MonitorNotification MonitorRef ProcessId DiedReason
120124 deriving (Typeable)
121125
126+-- | Exceptions thrown when a linked process dies
127+data LinkException = LinkException ProcessId DiedReason
128+ deriving (Typeable, Show)
129+
130+instance Exception LinkException
131+
122132-- | Why did a process die?
123133data DiedReason =
124134 DiedNormal
@@ -140,6 +150,7 @@ data NCMsg = NCMsg
140150-- | Signals to the node controller (see 'NCMsg')
141151data ProcessSignal =
142152 Monitor ProcessId MonitorRef
153+ | Link ProcessId
143154 | Died Identifier DiedReason
144155
145156--------------------------------------------------------------------------------
@@ -192,9 +203,9 @@ instance Binary ProcessId where
192203 put pid = put (processNodeId pid) >> put (processLocalId pid)
193204 get = ProcessId <$> get <*> get
194205
195−instance Binary MonitorReply where
196− put (ProcessDied ref pid reason) = put ref >> put pid >> put reason
197− get = ProcessDied <$> get <*> get <*> get
206+instance Binary MonitorNotification where
207+ put (MonitorNotification ref pid reason) = put ref >> put pid >> put reason
208+ get = MonitorNotification <$> get <*> get <*> get
198209
199210instance Binary NCMsg where
200211 put msg = put (ctrlMsgSender msg) >> put (ctrlMsgSignal msg)
@@ -202,12 +213,14 @@ instance Binary NCMsg where
202213
203214instance Binary ProcessSignal where
204215 put (Monitor pid ref) = putWord8 0 >> put pid >> put ref
205− put (Died who reason) = putWord8 1 >> put who >> put reason
216+ put (Link pid) = putWord8 1 >> put pid
217+ put (Died who reason) = putWord8 2 >> put who >> put reason
206218 get = do
207219 header <- getWord8
208220 case header of
209221 0 -> Monitor <$> get <*> get
210− 1 -> Died <$> get <*> get
222+ 1 -> Link <$> get
223+ 2 -> Died <$> get <*> get
211224 _ -> fail "ProcessSignal.get: invalid"
212225
213226instance Binary DiedReason where
distributed-process/src/Control/Distributed/Process/Internal/NodeController.hs+72−47
@@ -21,6 +21,7 @@ import Control.Monad.State (MonadState, StateT, evalStateT, gets, modify)
2121import Control.Monad.Reader (MonadReader(..), ReaderT, runReaderT)
2222import Control.Concurrent.MVar (withMVar)
2323import Control.Concurrent.Chan (readChan, writeChan)
24+import Control.Exception (throwTo)
2425import qualified Network.Transport as NT ( EndPoint
2526 , Connection
2627 , Reliability(ReliableOrdered)
@@ -34,9 +35,9 @@ import Control.Distributed.Process.Internal ( NodeId(..)
3435 , ProcessId(..)
3536 , LocalNode(..)
3637 , LocalProcess(..)
37− , Message(..)
3838 , MonitorRef
39− , MonitorReply(..)
39+ , MonitorNotification(..)
40+ , LinkException(..)
4041 , DiedReason(..)
4142 , Identifier
4243 , NCMsg(..)
@@ -87,13 +88,26 @@ nodeController = do
8788 Just nid' | nid' /= localNodeId node -> sendCtrlMsg nid' msg
8889 _ -> return ()
8990
90− ncEffect msg
91−
92−-- [Unified: ncEffect]
93−ncEffect :: NCMsg -> NC ()
91+ case msg of
92+ NCMsg (Left from) (Monitor them ref) ->
93+ ncEffectMonitor from them (Just ref)
94+ NCMsg (Right _) (Monitor _ _) ->
95+ error "Monitor message from a node?"
96+ NCMsg (Left from) (Link them) ->
97+ ncEffectMonitor from them Nothing
98+ NCMsg (Right _) (Link _) ->
99+ error "Link message from a node?"
100+ NCMsg _from (Died (Right nid) reason) ->
101+ ncEffectNodeDied nid reason
102+ NCMsg _from (Died (Left pid) reason) ->
103+ ncEffectProcessDied pid reason
94104
95105-- [Unified: Table 10]
96−ncEffect (NCMsg (Left from) (Monitor them ref)) = do
106+ncEffectMonitor :: ProcessId -- ^ Who's watching?
107+ -> ProcessId -- ^ Who's being watched?
108+ -> Maybe MonitorRef -- ^ 'Nothing' to link
109+ -> NC ()
110+ncEffectMonitor from them mRef = do
97111 node <- ask
98112 shouldLink <-
99113 if processNodeId them /= localNodeId node
@@ -103,60 +117,52 @@ ncEffect (NCMsg (Left from) (Monitor them ref)) = do
103117 let localFrom = processNodeId from == localNodeId node
104118 case (shouldLink, localFrom) of
105119 (True, _) -> -- [Unified: first rule]
106− modify $ monitorsFor them from ^: Set.insert ref
120+ case mRef of
121+ Just ref -> modify $ monitorsFor them from ^: Set.insert ref
122+ Nothing -> modify $ linksForProcess them ^: Set.insert from
107123 (False, True) -> -- [Unified: second rule]
108− sendLocal (processLocalId from) . createMessage $
109− ProcessDied ref them DiedNoProc
124+ notifyDied from them DiedNoProc mRef
110125 (False, False) -> -- [Unified: third rule]
111126 sendCtrlMsg (processNodeId from) NCMsg
112127 { ctrlMsgSender = Right (localNodeId node)
113128 , ctrlMsgSignal = Died (Left them) DiedNoProc
114129 }
115−
116−ncEffect (NCMsg (Right _) (Monitor _ _)) =
117− error "Monitor message from a node?"
118130
119−-- [Unified: Table 12, bottom rule]
120−ncEffect (NCMsg _from (Died (Right nid) reason)) = do
131+-- [Unified: Table 12, bottom rule]
132+ncEffectNodeDied :: NodeId -> DiedReason -> NC ()
133+ncEffectNodeDied nid reason = do
121134 node <- ask
122135 lnks <- gets (^. linksForNode nid)
123136 mons <- gets (^. monitorsForNode nid)
124137
125− forM_ (Map.toList lnks) $ \(_them, _uss) ->
126− fail "ncEffect: linking not implemented"
138+ -- We only need to notify local processes
139+ forM_ (Map.toList lnks) $ \(them, uss) -> do
140+ let pid = ProcessId nid them
141+ forM_ uss $ \us ->
142+ when (processNodeId us == localNodeId node) $
143+ notifyDied us pid reason Nothing
127144
128− forM_ (Map.toList mons) $ \(them, uss) ->
145+ forM_ (Map.toList mons) $ \(them, uss) -> do
146+ let pid = ProcessId nid them
129147 forM_ (Map.toList uss) $ \(us, refs) ->
130− -- We only need to notify local processes
131− when (processNodeId us == localNodeId node) $ do
132− let lpid = processLocalId us
133− rpid = ProcessId nid them
134− forM_ refs $ \ref ->
135− sendLocal lpid . createMessage $ ProcessDied ref rpid reason
148+ when (processNodeId us == localNodeId node) $
149+ forM_ refs $ notifyDied us pid reason . Just
136150
137151 modify $ (linksForNode nid ^= Map.empty)
138152 . (monitorsForNode nid ^= Map.empty)
139153
140154-- [Unified: Table 12, top rule]
141−ncEffect (NCMsg _from (Died (Left pid) reason)) = do
142− node <- ask
155+ncEffectProcessDied :: ProcessId -> DiedReason -> NC ()
156+ncEffectProcessDied pid reason = do
143157 lnks <- gets (^. linksForProcess pid)
144158 mons <- gets (^. monitorsForProcess pid)
145159
146− forM_ lnks $ \_us ->
147− fail "ncEffect: linking not implemented"
160+ forM_ lnks $ \us ->
161+ notifyDied us pid reason Nothing
148162
149163 forM_ (Map.toList mons) $ \(us, refs) ->
150− forM_ refs $ \ref -> do
151− let msg = createMessage $ ProcessDied ref pid reason
152− if processNodeId us == localNodeId node
153− then
154− sendLocal (processLocalId us) msg
155− else
156− sendCtrlMsg (processNodeId us) NCMsg
157− { ctrlMsgSender = Right (localNodeId node) -- TODO: why the change in sender? How does that affect 'reconnect' semantics?
158− , ctrlMsgSignal = Died (Left pid) reason
159− }
164+ forM_ refs $
165+ notifyDied us pid reason . Just
160166
161167 modify $ (linksForProcess pid ^= Set.empty)
162168 . (monitorsForProcess pid ^= Map.empty)
@@ -165,6 +171,33 @@ ncEffect (NCMsg _from (Died (Left pid) reason)) = do
165171-- Connections --
166172--------------------------------------------------------------------------------
167173
174+notifyDied :: ProcessId -- ^ Who to notify?
175+ -> ProcessId -- ^ Who died?
176+ -> DiedReason -- ^ How did they die?
177+ -> Maybe MonitorRef -- ^ 'Nothing' for linking
178+ -> NC ()
179+notifyDied dest src reason mRef = do
180+ node <- ask
181+ if processNodeId dest == localNodeId node
182+ then liftIO $ do
183+ let lpid = processLocalId dest
184+ mProc <- withMVar (localState node) $ return . (^. localProcessWithId lpid)
185+ -- By [Unified: table 6, rule missing_process] messages to dead processes
186+ -- can silently be dropped
187+ forM_ mProc $ \proc -> case mRef of
188+ Just ref -> do
189+ let msg = createMessage $ MonitorNotification ref src reason
190+ enqueue (processQueue proc) msg
191+ Nothing ->
192+ throwTo (processThread proc) $ LinkException src reason
193+ else
194+ -- TODO: why the change in sender? How does that affect 'reconnect' semantics?
195+ -- (see [Unified: Table 10]
196+ sendCtrlMsg (processNodeId dest) NCMsg
197+ { ctrlMsgSender = Right (localNodeId node)
198+ , ctrlMsgSignal = Died (Left src) reason
199+ }
200+
168201sendTo :: Identifier -> [BSS.ByteString] -> NC ()
169202sendTo them payload = do
170203 mConn <- connTo them
@@ -187,15 +220,6 @@ sendTo them payload = do
187220sendCtrlMsg :: NodeId -> NCMsg -> NC ()
188221sendCtrlMsg dest = sendTo (Right dest) . BSL.toChunks . encode
189222
190−sendLocal :: LocalProcessId -> Message -> NC ()
191−sendLocal lpid msg = do
192− node <- ask
193− liftIO $ do
194− mProc <- withMVar (localState node) $ return . (^. localProcessWithId lpid)
195− -- By [Unified: table 6, rule missing_process] messages to dead processes
196− -- can silently be dropped
197− forM_ mProc $ \proc -> enqueue (processQueue proc) msg
198−
199223connTo :: Identifier -> NC (Maybe NT.Connection)
200224connTo them = do
201225 mConn <- gets (^. connectionTo them)
@@ -236,6 +260,7 @@ createConnTo them = do
236260
237261destNid :: ProcessSignal -> Maybe NodeId
238262destNid (Monitor pid _) = Just $ processNodeId pid
263+destNid (Link pid) = Just $ processNodeId pid
239264destNid (Died (Right _) _) = Nothing
240265destNid (Died (Left _pid) _) = fail "destNid: TODO"
241266
distributed-process/tests/TestCH.hs+89−66
@@ -12,6 +12,7 @@ import Control.Concurrent.MVar ( newEmptyMVar
1212import Control.Monad (replicateM_)
1313import Control.Monad.IO.Class (liftIO)
1414import Control.Exception (throwIO)
15+import Control.Applicative ((<$>))
1516import qualified Network.Transport as NT (Transport, closeEndPoint)
1617import Network.Transport.TCP (createTransport, defaultTCPParameters)
1718import Control.Distributed.Process
@@ -62,10 +63,14 @@ testPing transport = do
6263
6364 takeMVar clientDone
6465
66+-- | Monitor or link to a remote node
67+monitorOrLink :: Bool -> ProcessId -> Process (Maybe MonitorRef)
68+monitorOrLink True pid = Just <$> monitor pid
69+monitorOrLink False pid = link pid >> return Nothing
6570
6671-- | Monitor an unreachable node
67−testMonitorUnreachable :: NT.Transport -> IO ()
68−testMonitorUnreachable transport = do
72+testMonitorUnreachable :: NT.Transport -> Bool -> IO ()
73+testMonitorUnreachable transport mOrL = do
6974 deadProcess <- newEmptyMVar
7075 done <- newEmptyMVar
7176
@@ -78,18 +83,20 @@ testMonitorUnreachable transport = do
7883 forkIO $ do
7984 localNode <- newLocalNode transport
8085 theirAddr <- readMVar deadProcess
81− runProcess localNode $ do
82− ref <- monitor theirAddr
83− ProcessDied ref' pid DiedDisconnect <- expect
84− True <- return $ ref' == ref && pid == theirAddr
85− return ()
86− putMVar done ()
87−
86+ runProcess localNode $
87+ catch (do ref <- monitorOrLink mOrL theirAddr
88+ MonitorNotification ref' pid DiedDisconnect <- expect
89+ True <- return $ Just ref' == ref && pid == theirAddr && mOrL
90+ liftIO $ putMVar done ())
91+ (\(LinkException pid DiedDisconnect) -> do
92+ True <- return $ pid == theirAddr && not mOrL
93+ liftIO $ putMVar done ())
94+
8895 takeMVar done
8996
9097-- | Monitor a process which terminates normally
91−testMonitorNormalTermination :: NT.Transport -> IO ()
92−testMonitorNormalTermination transport = do
98+testMonitorNormalTermination :: NT.Transport -> Bool -> IO ()
99+testMonitorNormalTermination transport mOrL = do
93100 monitorSetup <- newEmptyMVar
94101 monitoredProcess <- newEmptyMVar
95102 done <- newEmptyMVar
@@ -103,24 +110,26 @@ testMonitorNormalTermination transport = do
103110 forkIO $ do
104111 localNode <- newLocalNode transport
105112 theirAddr <- readMVar monitoredProcess
106− runProcess localNode $ do
107− ref <- monitor theirAddr
108− liftIO $ do
109− -- Monitor is asynchronous, but we want to make sure the monitor has
110− -- been fully created before allowing the remote process to terminate,
111− -- otherwise we might get a different signal here
112− threadDelay 100000
113− putMVar monitorSetup ()
114− ProcessDied ref' pid DiedNormal <- expect
115− True <- return $ ref' == ref && pid == theirAddr
116− return ()
117− putMVar done ()
113+ runProcess localNode $
114+ catch (do ref <- monitorOrLink mOrL theirAddr
115+ liftIO $ do
116+ -- Monitor is asynchronous, but we want to make sure the monitor has
117+ -- been fully created before allowing the remote process to terminate,
118+ -- otherwise we might get a different signal here
119+ threadDelay 100000
120+ putMVar monitorSetup ()
121+ MonitorNotification ref' pid DiedNormal <- expect
122+ True <- return $ Just ref' == ref && pid == theirAddr && mOrL
123+ liftIO $ putMVar done ())
124+ (\(LinkException pid DiedNormal) -> do
125+ True <- return $ pid == theirAddr && not mOrL
126+ liftIO $ putMVar done ())
118127
119128 takeMVar done
120129
121130-- | Monitor a process which terminates abnormally
122−testMonitorAbnormalTermination :: NT.Transport -> IO ()
123−testMonitorAbnormalTermination transport = do
131+testMonitorAbnormalTermination :: NT.Transport -> Bool -> IO ()
132+testMonitorAbnormalTermination transport mOrL = do
124133 monitorSetup <- newEmptyMVar
125134 monitoredProcess <- newEmptyMVar
126135 done <- newEmptyMVar
@@ -137,24 +146,26 @@ testMonitorAbnormalTermination transport = do
137146 forkIO $ do
138147 localNode <- newLocalNode transport
139148 theirAddr <- readMVar monitoredProcess
140− runProcess localNode $ do
141− ref <- monitor theirAddr
142− liftIO $ do
143− -- Monitor is asynchronous, but we want to make sure the monitor has
144− -- been fully created before allowing the remote process to terminate,
145− -- otherwise we might get a different signal here
146− threadDelay 100000
147− putMVar monitorSetup ()
148− ProcessDied ref' pid (DiedException err') <- expect
149− True <- return $ ref' == ref && pid == theirAddr && err' == show err
150− return ()
151− putMVar done ()
149+ runProcess localNode $
150+ catch (do ref <- monitorOrLink mOrL theirAddr
151+ liftIO $ do
152+ -- Monitor is asynchronous, but we want to make sure the monitor has
153+ -- been fully created before allowing the remote process to terminate,
154+ -- otherwise we might get a different signal here
155+ threadDelay 100000
156+ putMVar monitorSetup ()
157+ MonitorNotification ref' pid (DiedException err') <- expect
158+ True <- return $ Just ref' == ref && pid == theirAddr && err' == show err && mOrL
159+ liftIO $ putMVar done ())
160+ (\(LinkException pid (DiedException err')) -> do
161+ True <- return $ pid == theirAddr && err' == show err && not mOrL
162+ liftIO $ putMVar done ())
152163
153164 takeMVar done
154165
155166-- | Monitor a local process that is already dead
156−testMonitorLocalDeadProcess :: NT.Transport -> IO ()
157−testMonitorLocalDeadProcess transport = do
167+testMonitorLocalDeadProcess :: NT.Transport -> Bool -> IO ()
168+testMonitorLocalDeadProcess transport mOrL = do
158169 processDead <- newEmptyMVar
159170 processAddr <- newEmptyMVar
160171 localNode <- newLocalNode transport
@@ -168,17 +179,19 @@ testMonitorLocalDeadProcess transport = do
168179 theirAddr <- readMVar processAddr
169180 readMVar processDead
170181 runProcess localNode $ do
171− ref <- monitor theirAddr
172− ProcessDied ref' pid DiedNoProc <- expect
173− True <- return $ ref' == ref && pid == theirAddr
174− return ()
175− putMVar done ()
182+ catch (do ref <- monitorOrLink mOrL theirAddr
183+ MonitorNotification ref' pid DiedNoProc <- expect
184+ True <- return $ Just ref' == ref && pid == theirAddr && mOrL
185+ liftIO $ putMVar done ())
186+ (\(LinkException pid DiedNoProc) -> do
187+ True <- return $ pid == theirAddr && not mOrL
188+ liftIO $ putMVar done ())
176189
177190 takeMVar done
178191
179192-- | Monitor a remote process that is already dead
180−testMonitorRemoteDeadProcess :: NT.Transport -> IO ()
181−testMonitorRemoteDeadProcess transport = do
193+testMonitorRemoteDeadProcess :: NT.Transport -> Bool -> IO ()
194+testMonitorRemoteDeadProcess transport mOrL = do
182195 processDead <- newEmptyMVar
183196 processAddr <- newEmptyMVar
184197 done <- newEmptyMVar
@@ -193,17 +206,19 @@ testMonitorRemoteDeadProcess transport = do
193206 theirAddr <- readMVar processAddr
194207 readMVar processDead
195208 runProcess localNode $ do
196− ref <- monitor theirAddr
197− ProcessDied ref' pid DiedNoProc <- expect
198− True <- return $ ref' == ref && pid == theirAddr
199− return ()
200− putMVar done ()
209+ catch (do ref <- monitorOrLink mOrL theirAddr
210+ MonitorNotification ref' pid DiedNoProc <- expect
211+ True <- return $ Just ref' == ref && pid == theirAddr && mOrL
212+ liftIO $ putMVar done ())
213+ (\(LinkException pid DiedNoProc) -> do
214+ True <- return $ pid == theirAddr && not mOrL
215+ liftIO $ putMVar done ())
201216
202217 takeMVar done
203218
204219-- | Monitor a process that becomes disconnected
205−testMonitorDisconnect :: NT.Transport -> IO ()
206−testMonitorDisconnect transport = do
220+testMonitorDisconnect :: NT.Transport -> Bool -> IO ()
221+testMonitorDisconnect transport mOrL = do
207222 processAddr <- newEmptyMVar
208223 monitorSetup <- newEmptyMVar
209224 done <- newEmptyMVar
@@ -219,13 +234,15 @@ testMonitorDisconnect transport = do
219234 localNode <- newLocalNode transport
220235 theirAddr <- readMVar processAddr
221236 runProcess localNode $ do
222− ref <- monitor theirAddr
223− liftIO $ threadDelay 100000 >> putMVar monitorSetup ()
224− ProcessDied ref' pid DiedDisconnect <- expect
225− True <- return $ ref' == ref && pid == theirAddr
226− return ()
227− putMVar done ()
228−
237+ catch (do ref <- monitorOrLink mOrL theirAddr
238+ liftIO $ threadDelay 100000 >> putMVar monitorSetup ()
239+ MonitorNotification ref' pid DiedDisconnect <- expect
240+ True <- return $ Just ref' == ref && pid == theirAddr && mOrL
241+ liftIO $ putMVar done ())
242+ (\(LinkException pid DiedDisconnect) -> do
243+ True <- return $ pid == theirAddr && not mOrL
244+ liftIO $ putMVar done ())
245+
229246 takeMVar done
230247
231248
@@ -336,12 +353,18 @@ main = do
336353 Right transport <- createTransport "127.0.0.1" "8080" defaultTCPParameters
337354 runTests
338355 [ --("Ping", testPing transport)
339− ("MonitorUnreachable", testMonitorUnreachable transport)
340− , ("MonitorNormalTermination", testMonitorNormalTermination transport)
341− , ("MonitorAbnormalTermination", testMonitorAbnormalTermination transport)
342− , ("MonitorLocalDeadProcess", testMonitorLocalDeadProcess transport)
343− , ("MonitorRemoteDeadProcess", testMonitorRemoteDeadProcess transport)
344− , ("MonitorDisconnect", testMonitorDisconnect transport)
356+ ("MonitorUnreachable", testMonitorUnreachable transport True)
357+ , ("MonitorNormalTermination", testMonitorNormalTermination transport True)
358+ , ("MonitorAbnormalTermination", testMonitorAbnormalTermination transport True)
359+ , ("MonitorLocalDeadProcess", testMonitorLocalDeadProcess transport True)
360+ , ("MonitorRemoteDeadProcess", testMonitorRemoteDeadProcess transport True)
361+ , ("MonitorDisconnect", testMonitorDisconnect transport True)
362+ , ("LinkUnreachable", testMonitorUnreachable transport False)
363+ , ("LinkNormalTermination", testMonitorNormalTermination transport False)
364+ , ("LinkAbnormalTermination", testMonitorAbnormalTermination transport False)
365+ , ("LinkLocalDeadProcess", testMonitorLocalDeadProcess transport False)
366+ , ("LinkRemoteDeadProcess", testMonitorRemoteDeadProcess transport False)
367+ , ("LinkDisconnect", testMonitorDisconnect transport False)
345368 {-
346369 , ("Monitor2", testMonitor2 transport)
347370 , ("Monitor3", testMonitor3 transport)