@@ -13,7 +13,14 @@ import System.IO (fixIO)
13import qualified Data.ByteString.Lazy as BSL (fromChunks)
13import qualified Data.ByteString.Lazy as BSL (fromChunks)
14import Data.Binary (decode)
14import Data.Binary (decode)
15import Data.Map (Map)
15import Data.Map (Map)
16−import qualified Data.Map as Map (empty, lookup, insert, delete, toList)
16+import qualified Data.Map as Map
17import qualified Data.List as List (delete)
24import qualified Data.List as List (delete)
18import Data.Set (Set)
25import Data.Set (Set)
19import qualified Data.Set as Set (empty, insert, delete, member)
26import qualified Data.Set as Set (empty, insert, delete, member)
@@ -73,18 +80,26 @@ import Control.Distributed.Process.Internal.Types
73 , localPidUnique
80 , localPidUnique
74 , localProcessWithId
81 , localProcessWithId
75 , MonitorRef(..)
82 , MonitorRef(..)
76− , MonitorNotification(..)
83+ , ProcessMonitorNotification(..)
77− , LinkException(..)
84+ , NodeMonitorNotification(..)
85+ , PortMonitorNotification(..)
86+ , ProcessLinkException(..)
87+ , NodeLinkException(..)
88+ , PortLinkException(..)
78 , DidUnmonitor(..)
89 , DidUnmonitor(..)
79− , DidUnlink(..)
90+ , DidUnlinkProcess(..)
80 , SpawnRef
93 , SpawnRef
81 , DidSpawn(..)
94 , DidSpawn(..)
82 , Closure(..)
95 , Closure(..)
83 , Static(..)
96 , Static(..)
84 , MessageT
98 , MessageT
85 , TypedChannel(..)
99 , TypedChannel(..)
86 , Identifier(..)
100 , Identifier(..)
87− , ChannelId(..)
101+ , nodeOf
88 , typedChannelWithId
103 , typedChannelWithId
90import Control.Distributed.Process.Serializable (Serializable)
105import Control.Distributed.Process.Serializable (Serializable)
@@ -249,9 +264,9 @@ handleIncomingMessages node = go [] Map.empty Map.empty Set.empty
249 -- incoming connections (this requires a Transport layer
264 -- incoming connections (this requires a Transport layer
250 -- extension). (**)
265 -- extension). (**)
251 go (List.delete cid uninitConns) procs chans ctrls
266 go (List.delete cid uninitConns) procs chans ctrls
252− ChannelIdentifier chId -> do
267+ SendPortIdentifier chId -> do
253− let lcid = channelLocalId chId
268+ let lcid = sendPortLocalId chId
254− lpid = processLocalId (channelProcessId chId)
269+ lpid = processLocalId (sendPortProcessId chId)
255 mProc <- withMVar state $ return . (^. localProcessWithId lpid)
270 mProc <- withMVar state $ return . (^. localProcessWithId lpid)
256 case mProc of
271 case mProc of
257 Just proc -> do
272 Just proc -> do
@@ -325,9 +340,9 @@ runNodeController node =
326data NCState = NCState
341data NCState = NCState
327 { -- Mapping from remote processes to linked local processes
342 { -- Mapping from remote processes to linked local processes
328− _links :: Map NodeId (Map LocalProcessId (Set ProcessId))
343+ _links :: Map Identifier (Set ProcessId)
329 -- Mapping from remote processes to monitoring local processes
344 -- Mapping from remote processes to monitoring local processes
330− , _monitors :: Map NodeId (Map LocalProcessId (Map ProcessId (Set MonitorRef)))
345+ , _monitors :: Map Identifier (Set (ProcessId, MonitorRef))
333newtype NC a = NC { unNC :: StateT NCState (MessageT IO) a }
348newtype NC a = NC { unNC :: StateT NCState (MessageT IO) a }
@@ -362,24 +377,22 @@ nodeController = do
362 case msg of
377 case msg of
363 NCMsg (ProcessIdentifier from) (Link them) ->
378 NCMsg (ProcessIdentifier from) (Link them) ->
364 ncEffectMonitor from them Nothing
379 ncEffectMonitor from them Nothing
365− NCMsg (ProcessIdentifier from) (Monitor them ref) ->
380+ NCMsg (ProcessIdentifier from) (Monitor ref) ->
366− ncEffectMonitor from them (Just ref)
381+ ncEffectMonitor from (monitorRefIdent ref) (Just ref)
367 NCMsg (ProcessIdentifier from) (Unlink them) ->
382 NCMsg (ProcessIdentifier from) (Unlink them) ->
368 ncEffectUnlink from them
383 ncEffectUnlink from them
369 NCMsg (ProcessIdentifier from) (Unmonitor ref) ->
384 NCMsg (ProcessIdentifier from) (Unmonitor ref) ->
370 ncEffectUnmonitor from ref
385 ncEffectUnmonitor from ref
371− NCMsg _from (Died (NodeIdentifier nid) reason) ->
386+ NCMsg _from (Died ident reason) ->
372− ncEffectNodeDied nid reason
387+ ncEffectDied ident reason
373− NCMsg _from (Died (ProcessIdentifier pid) reason) ->
374− ncEffectProcessDied pid reason
375 NCMsg (ProcessIdentifier from) (Spawn proc ref) ->
388 NCMsg (ProcessIdentifier from) (Spawn proc ref) ->
376 ncEffectSpawn from proc ref
389 ncEffectSpawn from proc ref
377 unexpected ->
390 unexpected ->
378− error $ "Unexpected message " ++ show unexpected
391+ error $ "nodeController: unexpected message " ++ show unexpected
380-- [Unified: Table 10]
393-- [Unified: Table 10]
381ncEffectMonitor :: ProcessId -- ^ Who's watching?
394ncEffectMonitor :: ProcessId -- ^ Who's watching?
382− -> ProcessId -- ^ Who's being watched?
395+ -> Identifier -- ^ Who's being watched?
383 -> Maybe MonitorRef -- ^ 'Nothing' to link
396 -> Maybe MonitorRef -- ^ 'Nothing' to link
385ncEffectMonitor from them mRef = do
398ncEffectMonitor from them mRef = do
@@ -387,73 +400,62 @@ ncEffectMonitor from them mRef = do
387 shouldLink <-
400 shouldLink <-
388 if not (isLocal node them)
401 if not (isLocal node them)
389 then return True
402 then return True
390− else liftIO . withMVar (localState node) $
403+ else isValidLocalIdentifier them
391− return . isJust . (^. localProcessWithId (processLocalId them))
392− case (shouldLink, isLocal node from) of
404+ case (shouldLink, isLocal node (ProcessIdentifier from)) of
393 (True, _) -> -- [Unified: first rule]
405 (True, _) -> -- [Unified: first rule]
394 case mRef of
406 case mRef of
395− Just ref -> modify $ monitorsFor them from ^: Set.insert ref
407+ Just ref -> modify $ monitorsFor them ^: Set.insert (from, ref)
396− Nothing -> modify $ linksForProcess them ^: Set.insert from
408+ Nothing -> modify $ linksFor them ^: Set.insert from
397 (False, True) -> -- [Unified: second rule]
409 (False, True) -> -- [Unified: second rule]
398− notifyDied from them DiedNoProc mRef
410+ notifyDied from them DiedUnknownId mRef
399 (False, False) -> -- [Unified: third rule]
411 (False, False) -> -- [Unified: third rule]
400 ncMsg $ sendBinary (NodeIdentifier $ processNodeId from) NCMsg
412 ncMsg $ sendBinary (NodeIdentifier $ processNodeId from) NCMsg
401 { ctrlMsgSender = NodeIdentifier (localNodeId node)
413 { ctrlMsgSender = NodeIdentifier (localNodeId node)
402− , ctrlMsgSignal = Died (ProcessIdentifier them) DiedNoProc
414+ , ctrlMsgSignal = Died them DiedUnknownId
405-- [Unified: Table 11]
417-- [Unified: Table 11]
406−ncEffectUnlink :: ProcessId -> ProcessId -> NC ()
418+ncEffectUnlink :: ProcessId -> Identifier -> NC ()
407ncEffectUnlink from them = do
419ncEffectUnlink from them = do
408 node <- ncMsg getLocalNode
420 node <- ncMsg getLocalNode
409− when (isLocal node from) $ postMessage from $ DidUnlink them
421+ when (isLocal node (ProcessIdentifier from)) $
423+ ProcessIdentifier pid ->
424+ postAsMessage from $ DidUnlinkProcess pid
425+ NodeIdentifier nid ->
426+ postAsMessage from $ DidUnlinkNode nid
427+ SendPortIdentifier cid ->
428+ postAsMessage from $ DidUnlinkPort cid
410− modify $ linksForProcess them ^: Set.delete from
429+ modify $ linksFor them ^: Set.delete from
412-- [Unified: Table 11]
431-- [Unified: Table 11]
413ncEffectUnmonitor :: ProcessId -> MonitorRef -> NC ()
432ncEffectUnmonitor :: ProcessId -> MonitorRef -> NC ()
414ncEffectUnmonitor from ref = do
433ncEffectUnmonitor from ref = do
415 node <- ncMsg getLocalNode
434 node <- ncMsg getLocalNode
435+ when (isLocal node (ProcessIdentifier from)) $
416− when (isLocal node from) $ postMessage from $ DidUnmonitor ref
436+ postAsMessage from $ DidUnmonitor ref
417− modify $ monitorsFor (monitorRefPid ref) from ^: Set.delete ref
437+ modify $ monitorsFor (monitorRefIdent ref) ^: Set.delete (from, ref)
419−-- [Unified: Table 12, bottom rule]
420−ncEffectNodeDied :: NodeId -> DiedReason -> NC ()
421−ncEffectNodeDied nid reason = do
422− node <- ncMsg getLocalNode
423− lnks <- gets (^. linksForNode nid)
424− mons <- gets (^. monitorsForNode nid)
426− -- We only need to notify local processes
427− forM_ (Map.toList lnks) $ \(them, uss) -> do
428− let pid = ProcessId nid them
430− when (isLocal node us) $
431− notifyDied us pid reason Nothing
433− forM_ (Map.toList mons) $ \(them, uss) -> do
439+-- [Unified: Table 12]
434− let pid = ProcessId nid them
440+ncEffectDied :: Identifier -> DiedReason -> NC ()
435− forM_ (Map.toList uss) $ \(us, refs) ->
441+ncEffectDied ident reason = do
436− when (isLocal node us) $
442+ node <- ncMsg getLocalNode
437− forM_ refs $ notifyDied us pid reason . Just
443+ (affectedLinks, unaffectedLinks) <- gets (splitNotif ident . (^. links))
438−
444+ (affectedMons, unaffectedMons) <- gets (splitNotif ident . (^. monitors))
439− modify $ (linksForNode nid ^= Map.empty)
440− . (monitorsForNode nid ^= Map.empty)
442−-- [Unified: Table 12, top rule]
446+ let localOnly = case ident of NodeIdentifier _ -> True ; _ -> False
443−ncEffectProcessDied :: ProcessId -> DiedReason -> NC ()
444−ncEffectProcessDied pid reason = do
445− lnks <- gets (^. linksForProcess pid)
446− mons <- gets (^. monitorsForProcess pid)
448− forM_ lnks $ \us ->
448+ forM_ (Map.toList affectedLinks) $ \(them, uss) ->
450+ when (localOnly <= isLocal node (ProcessIdentifier us)) $
449− notifyDied us pid reason Nothing
451+ notifyDied us them reason Nothing
451− forM_ (Map.toList mons) $ \(us, refs) ->
453+ forM_ (Map.toList affectedMons) $ \(them, refs) ->
452− forM_ refs $
454+ forM_ refs $ \(us, ref) ->
455+ when (localOnly <= isLocal node (ProcessIdentifier us)) $
453− notifyDied us pid reason . Just
456+ notifyDied us them reason (Just ref)
455− modify $ (linksForProcess pid ^= Set.empty)
458+ modify $ (links ^= unaffectedLinks) . (monitors ^= unaffectedMons)
456− . (monitorsForProcess pid ^= Map.empty)
458-- [Unified: Table 13]
460-- [Unified: Table 13]
459ncEffectSpawn :: ProcessId -> Closure (Process ()) -> SpawnRef -> NC ()
461ncEffectSpawn :: ProcessId -> Closure (Process ()) -> SpawnRef -> NC ()
@@ -471,46 +473,48 @@ ncEffectSpawn pid cProc ref = do
471--------------------------------------------------------------------------------
473--------------------------------------------------------------------------------
473notifyDied :: ProcessId -- ^ Who to notify?
475notifyDied :: ProcessId -- ^ Who to notify?
474− -> ProcessId -- ^ Who died?
476+ -> Identifier -- ^ Who died?
475 -> DiedReason -- ^ How did they die?
477 -> DiedReason -- ^ How did they die?
476 -> Maybe MonitorRef -- ^ 'Nothing' for linking
478 -> Maybe MonitorRef -- ^ 'Nothing' for linking
478notifyDied dest src reason mRef = do
480notifyDied dest src reason mRef = do
479 node <- ncMsg getLocalNode
481 node <- ncMsg getLocalNode
480− case (isLocal node dest, mRef) of
482+ case (isLocal node (ProcessIdentifier dest), mRef, src) of
481− (True, Just ref) ->
483+ (True, Just ref, ProcessIdentifier pid) ->
482− postMessage dest $ MonitorNotification ref src reason
484+ postAsMessage dest $ ProcessMonitorNotification ref pid reason
483− (True, Nothing) ->
485+ (True, Just ref, NodeIdentifier nid) ->
486+ postAsMessage dest $ NodeMonitorNotification ref nid reason
487+ (True, Just ref, SendPortIdentifier cid) ->
488+ postAsMessage dest $ PortMonitorNotification ref cid reason
489+ (True, Nothing, ProcessIdentifier pid) ->
484− throwException dest $ LinkException src reason
490+ throwException dest $ ProcessLinkException pid reason
491+ (True, Nothing, NodeIdentifier pid) ->
492+ throwException dest $ NodeLinkException pid reason
493+ (True, Nothing, SendPortIdentifier pid) ->
494+ throwException dest $ PortLinkException pid reason
485− (False, _) ->
495+ (False, _, _) ->
486 -- TODO: why the change in sender? How does that affect 'reconnect' semantics?
496 -- TODO: why the change in sender? How does that affect 'reconnect' semantics?
487 -- (see [Unified: Table 10]
497 -- (see [Unified: Table 10]
488 ncMsg $ sendBinary (NodeIdentifier $ processNodeId dest) NCMsg
498 ncMsg $ sendBinary (NodeIdentifier $ processNodeId dest) NCMsg
489 { ctrlMsgSender = NodeIdentifier (localNodeId node)
499 { ctrlMsgSender = NodeIdentifier (localNodeId node)
490− , ctrlMsgSignal = Died (ProcessIdentifier src) reason
500+ , ctrlMsgSignal = Died src reason
493-- | [Unified: Table 8]
503-- | [Unified: Table 8]
494destNid :: ProcessSignal -> Maybe NodeId
504destNid :: ProcessSignal -> Maybe NodeId
495−destNid (Link pid) =
505+destNid (Link ident) = Just $ nodeOf ident
496− Just . processNodeId $ pid
497−destNid (Unlink pid) =
506+destNid (Unlink ident) = Just $ nodeOf ident
498− Just . processNodeId $ pid
499−destNid (Monitor pid _) =
507+destNid (Monitor ref) = Just $ nodeOf (monitorRefIdent ref)
500− Just . processNodeId $ pid
501−destNid (Unmonitor ref) =
508+destNid (Unmonitor ref) = Just $ nodeOf (monitorRefIdent ref)
502− Just . processNodeId . monitorRefPid $ ref
509+destNid (Spawn _ _) = Nothing
503-- We don't need to forward 'Died' signals; if monitoring/linking is setup,
510-- We don't need to forward 'Died' signals; if monitoring/linking is setup,
504-- then when a local process dies the monitoring/linking machinery will take
511-- then when a local process dies the monitoring/linking machinery will take
505-- care of notifying remote nodes
512-- care of notifying remote nodes
506−destNid (Died _ _) =
513+destNid (Died _ _) = Nothing
508−destNid (Spawn _ _) =
511-- | Check if a process is local to our own node
515-- | Check if a process is local to our own node
512−isLocal :: LocalNode -> ProcessId -> Bool
516+isLocal :: LocalNode -> Identifier -> Bool
513−isLocal nid pid = processNodeId pid == localNodeId nid
517+isLocal nid ident = nodeOf ident == localNodeId nid
515-- | Lookup a local closure
519-- | Lookup a local closure
516unClosure :: Typeable a => Closure a -> NC (Maybe a)
520unClosure :: Typeable a => Closure a -> NC (Maybe a)
@@ -518,13 +522,35 @@ unClosure (Closure (Static label) env) = do
518 rtable <- remoteTable <$> ncMsg getLocalNode
522 rtable <- remoteTable <$> ncMsg getLocalNode
519 return (resolveClosure rtable label env >>= fromDynamic)
523 return (resolveClosure rtable label env >>= fromDynamic)
525+-- | Check if an identifier refers to a valid local object
526+isValidLocalIdentifier :: Identifier -> NC Bool
527+isValidLocalIdentifier ident = do
528+ node <- ncMsg getLocalNode
529+ liftIO . withMVar (localState node) $ \nSt ->
531+ NodeIdentifier nid ->
532+ return $ nid == localNodeId node
533+ ProcessIdentifier pid -> do
534+ let mProc = nSt ^. localProcessWithId (processLocalId pid)
535+ return $ isJust mProc
536+ SendPortIdentifier cid -> do
537+ let pid = sendPortProcessId cid
538+ mProc = nSt ^. localProcessWithId (processLocalId pid)
540+ Nothing -> return False
541+ Just proc -> withMVar (processState proc) $ \pSt -> do
542+ let mCh = pSt ^. typedChannelWithId (sendPortLocalId cid)
521--------------------------------------------------------------------------------
545--------------------------------------------------------------------------------
522-- Messages to local processes --
546-- Messages to local processes --
523--------------------------------------------------------------------------------
547--------------------------------------------------------------------------------
525−postMessage :: Serializable a => ProcessId -> a -> NC ()
549+postAsMessage :: Serializable a => ProcessId -> a -> NC ()
550+postAsMessage pid = postMessage pid . createMessage
552+postMessage :: ProcessId -> Message -> NC ()
526−postMessage pid msg = withLocalProc pid $ \p ->
553+postMessage pid msg = withLocalProc pid $ \p -> enqueue (processQueue p) msg
527− enqueue (processQueue p) (createMessage msg)
529throwException :: Exception e => ProcessId -> e -> NC ()
555throwException :: Exception e => ProcessId -> e -> NC ()
530throwException pid e = withLocalProc pid $ \p ->
556throwException pid e = withLocalProc pid $ \p ->
@@ -544,23 +570,57 @@ withLocalProc pid p = do
544-- Accessors --
570-- Accessors --
545--------------------------------------------------------------------------------
571--------------------------------------------------------------------------------
547−links :: Accessor NCState (Map NodeId (Map LocalProcessId (Set ProcessId)))
573+links :: Accessor NCState (Map Identifier (Set ProcessId))
548−links = accessor _links (\lnks st -> st { _links = lnks })
574+links = accessor _links (\ls st -> st { _links = ls })
550−monitors :: Accessor NCState (Map NodeId (Map LocalProcessId (Map ProcessId (Set MonitorRef))))
551−monitors = accessor _monitors (\mons st -> st { _monitors = mons })
553−linksForNode :: NodeId -> Accessor NCState (Map LocalProcessId (Set ProcessId))
576+monitors :: Accessor NCState (Map Identifier (Set (ProcessId, MonitorRef)))
554−linksForNode nid = links >>> DAC.mapDefault Map.empty nid
577+monitors = accessor _monitors (\ms st -> st { _monitors = ms })
556−monitorsForNode :: NodeId -> Accessor NCState (Map LocalProcessId (Map ProcessId (Set MonitorRef)))
579+linksFor :: Identifier -> Accessor NCState (Set ProcessId)
557−monitorsForNode nid = monitors >>> DAC.mapDefault Map.empty nid
580+linksFor ident = links >>> DAC.mapDefault Set.empty ident
559−linksForProcess :: ProcessId -> Accessor NCState (Set ProcessId)
582+monitorsFor :: Identifier -> Accessor NCState (Set (ProcessId, MonitorRef))
560−linksForProcess pid = linksForNode (processNodeId pid) >>> DAC.mapDefault Set.empty (processLocalId pid)
583+monitorsFor ident = monitors >>> DAC.mapDefault Set.empty ident
562−monitorsForProcess :: ProcessId -> Accessor NCState (Map ProcessId (Set MonitorRef))
585+-- | @splitNotif ident@ splits a notifications map into those
563−monitorsForProcess pid = monitorsForNode (processNodeId pid) >>> DAC.mapDefault Map.empty (processLocalId pid)
586+-- notifications that should trigger when 'ident' fails and those links that
565−monitorsFor :: ProcessId -> ProcessId -> Accessor NCState (Set MonitorRef)
588+--
566−monitorsFor them us = monitorsForProcess them >>> DAC.mapDefault Set.empty us
589+-- There is a hierarchy between identifiers: failure of a node implies failure
590+-- of all processes on that node, and failure of a process implies failure of
591+-- all typed channels to that process. In other words, if 'ident' refers to a
592+-- node, then the /should trigger/ set will include
594+-- * the notifications for the node specifically
595+-- * the notifications for processes on that node, and
596+-- * the notifications for typed channels to processes on that node.
598+-- Similarly, if 'ident' refers to a process, the /should trigger/ set will
601+-- * the notifications for that process specifically and
602+-- * the notifications for typed channels to that process.
603+splitNotif :: Identifier
605+ -> (Map Identifier a, Map Identifier a)
606+splitNotif ident = Map.partitionWithKey (const . impliesDeathOf ident)
608+ -- | Does the death of one entity (node, project, channel) imply the death
610+ impliesDeathOf :: Identifier -- ^ Who died
611+ -> Identifier -- ^ Who's being watched
612+ -> Bool -- ^ Does this death implies the death of the watchee?
613+ NodeIdentifier nid `impliesDeathOf` NodeIdentifier nid' =
615+ NodeIdentifier nid `impliesDeathOf` ProcessIdentifier pid =
616+ processNodeId pid == nid
617+ NodeIdentifier nid `impliesDeathOf` SendPortIdentifier cid =
618+ processNodeId (sendPortProcessId cid) == nid
619+ ProcessIdentifier pid `impliesDeathOf` ProcessIdentifier pid' =
621+ ProcessIdentifier pid `impliesDeathOf` SendPortIdentifier cid =
622+ sendPortProcessId cid == pid
623+ SendPortIdentifier cid `impliesDeathOf` SendPortIdentifier cid' =
625+ _ `impliesDeathOf` _ =