@@ -14,8 +14,8 @@ import Prelude hiding (catch)
16import System.IO (fixIO, hPutStrLn, stderr)
16import System.IO (fixIO, hPutStrLn, stderr)
17−import qualified Data.ByteString.Lazy as BSL (fromChunks)
17+import qualified Data.NotByteString.Lazy as BSL (fromChunks)
18−import Data.Binary (decode)
18+import Data.NotByteString.Lazy.Binary (decode)
19import Data.Map (Map)
19import Data.Map (Map)
20import qualified Data.Map as Map
20import qualified Data.Map as Map
@@ -25,16 +25,16 @@ import qualified Data.Map as Map
26 , partitionWithKey
26 , partitionWithKey
27 , filterWithKey
27 , filterWithKey
29−import qualified Data.List as List (delete, (\\))
30import Data.Set (Set)
30import Data.Set (Set)
31−import qualified Data.Set as Set (empty, insert, delete, member, (\\), fromList)
31+import qualified Data.Set as Set (empty, insert, delete, member, filter)
32import Data.Foldable (forM_)
32import Data.Foldable (forM_)
33import Data.Maybe (isJust)
33import Data.Maybe (isJust)
34import Data.Typeable (Typeable)
34import Data.Typeable (Typeable)
35import Control.Category ((>>>))
35import Control.Category ((>>>))
36import Control.Applicative ((<$>))
36import Control.Applicative ((<$>))
37−import Control.Monad (void, when, forever)
37+import Control.Monad (void, when)
38import Control.Monad.IO.Class (MonadIO, liftIO)
38import Control.Monad.IO.Class (MonadIO, liftIO)
39import Control.Monad.State.Strict (MonadState, StateT, evalStateT, gets)
39import Control.Monad.State.Strict (MonadState, StateT, evalStateT, gets)
40import qualified Control.Monad.State.Strict as StateT (get, put)
40import qualified Control.Monad.State.Strict as StateT (get, put)
@@ -65,6 +65,8 @@ import qualified Network.Transport as NT
66 , closeEndPoint
66 , closeEndPoint
67 , ConnectionId
67 , ConnectionId
69import Data.Accessor (Accessor, accessor, (^.), (^=), (^:))
71import Data.Accessor (Accessor, accessor, (^.), (^=), (^:))
70import qualified Data.Accessor.Container as DAC (mapDefault, mapMaybe)
72import qualified Data.Accessor.Container as DAC (mapDefault, mapMaybe)
@@ -89,6 +91,7 @@ import Control.Distributed.Process.Internal.Types
89 , localPidCounter
91 , localPidCounter
90 , localPidUnique
92 , localPidUnique
91 , localProcessWithId
93 , localProcessWithId
92 , MonitorRef(..)
95 , MonitorRef(..)
93 , ProcessMonitorNotification(..)
96 , ProcessMonitorNotification(..)
94 , NodeMonitorNotification(..)
97 , NodeMonitorNotification(..)
@@ -119,6 +122,7 @@ import Control.Distributed.Process.Internal.Node
119 ( sendBinary
122 ( sendBinary
120 , sendMessage
123 , sendMessage
121 , sendPayload
124 , sendPayload
123import Control.Distributed.Process.Internal.Primitives (expect, register, finally)
127import Control.Distributed.Process.Internal.Primitives (expect, register, finally)
124import qualified Control.Distributed.Process.Internal.Closure.BuiltIn as BuiltIn (remoteTable)
128import qualified Control.Distributed.Process.Internal.Closure.BuiltIn as BuiltIn (remoteTable)
@@ -162,11 +166,17 @@ createBareLocalNode endPoint rtable = do
162 void . forkIO $ handleIncomingMessages node
166 void . forkIO $ handleIncomingMessages node
163 return node
167 return node
169+-- Like 'Control.Monad.forever' but sans space leak
170+{-# INLINE forever' #-}
171+forever' :: Monad m => m a -> m b
172+-- forever' a = let a' = a >> a' in a'
173+forever' a = a >> forever' a
165-- | Start and register the service processes on a node
175-- | Start and register the service processes on a node
166-- (for now, this is only the logger)
176-- (for now, this is only the logger)
167startServiceProcesses :: LocalNode -> IO ()
177startServiceProcesses :: LocalNode -> IO ()
168startServiceProcesses node = do
178startServiceProcesses node = do
169− logger <- forkProcess node . forever $ do
179+ logger <- forkProcess node . forever' $ do
170 (time, pid, string) <- expect :: Process (String, ProcessId, String)
180 (time, pid, string) <- expect :: Process (String, ProcessId, String)
171 liftIO . hPutStrLn stderr $ time ++ " " ++ show pid ++ ": " ++ string
181 liftIO . hPutStrLn stderr $ time ++ " " ++ show pid ++ ": " ++ string
172 runProcess node $ register "logger" logger
182 runProcess node $ register "logger" logger
@@ -212,8 +222,13 @@ forkProcess node proc = modifyMVar (localState node) $ \st -> do
212 (runLocalProcess lproc proc >> return DiedNormal)
222 (runLocalProcess lproc proc >> return DiedNormal)
213 (return . DiedException . (show :: SomeException -> String))
223 (return . DiedException . (show :: SomeException -> String))
214 -- [Unified: Table 4, rules termination and exiting]
224 -- [Unified: Table 4, rules termination and exiting]
215− modifyMVar_ (localState node) $
225+ modifyMVar_ (localState node) $ \st -> do
226+ let pid' = ProcessIdentifier pid
227+ let (affected, unaffected) = Map.partitionWithKey (\(fr, _to) !_v -> impliesDeathOf pid' fr) (st ^. localConnections)
228+ mapM_ NT.close (Map.elems affected)
216− return . (localProcessWithId lpid ^= Nothing)
229+ return $ (localProcessWithId lpid ^= Nothing)
230+ . (localConnections ^= unaffected)
217 writeChan (localCtrlChan node) NCMsg
232 writeChan (localCtrlChan node) NCMsg
218 { ctrlMsgSender = ProcessIdentifier pid
233 { ctrlMsgSender = ProcessIdentifier pid
219 , ctrlMsgSignal = Died (ProcessIdentifier pid) reason
234 , ctrlMsgSignal = Died (ProcessIdentifier pid) reason
@@ -237,25 +252,25 @@ forkProcess node proc = modifyMVar (localState node) $ \st -> do
239handleIncomingMessages :: LocalNode -> IO ()
254handleIncomingMessages :: LocalNode -> IO ()
240−handleIncomingMessages node = go [] Map.empty Map.empty Set.empty
255+handleIncomingMessages node = go Set.empty Map.empty Map.empty Set.empty
242− go :: [NT.ConnectionId] -- ^ Connections whose purpose we don't yet know
257+ go :: Set NT.ConnectionId -- ^ Connections whose purpose we don't yet know
243 -> Map NT.ConnectionId LocalProcess -- ^ Connections to local processes
258 -> Map NT.ConnectionId LocalProcess -- ^ Connections to local processes
244 -> Map NT.ConnectionId TypedChannel -- ^ Connections to typed channels
259 -> Map NT.ConnectionId TypedChannel -- ^ Connections to typed channels
245 -> Set NT.ConnectionId -- ^ Connections to our controller
260 -> Set NT.ConnectionId -- ^ Connections to our controller
246 -> IO ()
261 -> IO ()
247− go uninitConns procs chans ctrls = do
262+ go !uninitConns !procs !chans !ctrls = do
248 event <- NT.receive endpoint
263 event <- NT.receive endpoint
249 case event of
264 case event of
250 NT.ConnectionOpened cid _rel _theirAddr ->
265 NT.ConnectionOpened cid _rel _theirAddr ->
251 -- TODO: Check if _rel is ReliableOrdered, and if not, treat as
266 -- TODO: Check if _rel is ReliableOrdered, and if not, treat as
252 -- (**) below.
267 -- (**) below.
253− go (cid : uninitConns) procs chans ctrls
268+ go (Set.insert cid uninitConns) procs chans ctrls
254 NT.Received cid payload ->
269 NT.Received cid payload ->
255 case ( Map.lookup cid procs
270 case ( Map.lookup cid procs
256 , Map.lookup cid chans
271 , Map.lookup cid chans
257 , cid `Set.member` ctrls
272 , cid `Set.member` ctrls
258− , cid `elem` uninitConns
273+ , cid `Set.member` uninitConns
260 (Just proc, _, _, _) -> do
275 (Just proc, _, _, _) -> do
261 let msg = payloadToMessage payload
276 let msg = payloadToMessage payload
@@ -275,7 +290,7 @@ handleIncomingMessages node = go [] Map.empty Map.empty Set.empty
275 mProc <- withMVar state $ return . (^. localProcessWithId lpid)
290 mProc <- withMVar state $ return . (^. localProcessWithId lpid)
276 case mProc of
291 case mProc of
277 Just proc ->
292 Just proc ->
278− go (List.delete cid uninitConns)
293+ go (Set.delete cid uninitConns)
279 (Map.insert cid proc procs)
294 (Map.insert cid proc procs)
@@ -287,7 +302,7 @@ handleIncomingMessages node = go [] Map.empty Map.empty Set.empty
287 -- remote node as having died, and we should close
302 -- remote node as having died, and we should close
288 -- incoming connections (this requires a Transport layer
303 -- incoming connections (this requires a Transport layer
289 -- extension). (**)
304 -- extension). (**)
290− go (List.delete cid uninitConns) procs chans ctrls
305+ go (Set.delete cid uninitConns) procs chans ctrls
291 SendPortIdentifier chId -> do
306 SendPortIdentifier chId -> do
292 let lcid = sendPortLocalId chId
307 let lcid = sendPortLocalId chId
293 lpid = processLocalId (sendPortProcessId chId)
308 lpid = processLocalId (sendPortProcessId chId)
@@ -297,20 +312,20 @@ handleIncomingMessages node = go [] Map.empty Map.empty Set.empty
297 mChannel <- withMVar (processState proc) $ return . (^. typedChannelWithId lcid)
312 mChannel <- withMVar (processState proc) $ return . (^. typedChannelWithId lcid)
298 case mChannel of
313 case mChannel of
299 Just channel ->
314 Just channel ->
300− go (List.delete cid uninitConns)
315+ go (Set.delete cid uninitConns)
302 (Map.insert cid channel chans)
317 (Map.insert cid channel chans)
304 Nothing ->
319 Nothing ->
305 -- Unknown typed channel
320 -- Unknown typed channel
306 -- TODO (**) above
321 -- TODO (**) above
307− go (List.delete cid uninitConns) procs chans ctrls
322+ go (Set.delete cid uninitConns) procs chans ctrls
308 Nothing ->
323 Nothing ->
309 -- Unknown process
324 -- Unknown process
310 -- TODO (**) above
325 -- TODO (**) above
311− go (List.delete cid uninitConns) procs chans ctrls
326+ go (Set.delete cid uninitConns) procs chans ctrls
312 NodeIdentifier _ ->
327 NodeIdentifier _ ->
313− go (List.delete cid uninitConns)
328+ go (Set.delete cid uninitConns)
316 (Set.insert cid ctrls)
331 (Set.insert cid ctrls)
@@ -319,7 +334,7 @@ handleIncomingMessages node = go [] Map.empty Map.empty Set.empty
319 -- TODO (**) above
334 -- TODO (**) above
320 go uninitConns procs chans ctrls
335 go uninitConns procs chans ctrls
321 NT.ConnectionClosed cid ->
336 NT.ConnectionClosed cid ->
322− go (List.delete cid uninitConns)
337+ go (Set.delete cid uninitConns)
323 (Map.delete cid procs)
338 (Map.delete cid procs)
324 (Map.delete cid chans)
339 (Map.delete cid chans)
325 (Set.delete cid ctrls)
340 (Set.delete cid ctrls)
@@ -330,10 +345,11 @@ handleIncomingMessages node = go [] Map.empty Map.empty Set.empty
330 { ctrlMsgSender = nid
345 { ctrlMsgSender = nid
331 , ctrlMsgSignal = Died nid DiedDisconnect
346 , ctrlMsgSignal = Died nid DiedDisconnect
348+ let notRemoved k = k `notElem` cids
333− go (uninitConns List.\\ cids)
349+ go (Set.filter notRemoved uninitConns)
334− (Map.filterWithKey (\k _ -> k `notElem` cids) procs)
350+ (Map.filterWithKey (const . notRemoved) procs)
335− (Map.filterWithKey (\k _ -> k `notElem` cids) chans)
351+ (Map.filterWithKey (const . notRemoved) chans)
336− (ctrls Set.\\ Set.fromList cids)
352+ (Set.filter notRemoved ctrls)
337 NT.ErrorEvent (NT.TransportError (NT.EventConnectionLost Nothing _) _) ->
353 NT.ErrorEvent (NT.TransportError (NT.EventConnectionLost Nothing _) _) ->
338 -- TODO: We should treat an asymetrical connection loss (incoming
354 -- TODO: We should treat an asymetrical connection loss (incoming
339 -- connection broken, but outgoing connection still potentially ok)
355 -- connection broken, but outgoing connection still potentially ok)
@@ -392,7 +408,7 @@ initNCState = NCState { _links = Map.empty
392nodeController :: NC ()
408nodeController :: NC ()
393nodeController = do
409nodeController = do
394 node <- ask
410 node <- ask
395− forever $ do
411+ forever' $ do
396 msg <- liftIO $ readChan (localCtrlChan node)
412 msg <- liftIO $ readChan (localCtrlChan node)
398 -- [Unified: Table 7, rule nc_forward]
414 -- [Unified: Table 7, rule nc_forward]
@@ -424,6 +440,8 @@ nodeController = do
424 ncEffectWhereIs from label
440 ncEffectWhereIs from label
425 NCMsg from (NamedSend label msg') ->
441 NCMsg from (NamedSend label msg') ->
426 ncEffectNamedSend from label msg'
442 ncEffectNamedSend from label msg'
443+ NCMsg from (Reconnect to) ->
444+ ncEffectReconnect from to
427 unexpected ->
445 unexpected ->
428 error $ "nodeController: unexpected message " ++ show unexpected
446 error $ "nodeController: unexpected message " ++ show unexpected
@@ -547,6 +565,12 @@ ncEffectNamedSend from label msg = do
547 (ProcessIdentifier pid)
565 (ProcessIdentifier pid)
548 (messageToPayload msg)
566 (messageToPayload msg)
569+ncEffectReconnect :: Identifier -> Identifier -> NC ()
570+ncEffectReconnect from to = do
572+ liftIO $ reconnect node from to
550--------------------------------------------------------------------------------
574--------------------------------------------------------------------------------
551-- Auxiliary --
575-- Auxiliary --
552--------------------------------------------------------------------------------
576--------------------------------------------------------------------------------
@@ -592,6 +616,7 @@ destNid (Spawn _ _) = Nothing
592destNid (Register _ _) = Nothing
616destNid (Register _ _) = Nothing
593destNid (WhereIs _) = Nothing
617destNid (WhereIs _) = Nothing
594destNid (NamedSend _ _) = Nothing
618destNid (NamedSend _ _) = Nothing
619+destNid (Reconnect _) = Nothing
595-- We don't need to forward 'Died' signals; if monitoring/linking is setup,
620-- We don't need to forward 'Died' signals; if monitoring/linking is setup,
596-- then when a local process dies the monitoring/linking machinery will take
621-- then when a local process dies the monitoring/linking machinery will take
597-- care of notifying remote nodes
622-- care of notifying remote nodes