@@ -78,32 +78,22 @@ module Control.Distributed.Process
80import Prelude hiding (catch)
80import Prelude hiding (catch)
81−import qualified Data.ByteString as BSS (ByteString, concat, splitAt)
81+import qualified Data.ByteString as BSS (ByteString)
82−import qualified Data.ByteString.Lazy as BSL ( ByteString
82+import qualified Data.ByteString.Lazy as BSL (fromChunks)
87−import Data.Binary (Binary, decode, encode, put, get, putWord8, getWord8)
83+import Data.Binary (decode)
88import Data.Map (Map)
84import Data.Map (Map)
89−import qualified Data.Map as Map (empty, lookup, insert, delete, toList)
85+import qualified Data.Map as Map (empty, lookup, insert, delete)
90import qualified Data.List as List (delete)
86import qualified Data.List as List (delete)
91import Data.Set (Set)
87import Data.Set (Set)
92import qualified Data.Set as Set (empty, insert, delete, member)
88import qualified Data.Set as Set (empty, insert, delete, member)
93−import Data.Int (Int32)
94−import Data.Typeable (Typeable)
95import Data.Foldable (forM_)
89import Data.Foldable (forM_)
96−import Data.Maybe (isJust)
97−import Control.Monad (void, when, unless, forever)
90+import Control.Monad (void)
98import Control.Monad.Reader (MonadReader(..), ReaderT, runReaderT)
91import Control.Monad.Reader (MonadReader(..), ReaderT, runReaderT)
99import Control.Monad.IO.Class (MonadIO, liftIO)
92import Control.Monad.IO.Class (MonadIO, liftIO)
100−import Control.Monad.State (MonadState, StateT, evalStateT, gets, modify)
101−import Control.Applicative ((<$>), (<*>))
93+import Control.Applicative ((<$>))
102−import Control.Category ((>>>))
103import Control.Exception (Exception, throwIO, catch, SomeException)
94import Control.Exception (Exception, throwIO, catch, SomeException)
104import Control.Concurrent (forkIO)
95import Control.Concurrent (forkIO)
105−import Control.Concurrent.MVar ( MVar
96+import Control.Concurrent.MVar ( newMVar
107 , withMVar
97 , withMVar
108 , modifyMVar
98 , modifyMVar
109 , modifyMVar_
99 , modifyMVar_
@@ -111,22 +101,14 @@ import Control.Concurrent.MVar ( MVar
111 , putMVar
101 , putMVar
112 , takeMVar
102 , takeMVar
114−import Control.Concurrent.Chan (Chan, newChan, readChan, writeChan)
104+import Control.Concurrent.Chan (newChan, writeChan)
115−import Control.Distributed.Process.Internal.CQueue ( CQueue
105+import Control.Distributed.Process.Internal.CQueue ( dequeueMatching
117 , enqueue
106 , enqueue
118 , newCQueue
107 , newCQueue
120−import Control.Distributed.Process.Serializable ( Serializable
109+import Control.Distributed.Process.Serializable (Serializable, fingerprint)
127import qualified Network.Transport as NT ( Transport
110import qualified Network.Transport as NT ( Transport
128 , EndPoint
111 , EndPoint
130 , Connection
112 , Connection
131 , Reliability(ReliableOrdered)
113 , Reliability(ReliableOrdered)
132 , defaultConnectHints
114 , defaultConnectHints
@@ -142,65 +124,33 @@ import qualified Network.Transport as NT ( Transport
142 , closeEndPoint
124 , closeEndPoint
143 , ConnectionId
125 , ConnectionId
145−import qualified Network.Transport.Internal as NTI (encodeInt32, decodeInt32)
146−import Data.Accessor (Accessor, accessor, (^.), (^=), (^:))
127+import Data.Accessor ((^.), (^=), (^:))
147−import qualified Data.Accessor.Container as DAC (mapMaybe, mapDefault)
148import System.Random (randomIO)
128import System.Random (randomIO)
149−
129+import Control.Distributed.Process.Internal.NodeController (runNodeController)
150−-- | A local process ID consists of a seed which distinguishes processes from
130+import Control.Distributed.Process.Internal ( NodeId(..)
151−-- different instances of the same local node and a counter
152−data LocalProcessId = LocalProcessId
131+ , LocalProcessId(..)
153− { lpidUnique :: Int32
132+ , ProcessId(..)
154− , lpidCounter :: Int32
133+ , LocalNode(..)
155− }
134+ , LocalNodeState(..)
156− deriving (Eq, Ord, Typeable, Show)
135+ , LocalProcess(..)
157−
136+ , LocalProcessState(..)
158−-- We identify node IDs and endpoint IDs
159−newtype NodeId = NodeId { nodeAddress :: NT.EndPointAddress }
160− deriving (Show, Eq, Ord, Binary)
162−-- | A process ID combines a local process with with an endpoint address
163−-- (in other words, we identify nodes and endpoints)
164−data ProcessId = ProcessId
165− { processNodeId :: NodeId
166− , processLocalId :: LocalProcessId
168− deriving (Eq, Ord, Typeable, Show)
170−-- | Messages consist of their typeRep fingerprint and their encoding
171−data Message = Message
137+ , Message(..)
172− { messageFingerprint :: Fingerprint
138+ , MonitorRef
173− , messageEncoding :: BSL.ByteString
139+ , MonitorReply(..)
174− }
140+ , DiedReason(..)
176−-- | Local nodes
142+ , ProcessSignal(..)
177−data LocalNode = LocalNode
143+ , createMessage
178− { localNodeId :: NodeId
144+ , messageToPayload
179− , localEndPoint :: NT.EndPoint
145+ , payloadToMessage
180− , localState :: MVar LocalNodeState
146+ , payloadToId
181− , localCtrlChan :: Chan NCMsg
147+ , idToPayload
182− }
148+ , localPidCounter
183−
149+ , localPidUnique
184−-- | Local node state
150+ , localProcessWithId
185−data LocalNodeState = LocalNodeState
151+ , localMonitorCounter
186− { _localProcesses :: Map LocalProcessId LocalProcess
152+ , connectionTo
187− , _localPidCounter :: Int32
153+ )
188− , _localPidUnique :: Int32
189− , _localMonitorCounter :: Int32
192−-- | Processes running on our local node
193−data LocalProcess = LocalProcess
194− { processQueue :: CQueue Message
195− , processNode :: LocalNode
196− , processId :: ProcessId
197− , processState :: MVar LocalProcessState
200−-- | Local process state
201−data LocalProcessState = LocalProcessState
202− { _connections :: Map ProcessId NT.Connection
205-- The Cloud Haskell 'Process' type
155-- The Cloud Haskell 'Process' type
206newtype Process a = Process { unProcess :: ReaderT LocalProcess IO a }
156newtype Process a = Process { unProcess :: ReaderT LocalProcess IO a }
@@ -269,209 +219,6 @@ expectTimeout timeout = do
269 dequeueMatching queue (Just timeout) ((== fp) . messageFingerprint)
219 dequeueMatching queue (Just timeout) ((== fp) . messageFingerprint)
270 return $ fmap (decode . messageEncoding) msg
220 return $ fmap (decode . messageEncoding) msg
272−--------------------------------------------------------------------------------
273−-- The node controller --
274−--------------------------------------------------------------------------------
277− Monitor ProcessId MonitorRef
278− | Died Identifier DiedReason
280−type MonitorRef = Int32
282−data MonitorReply = ProcessDied MonitorRef ProcessId DiedReason
287− | DiedException String -- TODO: would prefer SomeException instead of String, but exceptions don't implement Binary
293−type Identifier = Either ProcessId NodeId
296− { ctrlMsgSender :: Identifier
297− , ctrlMsgSignal :: ProcessSignal
300−data NCState = NCState
301− { -- Mapping from remote processes to linked local processes
302− _ncLinks :: Map NodeId (Map LocalProcessId (Set ProcessId))
303− -- Mapping from remote processes to monitoring local processes
304− , _ncMons :: Map NodeId (Map LocalProcessId (Map ProcessId (Set MonitorRef)))
305− -- The node controller maintains its own set of connections
306− -- TODO: still not convinced that this is correct
307− , _ncConns :: Map Identifier NT.Connection
310−destNid :: ProcessSignal -> Maybe NodeId
311−destNid (Monitor pid _) = Just $ processNodeId pid
312−destNid (Died (Right _) _) = Nothing
313−destNid (Died (Left _pid) _) = fail "destNid: TODO"
315−initNCState :: NCState
316−initNCState = NCState
317− { _ncLinks = Map.empty
318− , _ncMons = Map.empty
319− , _ncConns = Map.empty
322−newtype NC a = NC { unNC :: ReaderT LocalNode (StateT NCState IO) a }
323− deriving (Functor, Monad, MonadIO, MonadState NCState, MonadReader LocalNode)
325−nodeController :: NC ()
326−nodeController = forever $ do
328− msg <- liftIO $ readChan (localCtrlChan node)
330− -- Forward the message if appropriate
331− case destNid (ctrlMsgSignal msg) of
332− Just nid' | nid' /= localNodeId node -> ncSendCtrlMsg nid' msg
337−ctrlSendTo :: Identifier -> [BSS.ByteString] -> NC ()
338−ctrlSendTo them payload = do
339− mConn <- ncConnTo them
340− didSend <- case mConn of
342− didSend <- liftIO $ NT.send conn payload
344− Left _ -> return False
345− Right () -> return True
349− -- [Unified: Table 9, rule node_disconnect]
351− liftIO . writeChan (localCtrlChan node) $ NCMsg
352− { ctrlMsgSender = them
353− , ctrlMsgSignal = Died them DiedDisconnect
356−ncSendCtrlMsg :: NodeId -> NCMsg -> NC ()
357−ncSendCtrlMsg dest = ctrlSendTo (Right dest) . BSL.toChunks . encode
359−ncSendLocal :: LocalProcessId -> Message -> NC ()
360−ncSendLocal lpid msg = do
363− mProc <- withMVar (localState node) $ return . (^. localProcessWithId lpid)
364− -- By [Unified: table 6, rule missing_process] messages to dead processes
365− -- can silently be dropped
366− forM_ mProc $ \proc -> enqueue (processQueue proc) msg
368−ncConnTo :: Identifier -> NC (Maybe NT.Connection)
370− mConn <- gets (^. ncConnFor them)
372− Just conn -> return (Just conn)
373− Nothing -> ncCreateConnTo them
375−ncCreateConnTo :: Identifier -> NC (Maybe NT.Connection)
376−ncCreateConnTo them = do
378− mConn <- liftIO $ NT.connect (localEndPoint node)
381− NT.defaultConnectHints
384− didSend <- liftIO $ NT.send conn firstMsg
389− modify $ ncConnFor them ^= Just conn
394− (addr, firstMsg) = case them of
395− Left pid -> ( nodeAddress (processNodeId pid)
396− , idToPayload (Just $ processLocalId pid)
398− Right nid -> ( nodeAddress nid
399− , idToPayload Nothing
403−-- [Unified: ncEffect]
404−ncEffect :: NCMsg -> NC ()
406−-- [Unified: Table 10]
407−ncEffect (NCMsg (Left from) (Monitor them ref)) = do
410− if processNodeId them /= localNodeId node
412− else liftIO . withMVar (localState node) $
413− return . isJust . (^. localProcessWithId (processLocalId them))
414− let localFrom = processNodeId from == localNodeId node
415− case (shouldLink, localFrom) of
416− (True, _) -> -- [Unified: first rule]
417− modify $ ncMonsFor them from ^: Set.insert ref
418− (False, True) -> -- [Unified: second rule]
419− ncSendLocal (processLocalId from) . createMessage $
420− ProcessDied ref them DiedNoProc
421− (False, False) -> -- [Unified: third rule]
422− ncSendCtrlMsg (processNodeId from) NCMsg
423− { ctrlMsgSender = Right (localNodeId node)
424− , ctrlMsgSignal = Died (Left them) DiedNoProc
427−ncEffect (NCMsg (Right _) (Monitor _ _)) =
428− error "Monitor message from a node?"
430−-- [Unified: Table 12, bottom rule]
431−ncEffect (NCMsg _from (Died (Right nid) reason)) = do
433− links <- gets (^. ncLinksForNode nid)
434− mons <- gets (^. ncMonsForNode nid)
436− forM_ (Map.toList links) $ \(_them, _uss) ->
437− fail "ncEffect: linking not implemented"
439− forM_ (Map.toList mons) $ \(them, uss) ->
440− forM_ (Map.toList uss) $ \(us, refs) ->
441− -- We only need to notify local processes
442− when (processNodeId us == localNodeId node) $ do
443− let lpid = processLocalId us
444− rpid = ProcessId nid them
445− forM_ refs $ \ref ->
446− ncSendLocal lpid . createMessage $ ProcessDied ref rpid reason
448− modify $ (ncLinksForNode nid ^= Map.empty)
449− . (ncMonsForNode nid ^= Map.empty)
451−-- [Unified: Table 12, top rule]
452−ncEffect (NCMsg _from (Died (Left pid) reason)) = do
454− links <- gets (^. ncLinksForProcess pid)
455− mons <- gets (^. ncMonsForProcess pid)
457− forM_ links $ \_us ->
458− fail "ncEffect: linking not implemented"
460− forM_ (Map.toList mons) $ \(us, refs) ->
461− forM_ refs $ \ref -> do
462− let msg = createMessage $ ProcessDied ref pid reason
463− if processNodeId us == localNodeId node
465− ncSendLocal (processLocalId us) msg
467− ncSendCtrlMsg (processNodeId us) NCMsg
468− { ctrlMsgSender = Right (localNodeId node) -- TODO: why the change in sender? How does that affect 'reconnect' semantics?
469− , ctrlMsgSignal = Died (Left pid) reason
472− modify $ (ncLinksForProcess pid ^= Set.empty)
473− . (ncMonsForProcess pid ^= Map.empty)
475--------------------------------------------------------------------------------
222--------------------------------------------------------------------------------
476-- Initialization --
223-- Initialization --
477--------------------------------------------------------------------------------
224--------------------------------------------------------------------------------
@@ -495,8 +242,7 @@ newLocalNode transport = do
495 , localState = state
242 , localState = state
496 , localCtrlChan = ctrlChan
243 , localCtrlChan = ctrlChan
498− void . forkIO $ evalStateT (runReaderT (unNC nodeController) node)
245+ void . forkIO $ runNodeController node
500 void . forkIO $ handleIncomingMessages node
246 void . forkIO $ handleIncomingMessages node
501 return node
247 return node
@@ -658,37 +404,6 @@ sendBinary them conn payload = do
658sendMessage :: ProcessId -> NT.Connection -> Message -> Process ()
404sendMessage :: ProcessId -> NT.Connection -> Message -> Process ()
659sendMessage them conn = sendBinary them conn . messageToPayload
405sendMessage them conn = sendBinary them conn . messageToPayload
661−messageToPayload :: Message -> [BSS.ByteString]
662−messageToPayload (Message fp enc) = encodeFingerprint fp : BSL.toChunks enc
664−payloadToMessage :: [BSS.ByteString] -> Message
665−payloadToMessage payload = Message fp msg
667− (encFp, msg) = BSL.splitAt (fromIntegral sizeOfFingerprint)
668− $ BSL.fromChunks payload
669− fp = decodeFingerprint . BSS.concat . BSL.toChunks $ encFp
671−-- | The first message we send across a connection to indicate the intended
672−-- recipient. Pass Nothing for the remote node controller
673−idToPayload :: Maybe LocalProcessId -> [BSS.ByteString]
674−idToPayload Nothing = [ NTI.encodeInt32 (0 :: Int) ]
675−idToPayload (Just lpid) = [ NTI.encodeInt32 (1 :: Int)
676− , NTI.encodeInt32 (lpidCounter lpid)
677− , NTI.encodeInt32 (lpidUnique lpid)
680−-- | Inverse of 'idToPayload'
681−payloadToId :: [BSS.ByteString] -> Maybe LocalProcessId
682−payloadToId bss = let (bs1, bss') = BSS.splitAt 4 . BSS.concat $ bss
683− (bs2, bs3) = BSS.splitAt 4 bss' in
684− case NTI.decodeInt32 bs1 :: Int of
686− 1 -> Just LocalProcessId
687− { lpidCounter = NTI.decodeInt32 bs2
688− , lpidUnique = NTI.decodeInt32 bs3
690− _ -> fail "payloadToId"
692remoteProcessFailed :: LocalNode -> ProcessId -> IO ()
407remoteProcessFailed :: LocalNode -> ProcessId -> IO ()
693remoteProcessFailed node them = do
408remoteProcessFailed node them = do
694 -- [Unified: Table 9 rule node_disconnect]
409 -- [Unified: Table 9 rule node_disconnect]
@@ -698,9 +413,6 @@ remoteProcessFailed node them = do
698 , ctrlMsgSignal = Died nid DiedDisconnect
413 , ctrlMsgSignal = Died nid DiedDisconnect
701−createMessage :: Serializable a => a -> Message
702−createMessage a = Message (fingerprint a) (encode a)
704getMonitorRef :: Process MonitorRef
416getMonitorRef :: Process MonitorRef
705getMonitorRef = do
417getMonitorRef = do
706 node <- processNode <$> ask
418 node <- processNode <$> ask
@@ -712,101 +424,3 @@ getMonitorRef = do
712-- This is most definitely NOT exported
424-- This is most definitely NOT exported
713runLocalProcess :: LocalProcess -> Process a -> IO a
425runLocalProcess :: LocalProcess -> Process a -> IO a
714runLocalProcess lproc proc = runReaderT (unProcess proc) lproc
426runLocalProcess lproc proc = runReaderT (unProcess proc) lproc
716−--------------------------------------------------------------------------------
717−-- Binary instances --
718−--------------------------------------------------------------------------------
720−instance Binary LocalProcessId where
721− put lpid = put (lpidUnique lpid) >> put (lpidCounter lpid)
722− get = LocalProcessId <$> get <*> get
724−instance Binary ProcessId where
725− put pid = put (processNodeId pid) >> put (processLocalId pid)
726− get = ProcessId <$> get <*> get
728−instance Binary MonitorReply where
729− put (ProcessDied ref pid reason) = put ref >> put pid >> put reason
730− get = ProcessDied <$> get <*> get <*> get
732−instance Binary NCMsg where
733− put msg = put (ctrlMsgSender msg) >> put (ctrlMsgSignal msg)
734− get = NCMsg <$> get <*> get
736−instance Binary ProcessSignal where
737− put (Monitor pid ref) = putWord8 0 >> put pid >> put ref
738− put (Died who reason) = putWord8 1 >> put who >> put reason
742− 0 -> Monitor <$> get <*> get
743− 1 -> Died <$> get <*> get
744− _ -> fail "ProcessSignal.get: invalid"
746−instance Binary DiedReason where
747− put DiedNormal = putWord8 0
748− put (DiedException e) = putWord8 1 >> put e
749− put DiedDisconnect = putWord8 2
750− put DiedNodeDown = putWord8 3
751− put DiedNoProc = putWord8 4
755− 0 -> return DiedNormal
756− 1 -> DiedException <$> get
757− 2 -> return DiedDisconnect
758− 3 -> return DiedNodeDown
759− 4 -> return DiedNoProc
760− _ -> fail "DiedReason.get: invalid"
762−--------------------------------------------------------------------------------
764−--------------------------------------------------------------------------------
766−localProcesses :: Accessor LocalNodeState (Map LocalProcessId LocalProcess)
767−localProcesses = accessor _localProcesses (\procs st -> st { _localProcesses = procs })
769−localPidCounter :: Accessor LocalNodeState Int32
770−localPidCounter = accessor _localPidCounter (\ctr st -> st { _localPidCounter = ctr })
772−localPidUnique :: Accessor LocalNodeState Int32
773−localPidUnique = accessor _localPidUnique (\unq st -> st { _localPidUnique = unq })
775−localMonitorCounter :: Accessor LocalNodeState Int32
776−localMonitorCounter = accessor _localMonitorCounter (\ctr st -> st { _localMonitorCounter = ctr })
778−localProcessWithId :: LocalProcessId -> Accessor LocalNodeState (Maybe LocalProcess)
779−localProcessWithId lpid = localProcesses >>> DAC.mapMaybe lpid
781−connections :: Accessor LocalProcessState (Map ProcessId NT.Connection)
782−connections = accessor _connections (\conns st -> st { _connections = conns })
784−connectionTo :: ProcessId -> Accessor LocalProcessState (Maybe NT.Connection)
785−connectionTo pid = connections >>> DAC.mapMaybe pid
787−ncLinks :: Accessor NCState (Map NodeId (Map LocalProcessId (Set ProcessId)))
788−ncLinks = accessor _ncLinks (\links st -> st { _ncLinks = links })
790−ncMons :: Accessor NCState (Map NodeId (Map LocalProcessId (Map ProcessId (Set MonitorRef))))
791−ncMons = accessor _ncMons (\mons st -> st { _ncMons = mons })
793−ncConns :: Accessor NCState (Map Identifier NT.Connection)
794−ncConns = accessor _ncConns (\conns st -> st { _ncConns = conns })
796−ncConnFor :: Identifier -> Accessor NCState (Maybe NT.Connection)
797−ncConnFor them = ncConns >>> DAC.mapMaybe them
799−ncLinksForNode :: NodeId -> Accessor NCState (Map LocalProcessId (Set ProcessId))
800−ncLinksForNode nid = ncLinks >>> DAC.mapDefault Map.empty nid
802−ncMonsForNode :: NodeId -> Accessor NCState (Map LocalProcessId (Map ProcessId (Set MonitorRef)))
803−ncMonsForNode nid = ncMons >>> DAC.mapDefault Map.empty nid
805−ncLinksForProcess :: ProcessId -> Accessor NCState (Set ProcessId)
806−ncLinksForProcess pid = ncLinksForNode (processNodeId pid) >>> DAC.mapDefault Set.empty (processLocalId pid)
808−ncMonsForProcess :: ProcessId -> Accessor NCState (Map ProcessId (Set MonitorRef))
809−ncMonsForProcess pid = ncMonsForNode (processNodeId pid) >>> DAC.mapDefault Map.empty (processLocalId pid)
811−ncMonsFor :: ProcessId -> ProcessId -> Accessor NCState (Set MonitorRef)
812−ncMonsFor them us = ncMonsForProcess them >>> DAC.mapDefault Set.empty us