@@ -24,6 +24,8 @@ import Data.Map (Map)
2424import qualified Data.Map as Map
2525 ( empty
2626 , toList
27+ , fromList
28+ , filter
2729 , partitionWithKey
2830 , elems
2931 , filterWithKey
@@ -31,7 +33,7 @@ import qualified Data.Map as Map
3133import Data.Set (Set)
3234import qualified Data.Set as Set (empty, insert, delete, member)
3335import Data.Foldable (forM_)
34−import Data.Maybe (isJust)
36+import Data.Maybe (isJust, isNothing, catMaybes)
3537import Data.Typeable (Typeable)
3638import Control.Category ((>>>))
3739import Control.Applicative ((<$>))
@@ -118,6 +120,7 @@ import Control.Distributed.Process.Internal.Types
118120 , nodeOf
119121 , SendPortId(..)
120122 , typedChannelWithId
123+ , RegisterReply(..)
121124 , WhereIsReply(..)
122125 , messageToPayload
123126 , payloadToMessage
@@ -437,8 +440,9 @@ data NCState = NCState
437440 _links :: !(Map Identifier (Set ProcessId))
438441 -- Mapping from remote processes to monitoring local processes
439442 , _monitors :: !(Map Identifier (Set (ProcessId, MonitorRef)))
440− -- Process registry
441− , _registry :: !(Map String ProcessId)
443+ -- Process registry: names and where they live, mapped to the PIDs
444+ , _registeredHere :: !(Map String ProcessId)
445+ , _registeredOnNodes :: !(Map ProcessId [(NodeId,Int)])
442446 }
443447
444448newtype NC a = NC { unNC :: StateT NCState (ReaderT LocalNode IO) a }
@@ -447,7 +451,8 @@ newtype NC a = NC { unNC :: StateT NCState (ReaderT LocalNode IO) a }
447451initNCState :: NCState
448452initNCState = NCState { _links = Map.empty
449453 , _monitors = Map.empty
450− , _registry = Map.empty
454+ , _registeredHere = Map.empty
455+ , _registeredOnNodes = Map.empty
451456 }
452457
453458--------------------------------------------------------------------------------
@@ -485,8 +490,8 @@ nodeController = do
485490 ncEffectDied ident reason
486491 NCMsg (ProcessIdentifier from) (Spawn proc ref) ->
487492 ncEffectSpawn from proc ref
488− NCMsg _from (Register label pid) ->
489− ncEffectRegister label pid
493+ NCMsg (ProcessIdentifier from) (Register label atnode pid force) ->
494+ ncEffectRegister from label atnode pid force
490495 NCMsg (ProcessIdentifier from) (WhereIs label) ->
491496 ncEffectWhereIs from label
492497 NCMsg from (NamedSend label msg') ->
@@ -554,6 +559,8 @@ ncEffectDied ident reason = do
554559 (affectedLinks, unaffectedLinks) <- gets (splitNotif ident . (^. links))
555560 (affectedMons, unaffectedMons) <- gets (splitNotif ident . (^. monitors))
556561
562+-- _registry :: !(Map (String,NodeId) ProcessId)
563+
557564 let localOnly = case ident of NodeIdentifier _ -> True ; _ -> False
558565
559566 forM_ (Map.toList affectedLinks) $ \(them, uss) ->
@@ -564,10 +571,34 @@ ncEffectDied ident reason = do
564571 forM_ (Map.toList affectedMons) $ \(them, refs) ->
565572 forM_ refs $ \(us, ref) ->
566573 when (localOnly <= isLocal node (ProcessIdentifier us)) $
567− notifyDied us them reason (Just ref)
574+ notifyDied us them reason (Just ref)
568575
569576 modify' $ (links ^= unaffectedLinks) . (monitors ^= unaffectedMons)
570577
578+ modify' $ registeredHere ^: Map.filter (\pid -> not $ ident `impliesDeathOf` ProcessIdentifier pid)
579+
580+ remaining <- fmap Map.toList (gets (^. registeredOnNodes)) >>=
581+ mapM (\(pid,nidlist) ->
582+ case ident `impliesDeathOf` ProcessIdentifier pid of
583+ True ->
584+ do forM_ nidlist $ \(nid,_) ->
585+ when (not $ isLocal node (NodeIdentifier nid))
586+ (forwardNameDeath node nid)
587+ return Nothing
588+ False -> return $ Just (pid,nidlist) )
589+ modify' $ registeredOnNodes ^= (Map.fromList (catMaybes remaining))
590+ where
591+ forwardNameDeath node nid =
592+ liftIO $ sendBinary node
593+ (NodeIdentifier $ localNodeId node)
594+ (NodeIdentifier $ nid)
595+ WithImplicitReconnect
596+ NCMsg
597+ { ctrlMsgSender = NodeIdentifier (localNodeId node)
598+ , ctrlMsgSignal = Died ident reason
599+ }
600+
601+
571602-- [Unified: Table 13]
572603ncEffectSpawn :: ProcessId -> Closure (Process ()) -> SpawnRef -> NC ()
573604ncEffectSpawn pid cProc ref = do
@@ -587,18 +618,71 @@ ncEffectSpawn pid cProc ref = do
587618
588619-- Unified semantics does not explicitly describe how to implement 'register',
589620-- but mentions it's "very similar to nsend" (Table 14)
590−ncEffectRegister :: String -> Maybe ProcessId -> NC ()
591−ncEffectRegister label mPid =
592− modify' $ registryFor label ^= mPid
593− -- An acknowledgement is not necessary. If we want a synchronous register,
594− -- it suffices to send a whereis requiry immediately after the register
595− -- (that may not suffice if we do decide for unreliable messaging instead)
621+-- We send a response indicated if the operation is invalid
622+ncEffectRegister :: ProcessId -> String -> NodeId -> Maybe ProcessId -> Bool -> NC ()
623+ncEffectRegister from label atnode mPid reregistration = do
624+ node <- ask
625+ currentVal <- gets (^. registeredHereFor label)
626+ isOk <-
627+ case mPid of
628+ Nothing -> -- unregister request
629+ return $ isJust currentVal
630+ Just thepid -> -- register request
631+ do isvalidlocal <- isValidLocalIdentifier (ProcessIdentifier thepid)
632+ return $ (isNothing currentVal /= reregistration) &&
633+ (not (isLocal node (ProcessIdentifier thepid) ) || isvalidlocal )
634+ if isLocal node (NodeIdentifier atnode)
635+ then do when (isOk) $
636+ do modify' $ registeredHereFor label ^= mPid
637+ updateRemote node currentVal mPid
638+ liftIO $ sendMessage node
639+ (NodeIdentifier (localNodeId node))
640+ (ProcessIdentifier from)
641+ WithImplicitReconnect
642+ (RegisterReply label isOk)
643+ else let operation =
644+ case reregistration of
645+ True -> flip decList
646+ False -> flip incList
647+ in case mPid of
648+ Nothing -> return ()
649+ Just pid -> modify' $ registeredOnNodesFor pid ^: (maybeify $ operation atnode)
650+ where updateRemote node (Just oldval) (Just newval) | processNodeId oldval /= processNodeId newval =
651+ do forward node (processNodeId oldval) (Register label atnode (Just oldval) True)
652+ forward node (processNodeId newval) (Register label atnode (Just newval) False)
653+ updateRemote node Nothing (Just newval) =
654+ forward node (processNodeId newval) (Register label atnode (Just newval) False)
655+ updateRemote node (Just oldval) Nothing =
656+ forward node (processNodeId oldval) (Register label atnode (Just oldval) True)
657+ updateRemote _ _ _ = return ()
658+ maybeify f Nothing = unmaybeify $ f []
659+ maybeify f (Just x) = unmaybeify $ f x
660+ unmaybeify [] = Nothing
661+ unmaybeify x = Just x
662+ incList [] tag = [(tag,1)]
663+ incList ((atag,acount):xs) tag | tag==atag = (atag,acount+1) : xs
664+ incList (x:xs) tag = x : incList xs tag
665+ decList [] _ = []
666+ decList ((atag,1):xs) tag | atag == tag = xs
667+ decList ((atag,n):xs) tag | atag == tag = (atag,n-1):xs
668+ decList (x:xs) tag = x:decList xs tag
669+ forward node to reg =
670+ when (not $ isLocal node (NodeIdentifier to)) $
671+ liftIO $ sendBinary node
672+ (ProcessIdentifier from)
673+ (NodeIdentifier to)
674+ WithImplicitReconnect
675+ NCMsg
676+ { ctrlMsgSender = ProcessIdentifier from
677+ , ctrlMsgSignal = reg
678+ }
679+
596680
597681-- Unified semantics does not explicitly describe 'whereis'
598682ncEffectWhereIs :: ProcessId -> String -> NC ()
599683ncEffectWhereIs from label = do
600684 node <- ask
601− mPid <- gets (^. registryFor label)
685+ mPid <- gets (^. registeredHereFor label)
602686 liftIO $ sendMessage node
603687 (NodeIdentifier (localNodeId node))
604688 (ProcessIdentifier from)
@@ -608,9 +692,9 @@ ncEffectWhereIs from label = do
608692-- [Unified: Table 14]
609693ncEffectNamedSend :: Identifier -> String -> Message -> NC ()
610694ncEffectNamedSend from label msg = do
611− mPid <- gets (^. registryFor label)
695+ node <- ask
696+ mPid <- gets (^. registeredHereFor label)
612697 -- If mPid is Nothing, we just ignore the named send (as per Table 14)
613− node <- ask
614698 forM_ mPid $ \pid ->
615699 liftIO $ sendPayload node
616700 from
@@ -660,7 +744,7 @@ destNid (Unlink ident) = Just $ nodeOf ident
660744destNid (Monitor ref) = Just $ nodeOf (monitorRefIdent ref)
661745destNid (Unmonitor ref) = Just $ nodeOf (monitorRefIdent ref)
662746destNid (Spawn _ _) = Nothing
663−destNid (Register _ _) = Nothing
747+destNid (Register _ _ _ _) = Nothing
664748destNid (WhereIs _) = Nothing
665749destNid (NamedSend _ _) = Nothing
666750-- We don't need to forward 'Died' signals; if monitoring/linking is setup,
@@ -732,8 +816,11 @@ links = accessor _links (\ls st -> st { _links = ls })
732816monitors :: Accessor NCState (Map Identifier (Set (ProcessId, MonitorRef)))
733817monitors = accessor _monitors (\ms st -> st { _monitors = ms })
734818
735−registry :: Accessor NCState (Map String ProcessId)
736−registry = accessor _registry (\ry st -> st { _registry = ry })
819+registeredHere :: Accessor NCState (Map String ProcessId)
820+registeredHere = accessor _registeredHere (\ry st -> st { _registeredHere = ry })
821+
822+registeredOnNodes :: Accessor NCState (Map ProcessId [(NodeId, Int)])
823+registeredOnNodes = accessor _registeredOnNodes (\ry st -> st { _registeredOnNodes = ry })
737824
738825linksFor :: Identifier -> Accessor NCState (Set ProcessId)
739826linksFor ident = links >>> DAC.mapDefault Set.empty ident
@@ -741,8 +828,11 @@ linksFor ident = links >>> DAC.mapDefault Set.empty ident
741828monitorsFor :: Identifier -> Accessor NCState (Set (ProcessId, MonitorRef))
742829monitorsFor ident = monitors >>> DAC.mapDefault Set.empty ident
743830
744−registryFor :: String -> Accessor NCState (Maybe ProcessId)
745−registryFor ident = registry >>> DAC.mapMaybe ident
831+registeredHereFor :: String -> Accessor NCState (Maybe ProcessId)
832+registeredHereFor ident = registeredHere >>> DAC.mapMaybe ident
833+
834+registeredOnNodesFor :: ProcessId -> Accessor NCState (Maybe [(NodeId,Int)])
835+registeredOnNodesFor ident = registeredOnNodes >>> DAC.mapMaybe ident
746836
747837-- | @splitNotif ident@ splits a notifications map into those
748838-- notifications that should trigger when 'ident' fails and those links that