@@ -7,56 +7,51 @@ import Data.Map (Map)
7import qualified Data.Map as Map (empty, toList)
7import qualified Data.Map as Map (empty, toList)
8import Data.Set (Set)
8import Data.Set (Set)
9import qualified Data.Set as Set (empty, insert, delete)
9import qualified Data.Set as Set (empty, insert, delete)
10−import qualified Data.ByteString as BSS (ByteString)
11−import qualified Data.ByteString.Lazy as BSL (toChunks)
12−import Data.Binary (encode)
13import Data.Foldable (forM_)
10import Data.Foldable (forM_)
14import Data.Maybe (isJust)
11import Data.Maybe (isJust)
15import Data.Accessor (Accessor, accessor, (^.), (^=), (^:))
12import Data.Accessor (Accessor, accessor, (^.), (^=), (^:))
16−import qualified Data.Accessor.Container as DAC (mapMaybe, mapDefault)
13+import qualified Data.Accessor.Container as DAC (mapDefault)
17import Control.Category ((>>>))
14import Control.Category ((>>>))
18−import Control.Monad (when, unless, forever)
15+import Control.Monad (when, forever)
19import Control.Monad.IO.Class (MonadIO, liftIO)
16import Control.Monad.IO.Class (MonadIO, liftIO)
20import Control.Monad.State (MonadState, StateT, evalStateT, gets, modify)
17import Control.Monad.State (MonadState, StateT, evalStateT, gets, modify)
21−import Control.Monad.Reader (MonadReader(..), ReaderT, runReaderT)
18+import qualified Control.Monad.Trans.Class as Trans (lift)
22import Control.Concurrent.MVar (withMVar)
19import Control.Concurrent.MVar (withMVar)
23−import Control.Concurrent.Chan (readChan, writeChan)
20+import Control.Concurrent.Chan (readChan)
24import Control.Exception (Exception, throwTo)
21import Control.Exception (Exception, throwTo)
25−import qualified Network.Transport as NT ( EndPoint
27− , Reliability(ReliableOrdered)
28− , defaultConnectHints
32import Control.Distributed.Process.Serializable (Serializable)
22import Control.Distributed.Process.Serializable (Serializable)
33import Control.Distributed.Process.Internal.CQueue (enqueue)
23import Control.Distributed.Process.Internal.CQueue (enqueue)
34−import Control.Distributed.Process.Internal ( NodeId(..)
24+import Control.Distributed.Process.Internal.Types
35− , LocalProcessId(..)
26+ , LocalProcessId(..)
36− , ProcessId(..)
27+ , ProcessId(..)
37− , LocalNode(..)
28+ , LocalNode(..)
38− , LocalProcess(..)
29+ , LocalProcess(..)
39− , MonitorRef(..)
30+ , MonitorRef(..)
40− , MonitorNotification(..)
31+ , MonitorNotification(..)
41− , LinkException(..)
32+ , LinkException(..)
42− , DidUnmonitor(..)
33+ , DidUnmonitor(..)
43− , DidUnlink(..)
34+ , DidUnlink(..)
44− , DiedReason(..)
35+ , DiedReason(..)
46− , NCMsg(..)
36+ , NCMsg(..)
47− , ProcessSignal(..)
37+ , ProcessSignal(..)
48− , createMessage
38+ , createMessage
50− , localProcessWithId
39+ , localProcessWithId
43+import Control.Distributed.Process.Internal.MessageT
53--------------------------------------------------------------------------------
48--------------------------------------------------------------------------------
54-- Top-level access to the node controller --
49-- Top-level access to the node controller --
55--------------------------------------------------------------------------------
50--------------------------------------------------------------------------------
57runNodeController :: LocalNode -> IO ()
52runNodeController :: LocalNode -> IO ()
58−runNodeController node =
53+runNodeController node =
59− evalStateT (runReaderT (unNC nodeController) node) initNCState
54+ runMessageT node (evalStateT (unNC nodeController) initNCState)
61--------------------------------------------------------------------------------
56--------------------------------------------------------------------------------
62-- Internal data types --
57-- Internal data types --
@@ -64,16 +59,21 @@ runNodeController node =
65data NCState = NCState
60data NCState = NCState
66 { -- Mapping from remote processes to linked local processes
61 { -- Mapping from remote processes to linked local processes
67− _links :: Map NodeId (Map LocalProcessId (Set ProcessId))
62+ _links :: Map NodeId (Map LocalProcessId (Set ProcessId))
68 -- Mapping from remote processes to monitoring local processes
63 -- Mapping from remote processes to monitoring local processes
69− , _monitors :: Map NodeId (Map LocalProcessId (Map ProcessId (Set MonitorRef)))
64+ , _monitors :: Map NodeId (Map LocalProcessId (Map ProcessId (Set MonitorRef)))
70− -- The node controller maintains its own set of connections
71− -- TODO: still not convinced that this is correct
72− , _connections :: Map Identifier NT.Connection
75−newtype NC a = NC { unNC :: ReaderT LocalNode (StateT NCState IO) a }
67+newtype NC a = NC { unNC :: StateT NCState (MessageT IO) a }
76− deriving (Functor, Monad, MonadIO, MonadState NCState, MonadReader LocalNode)
68+ deriving (Functor, Monad, MonadIO, MonadState NCState)
70+lift :: MessageT IO a -> NC a
71+lift = NC . Trans.lift
73+initNCState :: NCState
74+initNCState = NCState { _links = Map.empty
75+ , _monitors = Map.empty
78--------------------------------------------------------------------------------
78--------------------------------------------------------------------------------
79-- Core functionality --
79-- Core functionality --
@@ -82,13 +82,13 @@ newtype NC a = NC { unNC :: ReaderT LocalNode (StateT NCState IO) a }
82-- [Unified: Table 7]
82-- [Unified: Table 7]
83nodeController :: NC ()
83nodeController :: NC ()
84nodeController = do
84nodeController = do
85− node <- ask
85+ node <- lift getLocalNode
86 forever $ do
86 forever $ do
87 msg <- liftIO $ readChan (localCtrlChan node)
87 msg <- liftIO $ readChan (localCtrlChan node)
89 -- [Unified: Table 7, rule nc_forward]
89 -- [Unified: Table 7, rule nc_forward]
90 case destNid (ctrlMsgSignal msg) of
90 case destNid (ctrlMsgSignal msg) of
91− Just nid' | nid' /= localNodeId node -> sendCtrlMsg nid' msg
91+ Just nid' | nid' /= localNodeId node -> lift $ sendBinary (Right nid') msg
92 _ -> return ()
92 _ -> return ()
94 case msg of
94 case msg of
@@ -106,15 +106,8 @@ nodeController = do
106 ncEffectProcessDied pid reason
106 ncEffectProcessDied pid reason
107 NCMsg _from (Spawn _ _) ->
107 NCMsg _from (Spawn _ _) ->
108 fail "spawn not implemented"
108 fail "spawn not implemented"
109− -- Error cases
109+ unexpected ->
110− NCMsg (Right _) (Link _) ->
110+ error $ "Unexpected message " ++ show unexpected
111− error "Link message from a node?"
112− NCMsg (Right _) (Monitor _ _) ->
113− error "Monitor message from a node?"
114− NCMsg (Right _) (Unlink _) ->
115− error "Unlink message from a node?"
116− NCMsg (Right _) (Unmonitor _) ->
117− error "Unmonitor message from a node?"
119-- [Unified: Table 10]
112-- [Unified: Table 10]
120ncEffectMonitor :: ProcessId -- ^ Who's watching?
113ncEffectMonitor :: ProcessId -- ^ Who's watching?
@@ -122,7 +115,7 @@ ncEffectMonitor :: ProcessId -- ^ Who's watching?
122 -> Maybe MonitorRef -- ^ 'Nothing' to link
115 -> Maybe MonitorRef -- ^ 'Nothing' to link
124ncEffectMonitor from them mRef = do
117ncEffectMonitor from them mRef = do
125− node <- ask
118+ node <- lift getLocalNode
126 shouldLink <-
119 shouldLink <-
127 if not (isLocal node them)
120 if not (isLocal node them)
128 then return True
121 then return True
@@ -136,7 +129,7 @@ ncEffectMonitor from them mRef = do
136 (False, True) -> -- [Unified: second rule]
129 (False, True) -> -- [Unified: second rule]
137 notifyDied from them DiedNoProc mRef
130 notifyDied from them DiedNoProc mRef
138 (False, False) -> -- [Unified: third rule]
131 (False, False) -> -- [Unified: third rule]
139− sendCtrlMsg (processNodeId from) NCMsg
132+ lift $ sendBinary (Right $ processNodeId from) NCMsg
140 { ctrlMsgSender = Right (localNodeId node)
133 { ctrlMsgSender = Right (localNodeId node)
141 , ctrlMsgSignal = Died (Left them) DiedNoProc
134 , ctrlMsgSignal = Died (Left them) DiedNoProc
@@ -144,21 +137,21 @@ ncEffectMonitor from them mRef = do
144-- [Unified: Table 11]
137-- [Unified: Table 11]
145ncEffectUnlink :: ProcessId -> ProcessId -> NC ()
138ncEffectUnlink :: ProcessId -> ProcessId -> NC ()
146ncEffectUnlink from them = do
139ncEffectUnlink from them = do
147− node <- ask
140+ node <- lift getLocalNode
148 when (isLocal node from) $ postMessage from $ DidUnlink them
141 when (isLocal node from) $ postMessage from $ DidUnlink them
149 modify $ linksForProcess them ^: Set.delete from
142 modify $ linksForProcess them ^: Set.delete from
151-- [Unified: Table 11]
144-- [Unified: Table 11]
152ncEffectUnmonitor :: ProcessId -> MonitorRef -> NC ()
145ncEffectUnmonitor :: ProcessId -> MonitorRef -> NC ()
153ncEffectUnmonitor from ref = do
146ncEffectUnmonitor from ref = do
154− node <- ask
147+ node <- lift getLocalNode
155 when (isLocal node from) $ postMessage from $ DidUnmonitor ref
148 when (isLocal node from) $ postMessage from $ DidUnmonitor ref
156 modify $ monitorsFor (monitorRefPid ref) from ^: Set.delete ref
149 modify $ monitorsFor (monitorRefPid ref) from ^: Set.delete ref
158-- [Unified: Table 12, bottom rule]
151-- [Unified: Table 12, bottom rule]
159ncEffectNodeDied :: NodeId -> DiedReason -> NC ()
152ncEffectNodeDied :: NodeId -> DiedReason -> NC ()
160ncEffectNodeDied nid reason = do
153ncEffectNodeDied nid reason = do
161− node <- ask
154+ node <- lift getLocalNode
162 lnks <- gets (^. linksForNode nid)
155 lnks <- gets (^. linksForNode nid)
163 mons <- gets (^. monitorsForNode nid)
156 mons <- gets (^. monitorsForNode nid)
@@ -195,20 +188,16 @@ ncEffectProcessDied pid reason = do
195 . (monitorsForProcess pid ^= Map.empty)
188 . (monitorsForProcess pid ^= Map.empty)
197--------------------------------------------------------------------------------
190--------------------------------------------------------------------------------
198−-- Connections --
191+-- Auxiliary --
199--------------------------------------------------------------------------------
192--------------------------------------------------------------------------------
201−-- | Check if a process is local to our own node
202−isLocal :: LocalNode -> ProcessId -> Bool
203−isLocal nid pid = processNodeId pid == localNodeId nid
205notifyDied :: ProcessId -- ^ Who to notify?
194notifyDied :: ProcessId -- ^ Who to notify?
206 -> ProcessId -- ^ Who died?
195 -> ProcessId -- ^ Who died?
207 -> DiedReason -- ^ How did they die?
196 -> DiedReason -- ^ How did they die?
208 -> Maybe MonitorRef -- ^ 'Nothing' for linking
197 -> Maybe MonitorRef -- ^ 'Nothing' for linking
210notifyDied dest src reason mRef = do
199notifyDied dest src reason mRef = do
211− node <- ask
200+ node <- lift getLocalNode
212 case (isLocal node dest, mRef) of
201 case (isLocal node dest, mRef) of
213 (True, Just ref) ->
202 (True, Just ref) ->
214 postMessage dest $ MonitorNotification ref src reason
203 postMessage dest $ MonitorNotification ref src reason
@@ -217,11 +206,29 @@ notifyDied dest src reason mRef = do
217 (False, _) ->
206 (False, _) ->
218 -- TODO: why the change in sender? How does that affect 'reconnect' semantics?
207 -- TODO: why the change in sender? How does that affect 'reconnect' semantics?
219 -- (see [Unified: Table 10]
208 -- (see [Unified: Table 10]
220− sendCtrlMsg (processNodeId dest) NCMsg
209+ lift $ sendBinary (Right $ processNodeId dest) NCMsg
221 { ctrlMsgSender = Right (localNodeId node)
210 { ctrlMsgSender = Right (localNodeId node)
222 , ctrlMsgSignal = Died (Left src) reason
211 , ctrlMsgSignal = Died (Left src) reason
214+-- | [Unified: Table 8]
215+destNid :: ProcessSignal -> Maybe NodeId
216+destNid (Link pid) = Just . processNodeId $ pid
217+destNid (Unlink pid) = Just . processNodeId $ pid
218+destNid (Monitor pid _) = Just . processNodeId $ pid
219+destNid (Unmonitor ref) = Just . processNodeId . monitorRefPid $ ref
220+destNid (Died (Right _) _) = Nothing
221+destNid (Died (Left _pid) _) = fail "destNid: TODO"
222+destNid (Spawn _ _) = Nothing
224+-- | Check if a process is local to our own node
225+isLocal :: LocalNode -> ProcessId -> Bool
226+isLocal nid pid = processNodeId pid == localNodeId nid
228+--------------------------------------------------------------------------------
229+-- Messages to local processes --
230+--------------------------------------------------------------------------------
225postMessage :: Serializable a => ProcessId -> a -> NC ()
232postMessage :: Serializable a => ProcessId -> a -> NC ()
226postMessage pid msg = withLocalProc pid $ \p ->
233postMessage pid msg = withLocalProc pid $ \p ->
227 enqueue (processQueue p) (createMessage msg)
234 enqueue (processQueue p) (createMessage msg)
@@ -232,7 +239,7 @@ throwException pid e = withLocalProc pid $ \p ->
233withLocalProc :: ProcessId -> (LocalProcess -> IO ()) -> NC ()
240withLocalProc :: ProcessId -> (LocalProcess -> IO ()) -> NC ()
234withLocalProc pid p = do
241withLocalProc pid p = do
235− node <- ask
242+ node <- lift getLocalNode
236 liftIO $ do
243 liftIO $ do
237 -- By [Unified: table 6, rule missing_process] messages to dead processes
244 -- By [Unified: table 6, rule missing_process] messages to dead processes
238 -- can silently be dropped
245 -- can silently be dropped
@@ -240,82 +247,6 @@ withLocalProc pid p = do
240 mProc <- withMVar (localState node) $ return . (^. localProcessWithId lpid)
247 mProc <- withMVar (localState node) $ return . (^. localProcessWithId lpid)
241 forM_ mProc p
248 forM_ mProc p
243−sendTo :: Identifier -> [BSS.ByteString] -> NC ()
244−sendTo them payload = do
245− mConn <- connTo them
246− didSend <- case mConn of
248− didSend <- liftIO $ NT.send conn payload
250− Left _ -> return False
251− Right () -> return True
255− -- [Unified: Table 9, rule node_disconnect]
257− liftIO . writeChan (localCtrlChan node) $ NCMsg
258− { ctrlMsgSender = them
259− , ctrlMsgSignal = Died them DiedDisconnect
262−sendCtrlMsg :: NodeId -> NCMsg -> NC ()
263−sendCtrlMsg dest = sendTo (Right dest) . BSL.toChunks . encode
265−connTo :: Identifier -> NC (Maybe NT.Connection)
267− mConn <- gets (^. connectionTo them)
269− Just conn -> return (Just conn)
270− Nothing -> createConnTo them
272−createConnTo :: Identifier -> NC (Maybe NT.Connection)
273−createConnTo them = do
275− mConn <- liftIO $ NT.connect (localEndPoint node)
278− NT.defaultConnectHints
281− didSend <- liftIO $ NT.send conn firstMsg
286− modify $ connectionTo them ^= Just conn
291− (addr, firstMsg) = case them of
292− Left pid -> ( nodeAddress (processNodeId pid)
293− , idToPayload (Just $ processLocalId pid)
295− Right nid -> ( nodeAddress nid
296− , idToPayload Nothing
299−--------------------------------------------------------------------------------
301−--------------------------------------------------------------------------------
303−destNid :: ProcessSignal -> Maybe NodeId
304−destNid (Link pid) = Just . processNodeId $ pid
305−destNid (Unlink pid) = Just . processNodeId $ pid
306−destNid (Monitor pid _) = Just . processNodeId $ pid
307−destNid (Unmonitor ref) = Just . processNodeId . monitorRefPid $ ref
308−destNid (Died (Right _) _) = Nothing
309−destNid (Died (Left _pid) _) = fail "destNid: TODO"
310−destNid (Spawn _ _) = Nothing
312−initNCState :: NCState
313−initNCState = NCState
314− { _links = Map.empty
315− , _monitors = Map.empty
316− , _connections = Map.empty
319--------------------------------------------------------------------------------
250--------------------------------------------------------------------------------
320-- Accessors --
251-- Accessors --
321--------------------------------------------------------------------------------
252--------------------------------------------------------------------------------
@@ -326,12 +257,6 @@ links = accessor _links (\lnks st -> st { _links = lnks })
326monitors :: Accessor NCState (Map NodeId (Map LocalProcessId (Map ProcessId (Set MonitorRef))))
257monitors :: Accessor NCState (Map NodeId (Map LocalProcessId (Map ProcessId (Set MonitorRef))))
327monitors = accessor _monitors (\mons st -> st { _monitors = mons })
258monitors = accessor _monitors (\mons st -> st { _monitors = mons })
329−connections :: Accessor NCState (Map Identifier NT.Connection)
330−connections = accessor _connections (\conns st -> st { _connections = conns })
332−connectionTo :: Identifier -> Accessor NCState (Maybe NT.Connection)
333−connectionTo them = connections >>> DAC.mapMaybe them
335linksForNode :: NodeId -> Accessor NCState (Map LocalProcessId (Set ProcessId))
260linksForNode :: NodeId -> Accessor NCState (Map LocalProcessId (Set ProcessId))
336linksForNode nid = links >>> DAC.mapDefault Map.empty nid
261linksForNode nid = links >>> DAC.mapDefault Map.empty nid