git.iko.soy/distributed-process
on master help sign in
Merge pull request #85 from simonmar/for-edsko

Various improvements, and fix restarting of master with simplelocalnet

commit331215739dd49b6084fb0b30ee3951c2f67ac1e8 authorEdsko de Vries <edsko@well-typed.com> dateWed 12 Dec 2012, 06:16 (14 years ago) parent28d8a5faafd733
3 files changed+109−36
distributed-process-simplelocalnet/src/Control/Distributed/Process/Backend/SimpleLocalnet.hs+78−32 distributed-process/src/Control/Distributed/Process.hs+6 distributed-process/src/Control/Distributed/Process/Internal/Primitives.hs+25−4
distributed-process-simplelocalnet/src/Control/Distributed/Process/Backend/SimpleLocalnet.hs+78−32
@@ -104,7 +104,7 @@ import Data.Foldable (forM_)
104104import Data.Typeable (Typeable)
105105import Control.Applicative ((<$>))
106106import Control.Exception (throw)
107−import Control.Monad (forever, forM, replicateM)
107+import Control.Monad (forever, forM, replicateM, when, replicateM_)
108108import Control.Monad.IO.Class (liftIO)
109109import Control.Concurrent (forkIO, threadDelay, ThreadId)
110110import Control.Concurrent.MVar (MVar, newMVar, readMVar, modifyMVar_)
@@ -112,26 +112,36 @@ import Control.Distributed.Process
112112 ( RemoteTable
113113 , NodeId
114114 , Process
115+ , ProcessId
115116 , WhereIsReply(..)
116117 , whereis
117118 , whereisRemoteAsync
119+ , registerRemoteAsync
118120 , reregisterRemoteAsync
119121 , getSelfPid
120122 , register
123+ , reregister
121124 , expect
122125 , nsendRemote
123126 , receiveWait
127+ , receiveTimeout
124128 , match
125129 , matchIf
126130 , processNodeId
127131 , monitorNode
132+ , monitor
128133 , unmonitor
129134 , NodeMonitorNotification(..)
135+ , ProcessRegistrationException
130136 , finally
131137 , newChan
132138 , receiveChan
133139 , nsend
134140 , SendPort
141+ , RegisterReply(..)
142+ , bracket
143+ , try
144+ , send
135145 )
136146import qualified Control.Distributed.Process.Node as Node
137147 ( LocalNode
@@ -157,7 +167,7 @@ data Backend = Backend {
157167 , findPeers :: Int -> IO [NodeId]
158168 -- | Make sure that all log messages are printed by the logger on the
159169 -- current node
160− , redirectLogsHere :: Process ()
170+ , redirectLogsHere :: [ProcessId] -> Process ()
161171 }
162172
163173data BackendState = BackendState {
@@ -244,12 +254,27 @@ peerDiscoveryDaemon backendState recv send = forever go
244254--------------------------------------------------------------------------------
245255
246256-- | Make sure that all log messages are printed by the logger on this node
247−apiRedirectLogsHere :: Backend -> Process ()
248−apiRedirectLogsHere backend = do
257+apiRedirectLogsHere :: Backend -> [ProcessId] -> Process ()
258+apiRedirectLogsHere _backend slavecontrollers = do
249259 mLogger <- whereis "logger"
260+ myPid <- getSelfPid
261+
250262 forM_ mLogger $ \logger -> do
251− nids <- liftIO $ findPeers backend 1000000
252− forM_ nids $ \nid -> reregisterRemoteAsync nid "logger" logger -- ignore async response
263+
264+ bracket
265+ (mapM monitor slavecontrollers)
266+ (mapM unmonitor)
267+ $ \_ -> do
268+
269+ -- fire off redirect requests
270+ forM_ slavecontrollers $ \pid -> send pid (RedirectLogsTo logger myPid)
271+
272+ -- Wait for the replies
273+ replicateM_ (length slavecontrollers) $ do
274+ receiveWait
275+ [ match (\(RedirectLogsReply from ok) -> return ())
276+ , match (\m@(NodeMonitorNotification {}) -> return ())
277+ ]
253278
254279--------------------------------------------------------------------------------
255280-- Slaves --
@@ -259,18 +284,31 @@ apiRedirectLogsHere backend = do
259284--
260285-- This datatype is not exposed; instead, we expose primitives for dealing
261286-- with slaves.
262−data SlaveControllerMsg =
263− SlaveTerminate
287+data SlaveControllerMsg
288+ = SlaveTerminate
289+ | RedirectLogsTo ProcessId ProcessId
264290 deriving (Typeable, Show)
265291
266292instance Binary SlaveControllerMsg where
267293 put SlaveTerminate = putWord8 0
294+ put (RedirectLogsTo a b) = do putWord8 1; put (a,b)
268295 get = do
269296 header <- getWord8
270297 case header of
271298 0 -> return SlaveTerminate
299+ 1 -> do (a,b) <- get; return (RedirectLogsTo a b)
272300 _ -> fail "SlaveControllerMsg.get: invalid"
273301
302+data RedirectLogsReply
303+ = RedirectLogsReply ProcessId Bool
304+ deriving (Typeable, Show)
305+
306+instance Binary RedirectLogsReply where
307+ put (RedirectLogsReply from ok) = put (from,ok)
308+ get = do
309+ (from,ok) <- get
310+ return (RedirectLogsReply from ok)
311+
274312-- | Calling 'slave' sets up a new local node and then waits. You start
275313-- processes on the slave by calling 'spawn' from other nodes.
276314--
@@ -292,41 +330,49 @@ slaveController = do
292330 msg <- expect
293331 case msg of
294332 SlaveTerminate -> return ()
333+ RedirectLogsTo loggerPid from -> do
334+ r <- try (reregister "logger" loggerPid)
335+ ok <- case (r :: Either ProcessRegistrationException ()) of
336+ Right _ -> return True
337+ Left _ -> do
338+ r <- try (register "logger" loggerPid)
339+ case (r :: Either ProcessRegistrationException ()) of
340+ Right _ -> return True
341+ Left _ -> return False
342+ pid <- getSelfPid
343+ send from (RedirectLogsReply pid ok)
344+ go
295345
296346-- | Terminate the slave at the given node ID
297347terminateSlave :: NodeId -> Process ()
298348terminateSlave nid = nsendRemote nid "slaveController" SlaveTerminate
299349
300350-- | Find slave nodes
301−findSlaves :: Backend -> Process [NodeId]
351+findSlaves :: Backend -> Process [ProcessId]
302352findSlaves backend = do
303353 nodes <- liftIO $ findPeers backend 1000000
304− -- Fire of asynchronous requests for the slave controller
305− refs <- forM nodes $ \nid -> do
306− whereisRemoteAsync nid "slaveController"
307− ref <- monitorNode nid
308− return (nid, ref)
309− -- Wait for the replies
310− catMaybes <$> replicateM (length nodes) (
311− receiveWait
312− [ matchIf (\(WhereIsReply label _) -> label == "slaveController")
313− (\(WhereIsReply _ mPid) ->
314− case mPid of
315− Nothing ->
316− return Nothing
317− Just pid -> do
318− let nid = processNodeId pid
319− Just ref = lookup nid refs
320− unmonitor ref
321− return (Just nid))
322− , match (\(NodeMonitorNotification {}) -> return Nothing)
323− ])
354+ -- Fire off asynchronous requests for the slave controller
355+
356+ bracket
357+ (mapM monitorNode nodes)
358+ (mapM unmonitor)
359+ $ \_ -> do
360+
361+ -- fire off whereis requests
362+ forM nodes $ \nid -> whereisRemoteAsync nid "slaveController"
363+
364+ -- Wait for the replies
365+ catMaybes <$> replicateM (length nodes) (
366+ receiveWait
367+ [ match (\(WhereIsReply "slaveController" mPid) -> return mPid)
368+ , match (\(NodeMonitorNotification {}) -> return Nothing)
369+ ])
324370
325371-- | Terminate all slaves
326372terminateAllSlaves :: Backend -> Process ()
327373terminateAllSlaves backend = do
328374 slaves <- findSlaves backend
329− forM_ slaves terminateSlave
375+ forM_ slaves $ \pid -> send pid SlaveTerminate
330376 liftIO $ threadDelay 1000000
331377
332378--------------------------------------------------------------------------------
@@ -353,8 +399,8 @@ startMaster backend proc = do
353399 node <- newLocalNode backend
354400 Node.runProcess node $ do
355401 slaves <- findSlaves backend
356− redirectLogsHere backend
357− proc slaves `finally` shutdownLogger
402+ redirectLogsHere backend slaves
403+ proc (map processNodeId slaves) `finally` shutdownLogger
358404
359405--
360406-- | shut down the logger process. This ensures that any pending
distributed-process/src/Control/Distributed/Process.hs+6
@@ -62,6 +62,7 @@ module Control.Distributed.Process
6262 , monitorNode
6363 , monitorPort
6464 , unmonitor
65+ , withMonitor
6566 , MonitorRef
6667 , ProcessLinkException(..)
6768 , NodeLinkException(..)
@@ -92,8 +93,10 @@ module Control.Distributed.Process
9293 , whereisRemoteAsync
9394 , nsendRemote
9495 , WhereIsReply(..)
96+ , RegisterReply(..)
9597 -- * Exception handling
9698 , catch
99+ , try
97100 , mask
98101 , onException
99102 , bracket
@@ -150,6 +153,7 @@ import Control.Distributed.Process.Internal.Types
150153 , ReceivePort(..)
151154 , SendPortId(..)
152155 , WhereIsReply(..)
156+ , RegisterReply(..)
153157 , LocalProcess(processNode)
154158 , nullProcessId
155159 )
@@ -204,6 +208,7 @@ import Control.Distributed.Process.Internal.Primitives
204208 , monitorNode
205209 , monitorPort
206210 , unmonitor
211+ , withMonitor
207212 -- Logging
208213 , say
209214 -- Registry
@@ -222,6 +227,7 @@ import Control.Distributed.Process.Internal.Primitives
222227 , unClosure
223228 -- Exception handling
224229 , catch
230+ , try
225231 , mask
226232 , onException
227233 , bracket
distributed-process/src/Control/Distributed/Process/Internal/Primitives.hs+25−4
@@ -36,6 +36,7 @@ module Control.Distributed.Process.Internal.Primitives
3636 , unlink
3737 , monitor
3838 , unmonitor
39+ , withMonitor
3940 -- * Logging
4041 , say
4142 -- * Registry
@@ -54,6 +55,7 @@ module Control.Distributed.Process.Internal.Primitives
5455 , unStatic
5556 -- * Exception handling
5657 , catch
58+ , try
5759 , mask
5860 , onException
5961 , bracket
@@ -88,7 +90,7 @@ import Control.Monad.Reader (ask)
8890import Control.Monad.IO.Class (MonadIO, liftIO)
8991import Control.Applicative ((<$>))
9092import Control.Exception (Exception, throwIO, SomeException)
91−import qualified Control.Exception as Ex (catch, mask)
93+import qualified Control.Exception as Ex (catch, mask, try)
9294import Control.Distributed.Process.Internal.StrictMVar
9395 ( StrictMVar
9496 , modifyMVar
@@ -436,6 +438,20 @@ link = sendCtrlMsg Nothing . Link . ProcessIdentifier
436438monitor :: ProcessId -> Process MonitorRef
437439monitor = monitor' . ProcessIdentifier
438440
441+-- | Establishes temporary monitoring of another process.
442+--
443+-- @withMonitor pid code@ sets up monitoring of @pid@ for the duration
444+-- of @code@. Note: although monitoring is no longer active when
445+-- @withMonitor@ returns, there might still be unreceived monitor
446+-- messages in the queue.
447+--
448+withMonitor :: ProcessId -> Process a -> Process a
449+withMonitor pid code = bracket (monitor pid) unmonitor (\_ -> code)
450+ -- unmonitor blocks waiting for the response, so there's a possibility
451+ -- that an exception might interrupt withMonitor before the unmonitor
452+ -- has completed. I think that's better than making the unmonitor
453+ -- uninterruptible.
454+
439455-- | Remove a link
440456--
441457-- This is synchronous in the sense that once it returns you are guaranteed
@@ -489,7 +505,13 @@ catch p h = do
489505 lproc <- ask
490506 liftIO $ Ex.catch (runLocalProcess lproc p) (runLocalProcess lproc . h)
491507
492−-- | Lift 'Control.Exception.mask'
508+-- | Lift 'Control.Exception.try'
509+try :: Exception e => Process a -> Process (Either e a)
510+try p = do
511+ lproc <- ask
512+ liftIO $ Ex.try (runLocalProcess lproc p)
513+
514+-- | Lift 'Control.Exception.mask'
493515mask :: ((forall a. Process a -> Process a) -> Process b) -> Process b
494516mask p = do
495517 lproc <- ask
@@ -793,8 +815,7 @@ sendCtrlMsg mNid signal = do
793815 }
794816 case mNid of
795817 Nothing -> do
796− ctrlChan <- localCtrlChan . processNode <$> ask
797− liftIO $ writeChan ctrlChan msg
818+ liftIO $ writeChan (localCtrlChan (processNode proc)) msg
798819 Just nid ->
799820 liftIO $ sendBinary (processNode proc)
800821 (ProcessIdentifier (processId proc))