git.iko.soy/distributed-process
on master help sign in
Support unlink/unmonitor
commit05d2ebf5cca4a616e0c84fe5d05628e9b4bf5f9f authorEdsko de Vries <edsko@edsko.net> dateThu 14 Jun 2012, 17:16 (14 years ago) parent119c411
5 files changed+243−151
distributed-process/src/Control/Distributed/Process.hs+34−22 distributed-process/src/Control/Distributed/Process/Internal.hs+50−15 distributed-process/src/Control/Distributed/Process/Internal/NodeController.hs+71−29 distributed-process/tests/TestCH.hs+86−84 network-transport/src/Network/Transport.hs+2−1
distributed-process/src/Control/Distributed/Process.hs+34−22
@@ -64,12 +64,16 @@ module Control.Distributed.Process
6464 , send
6565 , getSelfPid
6666 -- * Monitoring and linking
67+ , link
68+ , unlink
6769 , monitor
68− , MonitorNotification(..)
69− , MonitorRef
70+ , unmonitor
7071 , LinkException(..)
72+ , MonitorRef -- opaque
73+ , MonitorNotification(..)
7174 , DiedReason(..)
72− , link
75+ , DidUnmonitor(..)
76+ , DidUnlink(..)
7377 -- * Initialization
7478 , newLocalNode
7579 , forkProcess
@@ -140,9 +144,11 @@ import Control.Distributed.Process.Internal ( NodeId(..)
140144 , LocalProcess(..)
141145 , LocalProcessState(..)
142146 , Message(..)
143− , MonitorRef
147+ , MonitorRef(..)
144148 , MonitorNotification(..)
145149 , LinkException(..)
150+ , DidUnmonitor(..)
151+ , DidUnlink(..)
146152 , DiedReason(..)
147153 , NCMsg(..)
148154 , ProcessSignal(..)
@@ -190,20 +196,21 @@ getSelfPid = processId <$> ask
190196-- | Monitor another process
191197monitor :: ProcessId -> Process MonitorRef
192198monitor them = do
193− us <- getSelfPid
194− monitorRef <- getMonitorRef
195− postCtrlMsg NCMsg { ctrlMsgSender = Left us
196− , ctrlMsgSignal = Monitor them monitorRef
197− }
199+ monitorRef <- getMonitorRefFor them
200+ postCtrlMsg $ Monitor them monitorRef
198201 return monitorRef
199202
200203-- | Link to a remote process
201204link :: ProcessId -> Process ()
202−link them = do
203− us <- getSelfPid
204− postCtrlMsg NCMsg { ctrlMsgSender = Left us
205− , ctrlMsgSignal = Link them
206− }
205+link = postCtrlMsg . Link
206+
207+-- | Remove a monitor
208+unmonitor :: MonitorRef -> Process ()
209+unmonitor = postCtrlMsg . Unmonitor
210+
211+-- | Remove a link
212+unlink :: ProcessId -> Process ()
213+unlink = postCtrlMsg . Unlink
207214
208215--------------------------------------------------------------------------------
209216-- Auxiliary API --
@@ -319,8 +326,9 @@ handleIncomingMessages node = go [] Map.empty Set.empty
319326 let msg = payloadToMessage payload
320327 enqueue (processQueue proc) msg
321328 go uninitConns procConns ctrlConns
322− Nothing | cid `Set.member` ctrlConns ->
329+ Nothing | cid `Set.member` ctrlConns -> do
323330 writeChan ctrlChan (decode . BSL.fromChunks $ payload)
331+ go uninitConns procConns ctrlConns
324332 Nothing | cid `elem` uninitConns ->
325333 case payloadToId payload of
326334 Just lpid -> do
@@ -425,12 +433,13 @@ remoteProcessFailed node them = do
425433 , ctrlMsgSignal = Died nid DiedDisconnect
426434 }
427435
428−getMonitorRef :: Process MonitorRef
429−getMonitorRef = do
436+getMonitorRefFor :: ProcessId -> Process MonitorRef
437+getMonitorRefFor pid = do
430438 node <- processNode <$> ask
431− liftIO $ modifyMVar (localState node) $ \st ->
439+ liftIO $ modifyMVar (localState node) $ \st -> do
440+ let counter = st ^. localMonitorCounter
432441 return ( localMonitorCounter ^: (+ 1) $ st
433− , st ^. localMonitorCounter
442+ , MonitorRef pid counter
434443 )
435444
436445-- This is most definitely NOT exported
@@ -438,7 +447,10 @@ runLocalProcess :: LocalProcess -> Process a -> IO a
438447runLocalProcess lproc proc = runReaderT (unProcess proc) lproc
439448
440449-- | Post a control message on the local node controller
441−postCtrlMsg :: NCMsg -> Process ()
442−postCtrlMsg msg = do
450+postCtrlMsg :: ProcessSignal -> Process ()
451+postCtrlMsg signal = do
452+ us <- getSelfPid
443453 ctrlChan <- localCtrlChan . processNode <$> ask
444− liftIO $ writeChan ctrlChan msg
454+ liftIO $ writeChan ctrlChan NCMsg { ctrlMsgSender = Left us
455+ , ctrlMsgSignal = signal
456+ }
distributed-process/src/Control/Distributed/Process/Internal.hs+50−15
@@ -11,10 +11,12 @@ module Control.Distributed.Process.Internal
1111 -- * Messages
1212 , Message(..)
1313 -- * Monitoring and linking
14− , MonitorRef
14+ , MonitorRef(..)
1515 , MonitorNotification(..)
1616 , LinkException(..)
1717 , DiedReason(..)
18+ , DidUnmonitor(..)
19+ , DidUnlink(..)
1820 -- * Node controller data types
1921 , Identifier
2022 , NCMsg(..)
@@ -62,7 +64,10 @@ import Control.Distributed.Process.Internal.CQueue (CQueue)
6264
6365-- We identify node IDs and endpoint IDs
6466newtype NodeId = NodeId { nodeAddress :: NT.EndPointAddress }
65− deriving (Show, Eq, Ord, Binary)
67+ deriving (Eq, Ord, Binary)
68+
69+instance Show NodeId where
70+ show = show . nodeAddress
6671
6772-- | A local process ID consists of a seed which distinguishes processes from
6873-- different instances of the same local node and a counter
@@ -70,7 +75,10 @@ data LocalProcessId = LocalProcessId
7075 { lpidUnique :: Int32
7176 , lpidCounter :: Int32
7277 }
73− deriving (Eq, Ord, Typeable, Show)
78+ deriving (Eq, Ord, Typeable)
79+
80+instance Show LocalProcessId where
81+ show = show . lpidCounter
7482
7583-- | A process ID combines a local process with with an endpoint address
7684-- (in other words, we identify nodes and endpoints)
@@ -78,7 +86,10 @@ data ProcessId = ProcessId
7886 { processNodeId :: NodeId
7987 , processLocalId :: LocalProcessId
8088 }
81− deriving (Eq, Ord, Typeable, Show)
89+ deriving (Eq, Ord, Typeable)
90+
91+instance Show ProcessId where
92+ show pid = show (processNodeId pid) ++ ":" ++ show (processLocalId pid)
8293
8394-- | Local nodes
8495data LocalNode = LocalNode
@@ -116,8 +127,12 @@ data Message = Message
116127 , messageEncoding :: BSL.ByteString
117128 }
118129
119−-- | MonitorRef should be an opaque type
120−type MonitorRef = Int32
130+-- | MonitorRef is opaque for regular Cloud Haskell processes
131+data MonitorRef = MonitorRef
132+ { monitorRefPid :: ProcessId
133+ , monitorRefCounter :: Int32
134+ }
135+ deriving (Eq, Ord, Show)
121136
122137-- | Messages sent by monitors
123138data MonitorNotification = MonitorNotification MonitorRef ProcessId DiedReason
@@ -129,6 +144,14 @@ data LinkException = LinkException ProcessId DiedReason
129144
130145instance Exception LinkException
131146
147+-- | (Asynchronous) reply from unmonitor
148+newtype DidUnmonitor = DidUnmonitor MonitorRef
149+ deriving (Typeable, Binary)
150+
151+-- | (Asynchronous) reply from unlink
152+newtype DidUnlink = DidUnlink ProcessId
153+ deriving (Typeable, Binary)
154+
132155-- | Why did a process die?
133156data DiedReason =
134157 DiedNormal
@@ -136,7 +159,7 @@ data DiedReason =
136159 | DiedDisconnect
137160 | DiedNodeDown
138161 | DiedNoProc
139− deriving Show
162+ deriving (Show, Eq)
140163
141164-- | Node or process identifier
142165type Identifier = Either ProcessId NodeId
@@ -146,12 +169,16 @@ data NCMsg = NCMsg
146169 { ctrlMsgSender :: Identifier
147170 , ctrlMsgSignal :: ProcessSignal
148171 }
172+ deriving Show
149173
150174-- | Signals to the node controller (see 'NCMsg')
151175data ProcessSignal =
152− Monitor ProcessId MonitorRef
153− | Link ProcessId
176+ Link ProcessId
177+ | Unlink ProcessId
178+ | Monitor ProcessId MonitorRef
179+ | Unmonitor MonitorRef
154180 | Died Identifier DiedReason
181+ deriving Show
155182
156183--------------------------------------------------------------------------------
157184-- Serialization/deserialization --
@@ -211,16 +238,24 @@ instance Binary NCMsg where
211238 put msg = put (ctrlMsgSender msg) >> put (ctrlMsgSignal msg)
212239 get = NCMsg <$> get <*> get
213240
241+instance Binary MonitorRef where
242+ put ref = put (monitorRefPid ref) >> put (monitorRefCounter ref)
243+ get = MonitorRef <$> get <*> get
244+
214245instance Binary ProcessSignal where
215− put (Monitor pid ref) = putWord8 0 >> put pid >> put ref
216− put (Link pid) = putWord8 1 >> put pid
217− put (Died who reason) = putWord8 2 >> put who >> put reason
246+ put (Link pid) = putWord8 0 >> put pid
247+ put (Unlink pid) = putWord8 1 >> put pid
248+ put (Monitor pid ref) = putWord8 2 >> put pid >> put ref
249+ put (Unmonitor ref) = putWord8 3 >> put ref
250+ put (Died who reason) = putWord8 4 >> put who >> put reason
218251 get = do
219252 header <- getWord8
220253 case header of
221− 0 -> Monitor <$> get <*> get
222− 1 -> Link <$> get
223− 2 -> Died <$> get <*> get
254+ 0 -> Link <$> get
255+ 1 -> Unlink <$> get
256+ 2 -> Monitor <$> get <*> get
257+ 3 -> Unmonitor <$> get
258+ 4 -> Died <$> get <*> get
224259 _ -> fail "ProcessSignal.get: invalid"
225260
226261instance Binary DiedReason where
distributed-process/src/Control/Distributed/Process/Internal/NodeController.hs+71−29
@@ -6,7 +6,7 @@ module Control.Distributed.Process.Internal.NodeController
66import Data.Map (Map)
77import qualified Data.Map as Map (empty, toList)
88import Data.Set (Set)
9−import qualified Data.Set as Set (empty, insert)
9+import qualified Data.Set as Set (empty, insert, delete)
1010import qualified Data.ByteString as BSS (ByteString)
1111import qualified Data.ByteString.Lazy as BSL (toChunks)
1212import Data.Binary (encode)
@@ -21,7 +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)
24+import Control.Exception (Exception, throwTo)
2525import qualified Network.Transport as NT ( EndPoint
2626 , Connection
2727 , Reliability(ReliableOrdered)
@@ -29,15 +29,18 @@ import qualified Network.Transport as NT ( EndPoint
2929 , send
3030 , connect
3131 )
32+import Control.Distributed.Process.Serializable (Serializable)
3233import Control.Distributed.Process.Internal.CQueue (enqueue)
3334import Control.Distributed.Process.Internal ( NodeId(..)
3435 , LocalProcessId(..)
3536 , ProcessId(..)
3637 , LocalNode(..)
3738 , LocalProcess(..)
38− , MonitorRef
39+ , MonitorRef(..)
3940 , MonitorNotification(..)
4041 , LinkException(..)
42+ , DidUnmonitor(..)
43+ , DidUnlink(..)
4144 , DiedReason(..)
4245 , Identifier
4346 , NCMsg(..)
@@ -89,18 +92,27 @@ nodeController = do
8992 _ -> return ()
9093
9194 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?"
9695 NCMsg (Left from) (Link them) ->
9796 ncEffectMonitor from them Nothing
98− NCMsg (Right _) (Link _) ->
99− error "Link message from a node?"
97+ NCMsg (Left from) (Monitor them ref) ->
98+ ncEffectMonitor from them (Just ref)
99+ NCMsg (Left from) (Unlink them) ->
100+ ncEffectUnlink from them
101+ NCMsg (Left from) (Unmonitor ref) ->
102+ ncEffectUnmonitor from ref
100103 NCMsg _from (Died (Right nid) reason) ->
101104 ncEffectNodeDied nid reason
102105 NCMsg _from (Died (Left pid) reason) ->
103106 ncEffectProcessDied pid reason
107+ -- Error cases
108+ NCMsg (Right _) (Link _) ->
109+ error "Link message from a node?"
110+ NCMsg (Right _) (Monitor _ _) ->
111+ error "Monitor message from a node?"
112+ NCMsg (Right _) (Unlink _) ->
113+ error "Unlink message from a node?"
114+ NCMsg (Right _) (Unmonitor _) ->
115+ error "Unmonitor message from a node?"
104116
105117-- [Unified: Table 10]
106118ncEffectMonitor :: ProcessId -- ^ Who's watching?
@@ -110,12 +122,11 @@ ncEffectMonitor :: ProcessId -- ^ Who's watching?
110122ncEffectMonitor from them mRef = do
111123 node <- ask
112124 shouldLink <-
113− if processNodeId them /= localNodeId node
125+ if not (isLocal node them)
114126 then return True
115127 else liftIO . withMVar (localState node) $
116128 return . isJust . (^. localProcessWithId (processLocalId them))
117− let localFrom = processNodeId from == localNodeId node
118− case (shouldLink, localFrom) of
129+ case (shouldLink, isLocal node from) of
119130 (True, _) -> -- [Unified: first rule]
120131 case mRef of
121132 Just ref -> modify $ monitorsFor them from ^: Set.insert ref
@@ -128,6 +139,20 @@ ncEffectMonitor from them mRef = do
128139 , ctrlMsgSignal = Died (Left them) DiedNoProc
129140 }
130141
142+-- [Unified: Table 11]
143+ncEffectUnlink :: ProcessId -> ProcessId -> NC ()
144+ncEffectUnlink from them = do
145+ node <- ask
146+ when (isLocal node from) $ postMessage from $ DidUnlink them
147+ modify $ linksForProcess them ^: Set.delete from
148+
149+-- [Unified: Table 11]
150+ncEffectUnmonitor :: ProcessId -> MonitorRef -> NC ()
151+ncEffectUnmonitor from ref = do
152+ node <- ask
153+ when (isLocal node from) $ postMessage from $ DidUnmonitor ref
154+ modify $ monitorsFor (monitorRefPid ref) from ^: Set.delete ref
155+
131156-- [Unified: Table 12, bottom rule]
132157ncEffectNodeDied :: NodeId -> DiedReason -> NC ()
133158ncEffectNodeDied nid reason = do
@@ -139,13 +164,13 @@ ncEffectNodeDied nid reason = do
139164 forM_ (Map.toList lnks) $ \(them, uss) -> do
140165 let pid = ProcessId nid them
141166 forM_ uss $ \us ->
142− when (processNodeId us == localNodeId node) $
167+ when (isLocal node us) $
143168 notifyDied us pid reason Nothing
144169
145170 forM_ (Map.toList mons) $ \(them, uss) -> do
146171 let pid = ProcessId nid them
147172 forM_ (Map.toList uss) $ \(us, refs) ->
148− when (processNodeId us == localNodeId node) $
173+ when (isLocal node us) $
149174 forM_ refs $ notifyDied us pid reason . Just
150175
151176 modify $ (linksForNode nid ^= Map.empty)
@@ -171,6 +196,10 @@ ncEffectProcessDied pid reason = do
171196-- Connections --
172197--------------------------------------------------------------------------------
173198
199+-- | Check if a process is local to our own node
200+isLocal :: LocalNode -> ProcessId -> Bool
201+isLocal nid pid = processNodeId pid == localNodeId nid
202+
174203notifyDied :: ProcessId -- ^ Who to notify?
175204 -> ProcessId -- ^ Who died?
176205 -> DiedReason -- ^ How did they die?
@@ -178,25 +207,36 @@ notifyDied :: ProcessId -- ^ Who to notify?
178207 -> NC ()
179208notifyDied dest src reason mRef = do
180209 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
210+ case (isLocal node dest, mRef) of
211+ (True, Just ref) ->
212+ postMessage dest $ MonitorNotification ref src reason
213+ (True, Nothing) ->
214+ throwException dest $ LinkException src reason
215+ (False, _) ->
194216 -- TODO: why the change in sender? How does that affect 'reconnect' semantics?
195217 -- (see [Unified: Table 10]
196218 sendCtrlMsg (processNodeId dest) NCMsg
197219 { ctrlMsgSender = Right (localNodeId node)
198220 , ctrlMsgSignal = Died (Left src) reason
199221 }
222+
223+postMessage :: Serializable a => ProcessId -> a -> NC ()
224+postMessage pid msg = withLocalProc pid $ \p ->
225+ enqueue (processQueue p) (createMessage msg)
226+
227+throwException :: Exception e => ProcessId -> e -> NC ()
228+throwException pid e = withLocalProc pid $ \p ->
229+ throwTo (processThread p) e
230+
231+withLocalProc :: ProcessId -> (LocalProcess -> IO ()) -> NC ()
232+withLocalProc pid p = do
233+ node <- ask
234+ liftIO $ do
235+ -- By [Unified: table 6, rule missing_process] messages to dead processes
236+ -- can silently be dropped
237+ let lpid = processLocalId pid
238+ mProc <- withMVar (localState node) $ return . (^. localProcessWithId lpid)
239+ forM_ mProc p
200240
201241sendTo :: Identifier -> [BSS.ByteString] -> NC ()
202242sendTo them payload = do
@@ -259,8 +299,10 @@ createConnTo them = do
259299--------------------------------------------------------------------------------
260300
261301destNid :: ProcessSignal -> Maybe NodeId
262−destNid (Monitor pid _) = Just $ processNodeId pid
263−destNid (Link pid) = Just $ processNodeId pid
302+destNid (Link pid) = Just . processNodeId $ pid
303+destNid (Unlink pid) = Just . processNodeId $ pid
304+destNid (Monitor pid _) = Just . processNodeId $ pid
305+destNid (Unmonitor ref) = Just . processNodeId . monitorRefPid $ ref
264306destNid (Died (Right _) _) = Nothing
265307destNid (Died (Left _pid) _) = fail "destNid: TODO"
266308
distributed-process/tests/TestCH.hs+86−84
@@ -3,8 +3,10 @@ module Main where
33import Prelude hiding (catch)
44import Data.Binary (Binary)
55import Data.Typeable (Typeable)
6+import Data.Foldable (forM_)
67import Control.Concurrent (forkIO, threadDelay)
7−import Control.Concurrent.MVar ( newEmptyMVar
8+import Control.Concurrent.MVar ( MVar
9+ , newEmptyMVar
810 , putMVar
911 , takeMVar
1012 , readMVar
@@ -64,13 +66,53 @@ testPing transport = do
6466 takeMVar clientDone
6567
6668-- | 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
69+monitorOrLink :: Bool -- ^ 'True' for monitor, 'False' for link
70+ -> ProcessId -- Process to monitor/link to
71+ -> Maybe (MVar ()) -- MVar to signal on once the monitor has been set up
72+ -> Process (Maybe MonitorRef)
73+monitorOrLink mOrL pid mSignal = do
74+ result <- if mOrL then Just <$> monitor pid
75+ else link pid >> return Nothing
76+ -- Monitor is asynchronous, which usually does not matter but if we want a
77+ -- *specific* signal then it does. Therefore we wait an arbitrary delay and
78+ -- hope that this means the monitor has been set up
79+ forM_ mSignal $ \signal -> liftIO . forkIO $ threadDelay 100000 >> putMVar signal ()
80+ return result
81+
82+monitorTestProcess :: ProcessId -- Process to monitor/link to
83+ -> Bool -- 'True' for monitor, 'False' for link
84+ -> Bool -- Should we unmonitor?
85+ -> DiedReason -- Expected cause of death
86+ -> Maybe (MVar ()) -- Signal for 'monitor set up'
87+ -> MVar () -- Signal for successful termination
88+ -> Process ()
89+monitorTestProcess theirAddr mOrL un reason monitorSetup done =
90+ catch (do mRef <- monitorOrLink mOrL theirAddr monitorSetup
91+ case (un, mRef) of
92+ (True, Nothing) -> do
93+ unlink theirAddr
94+ DidUnlink pid <- expect
95+ True <- return $ pid == theirAddr
96+ liftIO $ putMVar done ()
97+ (True, Just ref) -> do
98+ unmonitor ref
99+ DidUnmonitor ref' <- expect
100+ True <- return $ ref == ref'
101+ liftIO $ putMVar done ()
102+ (False, ref) -> do
103+ MonitorNotification ref' pid reason' <- expect
104+ True <- return $ Just ref' == ref && pid == theirAddr && mOrL && reason == reason'
105+ liftIO $ putMVar done ()
106+ )
107+ (\(LinkException pid reason') -> do
108+ True <- return $ pid == theirAddr && not mOrL && not un && reason == reason'
109+ liftIO $ putMVar done ()
110+ )
111+
70112
71113-- | Monitor an unreachable node
72−testMonitorUnreachable :: NT.Transport -> Bool -> IO ()
73−testMonitorUnreachable transport mOrL = do
114+testMonitorUnreachable :: NT.Transport -> Bool -> Bool -> IO ()
115+testMonitorUnreachable transport mOrL un = do
74116 deadProcess <- newEmptyMVar
75117 done <- newEmptyMVar
76118
@@ -84,19 +126,13 @@ testMonitorUnreachable transport mOrL = do
84126 localNode <- newLocalNode transport
85127 theirAddr <- readMVar deadProcess
86128 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 ())
129+ monitorTestProcess theirAddr mOrL un DiedDisconnect Nothing done
94130
95131 takeMVar done
96132
97133-- | Monitor a process which terminates normally
98−testMonitorNormalTermination :: NT.Transport -> Bool -> IO ()
99−testMonitorNormalTermination transport mOrL = do
134+testMonitorNormalTermination :: NT.Transport -> Bool -> Bool -> IO ()
135+testMonitorNormalTermination transport mOrL un = do
100136 monitorSetup <- newEmptyMVar
101137 monitoredProcess <- newEmptyMVar
102138 done <- newEmptyMVar
@@ -111,25 +147,13 @@ testMonitorNormalTermination transport mOrL = do
111147 localNode <- newLocalNode transport
112148 theirAddr <- readMVar monitoredProcess
113149 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 ())
150+ monitorTestProcess theirAddr mOrL un DiedNormal (Just monitorSetup) done
127151
128152 takeMVar done
129153
130154-- | Monitor a process which terminates abnormally
131−testMonitorAbnormalTermination :: NT.Transport -> Bool -> IO ()
132−testMonitorAbnormalTermination transport mOrL = do
155+testMonitorAbnormalTermination :: NT.Transport -> Bool -> Bool -> IO ()
156+testMonitorAbnormalTermination transport mOrL un = do
133157 monitorSetup <- newEmptyMVar
134158 monitoredProcess <- newEmptyMVar
135159 done <- newEmptyMVar
@@ -147,25 +171,13 @@ testMonitorAbnormalTermination transport mOrL = do
147171 localNode <- newLocalNode transport
148172 theirAddr <- readMVar monitoredProcess
149173 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 ())
174+ monitorTestProcess theirAddr mOrL un (DiedException (show err)) (Just monitorSetup) done
163175
164176 takeMVar done
165177
166178-- | Monitor a local process that is already dead
167−testMonitorLocalDeadProcess :: NT.Transport -> Bool -> IO ()
168−testMonitorLocalDeadProcess transport mOrL = do
179+testMonitorLocalDeadProcess :: NT.Transport -> Bool -> Bool -> IO ()
180+testMonitorLocalDeadProcess transport mOrL un = do
169181 processDead <- newEmptyMVar
170182 processAddr <- newEmptyMVar
171183 localNode <- newLocalNode transport
@@ -179,19 +191,13 @@ testMonitorLocalDeadProcess transport mOrL = do
179191 theirAddr <- readMVar processAddr
180192 readMVar processDead
181193 runProcess localNode $ do
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 ())
194+ monitorTestProcess theirAddr mOrL un DiedNoProc Nothing done
189195
190196 takeMVar done
191197
192198-- | Monitor a remote process that is already dead
193−testMonitorRemoteDeadProcess :: NT.Transport -> Bool -> IO ()
194−testMonitorRemoteDeadProcess transport mOrL = do
199+testMonitorRemoteDeadProcess :: NT.Transport -> Bool -> Bool -> IO ()
200+testMonitorRemoteDeadProcess transport mOrL un = do
195201 processDead <- newEmptyMVar
196202 processAddr <- newEmptyMVar
197203 done <- newEmptyMVar
@@ -206,19 +212,13 @@ testMonitorRemoteDeadProcess transport mOrL = do
206212 theirAddr <- readMVar processAddr
207213 readMVar processDead
208214 runProcess localNode $ do
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 ())
215+ monitorTestProcess theirAddr mOrL un DiedNoProc Nothing done
216216
217217 takeMVar done
218218
219219-- | Monitor a process that becomes disconnected
220−testMonitorDisconnect :: NT.Transport -> Bool -> IO ()
221−testMonitorDisconnect transport mOrL = do
220+testMonitorDisconnect :: NT.Transport -> Bool -> Bool -> IO ()
221+testMonitorDisconnect transport mOrL un = do
222222 processAddr <- newEmptyMVar
223223 monitorSetup <- newEmptyMVar
224224 done <- newEmptyMVar
@@ -234,14 +234,7 @@ testMonitorDisconnect transport mOrL = do
234234 localNode <- newLocalNode transport
235235 theirAddr <- readMVar processAddr
236236 runProcess localNode $ do
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 ())
237+ monitorTestProcess theirAddr mOrL un DiedDisconnect (Just monitorSetup) done
245238
246239 takeMVar done
247240
@@ -352,19 +345,28 @@ main :: IO ()
352345main = do
353346 Right transport <- createTransport "127.0.0.1" "8080" defaultTCPParameters
354347 runTests
355− [ --("Ping", testPing 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)
348+ [ ("Ping", testPing transport)
349+ -- The "missing" combinations in the list below don't make much sense, as
350+ -- we cannot guarantee that the monitor reply or link exception will not
351+ -- happen before the unmonitor or unlink
352+ , ("MonitorUnreachable", testMonitorUnreachable transport True False)
353+ , ("MonitorNormalTermination", testMonitorNormalTermination transport True False)
354+ , ("MonitorAbnormalTermination", testMonitorAbnormalTermination transport True False)
355+ , ("MonitorLocalDeadProcess", testMonitorLocalDeadProcess transport True False)
356+ , ("MonitorRemoteDeadProcess", testMonitorRemoteDeadProcess transport True False)
357+ , ("MonitorDisconnect", testMonitorDisconnect transport True False)
358+ , ("LinkUnreachable", testMonitorUnreachable transport False False)
359+ , ("LinkNormalTermination", testMonitorNormalTermination transport False False)
360+ , ("LinkAbnormalTermination", testMonitorAbnormalTermination transport False False)
361+ , ("LinkLocalDeadProcess", testMonitorLocalDeadProcess transport False False)
362+ , ("LinkRemoteDeadProcess", testMonitorRemoteDeadProcess transport False False)
363+ , ("LinkDisconnect", testMonitorDisconnect transport False False)
364+ , ("UnmonitorNormalTermination", testMonitorNormalTermination transport True True)
365+ , ("UnmonitorAbnormalTermination", testMonitorAbnormalTermination transport True True)
366+ , ("UnmonitorDisconnect", testMonitorDisconnect transport True True)
367+ , ("UnlinkNormalTermination", testMonitorNormalTermination transport False True)
368+ , ("UnlinkAbnormalTermination", testMonitorAbnormalTermination transport False True)
369+ , ("UnlinkDisconnect", testMonitorDisconnect transport False True)
368370 {-
369371 , ("Monitor2", testMonitor2 transport)
370372 , ("Monitor3", testMonitor3 transport)
network-transport/src/Network/Transport.hs+2−1
@@ -23,6 +23,7 @@ module Network.Transport ( -- * Types
2323 ) where
2424
2525import Data.ByteString (ByteString)
26+import qualified Data.ByteString.Char8 as BSC (unpack)
2627import Control.Exception (Exception)
2728import Data.Typeable (Typeable)
2829import Data.Binary (Binary)
@@ -113,7 +114,7 @@ newtype EndPointAddress = EndPointAddress { endPointAddressToByteString :: ByteS
113114 deriving (Eq, Ord, Typeable, Binary)
114115
115116instance Show EndPointAddress where
116− show = show . endPointAddressToByteString
117+ show = BSC.unpack . endPointAddressToByteString
117118
118119-- | EndPointAddress of a multicast group.
119120newtype MulticastAddress = MulticastAddress { multicastAddressToByteString :: ByteString }