git.iko.soy/distributed-process
on master help sign in
Build up an initial execution environment for agents
commitdd2ddb502d6425f81f402a696284c2b38eb818dd authorTim Watson <watson.timothy@gmail.com> dateWed 4 Sep 2013, 22:57 (13 years ago) parentfb085c6
9 files changed+435−66
.gitignore+1 distributed-process.cabal+5−4 src/Control/Distributed/Process/Internal/Types.hs+2−1 src/Control/Distributed/Process/Management.hs+118−12 src/Control/Distributed/Process/Management/Agent.hs+13−21 src/Control/Distributed/Process/Management/Bus.hs−23 src/Control/Distributed/Process/Management/Table.hs+219 src/Control/Distributed/Process/Management/Types.hs+72 src/Control/Distributed/Process/Node.hs+5−5
.gitignore+1
@@ -11,4 +11,5 @@ dist/
1111*.ps
1212*.swp
1313cabal-dev
14+cabal-dev*
1415.DS_Store
distributed-process.cabal+5−4
@@ -70,7 +70,6 @@ Library
7070 Control.Distributed.Process.Serializable,
7171 Control.Distributed.Process.UnsafePrimitives
7272 Other-modules: Control.Distributed.Process.Internal.Primitives,
73− Control.Distributed.Process.Management.Bus,
7473 Control.Distributed.Process.Internal.Spawn,
7574 Control.Distributed.Process.Internal.Primitives,
7675 Control.Distributed.Process.Internal.CQueue,
@@ -80,10 +79,12 @@ Library
8079 Control.Distributed.Process.Internal.StrictMVar,
8180 Control.Distributed.Process.Internal.WeakTQueue,
8281 Control.Distributed.Process.Internal.StrictContainerAccessors,
83− Control.Distributed.Process.Management.Trace.Types,
82+ Control.Distributed.Process.Management.Bus,
83+ Control.Distributed.Process.Management.Table,
8484 Control.Distributed.Process.Management.Trace.Primitives,
85− Control.Distributed.Process.Management.Trace.Tracer,
86− Control.Distributed.Process.Management.Trace.Remote
85+ Control.Distributed.Process.Management.Trace.Remote,
86+ Control.Distributed.Process.Management.Trace.Types,
87+ Control.Distributed.Process.Management.Trace.Tracer
8788 Extensions: RankNTypes,
8889 ScopedTypeVariables,
8990 FlexibleInstances,
src/Control/Distributed/Process/Internal/Types.hs+2−1
@@ -99,6 +99,7 @@ import Control.Exception (Exception)
9999import Control.Concurrent (ThreadId)
100100import Control.Concurrent.Chan (Chan)
101101import Control.Concurrent.STM (STM)
102+import Control.Concurrent.STM.TChan (TChan)
102103import qualified Network.Transport as NT (EndPoint, EndPointAddress, Connection)
103104import Control.Applicative (Applicative, Alternative, (<$>), (<*>))
104105import Control.Monad.Reader (MonadReader(..), ReaderT, runReaderT)
@@ -214,7 +215,7 @@ data MxEventBus =
214215 agent :: !ProcessId
215216 , tracer :: !Tracer
216217 , evbuss :: !(Weak (CQueue Message))
217− , mxNew :: !((Message -> Process ()) -> IO ProcessId)
218+ , mxNew :: !((TChan Message -> Process ()) -> IO ProcessId)
218219-- , mxReg :: !(StrictMVar (Map MxAgentId ))
219220 }
220221
src/Control/Distributed/Process/Management.hs+118−12
@@ -38,9 +38,38 @@
3838--
3939--
4040-----------------------------------------------------------------------------
41−module Control.Distributed.Process.Management where
41+module Control.Distributed.Process.Management
42+ (
43+ MxAction()
44+ , MxAgentId
45+ -- * Firing /mx events/
46+ , mxNotify
47+ -- * Constructing Agents
48+ , mxAgent
49+ , mxAgentDeactivate
50+ , mxAgentReady
51+ , mxAgentPublish
52+ , mxSetLocal
53+ , mxGetLocal
54+ -- * Lifting
55+ , liftP
56+ -- * Management Data API
57+ , mxPublish
58+ , mxSet
59+ , mxGet
60+ ) where
4261
4362import Control.Applicative ((<$>))
63+import Control.Concurrent.STM (atomically)
64+import Control.Concurrent.STM.TChan
65+ ( readTChan
66+ )
67+import Control.Distributed.Process.Internal.Primitives
68+ ( newChan
69+ , nsend
70+ , receiveWait
71+ , matchChan
72+ )
4473import Control.Distributed.Process.Internal.Types
4574 ( Process
4675 , ProcessId
@@ -51,9 +80,28 @@ import Control.Distributed.Process.Internal.Types
5180 , unsafeCreateUnencodedMessage
5281 )
5382import Control.Distributed.Process.Management.Bus (publishEvent)
83+import qualified Control.Distributed.Process.Management.Table as Table
84+import Control.Distributed.Process.Management.Types
85+ ( MxAgentId(..)
86+ , MxAgent(..)
87+ , MxAgentState(..)
88+ , MxAgentStart(..)
89+ )
5490import Control.Distributed.Process.Serializable (Serializable)
5591import Control.Monad.IO.Class (liftIO)
5692import Control.Monad.Reader (ask)
93+import qualified Control.Monad.State as ST
94+ ( MonadState
95+ , StateT
96+ , get
97+ , modify
98+ , lift
99+ , runStateT
100+ )
101+
102+data MxAction =
103+ MxAgentDeactivate !String
104+ | MxAgentReady
57105
58106-- | Publishes an arbitrary @Serializable@ message to the management event bus.
59107-- Note that /no attempt is made to force the argument/, therefore it is very
@@ -65,20 +113,78 @@ mxNotify msg = do
65113 bus <- localEventBus . processNode <$> ask
66114 liftIO $ publishEvent bus $ unsafeCreateUnencodedMessage msg
67115
68−mxAgent :: (Message -> Process ()) -> Process ProcessId
69−mxAgent handler = do
70− node <- processNode <$> ask
71− pid <- liftIO $ mxNew (localEventBus node) handler
72− -- registerAgent pid
73− return pid
116+-- | Publish an arbitrary @Message@ as a property in the management database.
117+--
118+-- For publishing @Serializable@ data, use 'mxSet' instead. For publishing
119+-- properties efficiently within an agent, use 'mxAgentPublish' instead,
120+-- since it will be more efficient in that context.
121+mxPublish :: MxAgentId -> String -> Message -> Process ()
122+mxPublish a k v = Table.set k v (Table.MxForAgent a)
123+
124+-- | Sets an arbitrary @Serializable@ datum against a key in the management
125+-- database. Note that /no attempt is made to force the argument/, therefore
126+-- it is very important that you do not pass unevaluated thunks that might
127+-- crash some other, arbitrary process (or management agent!) that obtains
128+-- and attempts to force the value later on.
129+--
130+mxSet :: Serializable a => MxAgentId -> String -> a -> Process ()
131+mxSet mxId key msg =
132+ Table.set key (unsafeCreateUnencodedMessage msg) (Table.MxForAgent mxId)
74133
75−mxPublish = undefined
134+-- | Fetches a property from the management database for the given key.
135+-- If the property is not set, or does not match the expected type when
136+-- typechecked (at runtime), returns @Nothing@.
137+mxGet :: Serializable a => MxAgentId -> String -> Process (Maybe a)
138+mxGet mxId = Table.fetch (Table.MxForAgent mxId)
76139
77−mxSet = undefined
140+--------------------------------------------------------------------------------
141+-- API for writing user defined management extensions (i.e., agents) --
142+--------------------------------------------------------------------------------
78143
79−mxGet = undefined
144+mxAgentPublish = undefined
145+
146+mxAgentDeactivate :: forall s. String -> MxAgent s MxAction
147+mxAgentDeactivate = return . MxAgentDeactivate
148+
149+mxAgentReady :: forall s. MxAgent s MxAction
150+mxAgentReady = return MxAgentReady
151+
152+liftP :: Process a -> MxAgent s a
153+liftP p = MxAgent $ ST.lift p
154+
155+mxSetLocal :: s -> MxAgent s ()
156+mxSetLocal s = ST.modify $ \st -> st { mxLocalState = s }
157+
158+mxGetLocal :: MxAgent s s
159+mxGetLocal = ST.get >>= return . mxLocalState
160+
161+-- | Activates a new agent.
162+--
163+mxAgent :: MxAgentId
164+ -> s
165+ -> (Message -> MxAgent s MxAction)
166+ -> Process ProcessId
167+mxAgent mxId initState handler = do
168+ node <- processNode <$> ask
169+ pid <- liftIO $ mxNew (localEventBus node) $ start
170+ return pid
171+ where
172+ start chan = do
173+ (sp, rp) <- newChan
174+ nsend Table.mxTableCoordinator (MxAgentStart sp mxId)
175+ tablePid <- receiveWait [ matchChan rp (\(p :: ProcessId) -> return p) ]
176+ runAgent handler chan $ MxAgentState mxId tablePid initState
80177
81−mxIncrement = undefined
178+ runAgent h c s = do
179+ msg <- (liftIO $ atomically $ readTChan c)
180+ (action, state) <- runAgentST s $ h msg
181+ case action of
182+ MxAgentReady -> runAgent h c state
183+ MxAgentDeactivate _ -> {- TODO: log r -} return ()
184+-- MxAgentBecome h' -> runAgent h' c state
82185
83−mxDecrement = undefined
186+ runAgentST :: MxAgentState s
187+ -> MxAgent s MxAction
188+ -> Process (MxAction, MxAgentState s)
189+ runAgentST state proc = ST.runStateT (unAgent proc) state
84190
src/Control/Distributed/Process/Management/Agent.hs+13−21
@@ -8,7 +8,6 @@ import Control.Concurrent.STM (atomically)
88import Control.Concurrent.STM.TChan
99 ( TChan
1010 , newBroadcastTChanIO
11− , readTChan
1211 , writeTChan
1312 , dupTChan
1413 )
@@ -23,39 +22,36 @@ import Control.Distributed.Process.Internal.CQueue
2322 ( enqueueSTM
2423 , CQueue
2524 )
25+import Control.Distributed.Process.Management.Types
26+ ( Fork
27+ )
2628import Control.Distributed.Process.Management.Trace.Tracer
2729 ( traceController
2830 )
2931import Control.Distributed.Process.Internal.Types
3032 ( Process
31− , SendPort
3233 , Message
33− , MxEventBus(..)
3434 , Tracer(..)
35− , LocalNode(..)
3635 , LocalProcess(..)
3736 , ProcessId
3837 , forever'
3938 )
4039import Control.Exception (AsyncException(ThreadKilled), SomeException)
41−import qualified Control.Exception as Ex (catch, finally)
4240import Control.Monad (void)
4341import Control.Monad.IO.Class (liftIO)
4442import Control.Monad.Reader (ask)
45−import GHC.Weak (Weak(Weak), deRefWeak)
46−import System.Environment (getEnv)
43+import GHC.Weak (Weak, deRefWeak)
44+
45+--------------------------------------------------------------------------------
46+-- Agent Controller Implementation --
47+--------------------------------------------------------------------------------
4748
48−-- | Gross though it is, this synonym represents a function
49−-- used to forking new processes, which has to be passed as a HOF
50−-- when calling mxAgentController, since there's no other way to
51−-- avoid a circular dependency with Node.hs
52−type Fork = (Process () -> IO ProcessId)
5349
5450-- | A triple containing a configured tracer, weak pointer to the
5551-- agent controller's mailbox (CQueue) and an expression used to
5652-- instantiate new agents on the current node.
5753type AgentConfig =
58− (Tracer, Weak (CQueue Message), ((Message -> Process ()) -> IO ProcessId))
54+ (Tracer, Weak (CQueue Message), ((TChan Message -> Process ()) -> IO ProcessId))
5955
6056-- | Starts a management agent for the current node. The agent process
6157-- must not crash or be killed, so we generally avoid publishing its
@@ -75,11 +71,11 @@ mxAgentController :: Fork
7571 -> MVar AgentConfig
7672 -> Process ()
7773mxAgentController forkProcess mv = do
78− node <- processNode <$> ask
74+ -- node <- processNode <$> ask
7975 trc <- liftIO $ startTracing forkProcess
8076 sigbus <- liftIO $ newBroadcastTChanIO
8177 weakQueue <- processWeakQ <$> ask
82− liftIO $ putMVar mv (trc, weakQueue, mxAgent forkProcess sigbus)
78+ liftIO $ putMVar mv (trc, weakQueue, mxStartAgent forkProcess sigbus)
8379 go sigbus trc forkProcess
8480 where
8581 go bus tracer fork = forever' $ do
@@ -119,12 +115,8 @@ mxAgentController forkProcess mv = do
119115 liftIO $ atomically $ enqueueSTM q msg >> writeTChan ch msg
120116
121117-- | Forks a new process in which an mxAgent is run.
122−mxAgent :: Fork -> TChan Message -> (Message -> Process ()) -> IO ProcessId
123−mxAgent fork chan handler = (atomically (dupTChan chan)) >>= fork . run handler
124− where
125− run :: (Message -> Process ()) -> TChan Message -> Process ()
126− run handler' chan' = do
127− (liftIO $ atomically $ readTChan chan') >>= handler' >> run handler' chan'
118+mxStartAgent :: Fork -> TChan Message -> (TChan Message -> Process ()) -> IO ProcessId
119+mxStartAgent fork chan handler = (atomically (dupTChan chan)) >>= fork . handler
128120
129121startTracing :: Fork -> IO Tracer
130122startTracing forkProcess = do
src/Control/Distributed/Process/Management/Bus.hs−23
@@ -21,29 +21,6 @@ import Network.Transport
2121 )
2222import System.Mem.Weak (deRefWeak)
2323
24−data MxEvent =
25− MxSpawned ProcessId
26− -- ^ fired whenever a local process is spawned
27− | MxRegistered ProcessId String
28− -- ^ fired whenever a process/name is registered (locally)
29− | MxUnRegistered ProcessId String
30− -- ^ fired whenever a process/name is unregistered (locally)
31− | MxProcessDied ProcessId DiedReason
32− -- ^ fired whenever a process dies
33− | MxNodeDied NodeId DiedReason
34− -- ^ fired whenever a node /dies/ (i.e., the connection is broken/disconnected)
35− | MxSent ProcessId ProcessId Message
36− -- ^ fired whenever a message is sent from a local process
37− | MxReceived ProcessId Message
38− -- ^ fired whenever a message is received by a local process
39− | MxConnected ConnectionId EndPointAddress
40− -- ^ fired when a network-transport connection is first established
41− | MxDisconnected ConnectionId
42− -- ^ fired when a network-transport connection is broken/disconnected
43− | MxUser Message
44− -- ^ a user defined trace event (see 'traceMessage')
45− deriving (Typeable, Show)
46−
4724publishEvent :: MxEventBus -> Message -> IO ()
4825publishEvent MxEventBusInitialising _ = return ()
4926publishEvent (MxEventBus _ _ wqRef _) msg = do
src/Control/Distributed/Process/Management/Table.hsnew file+219
@@ -0,0 +1,219 @@
1+{-# LANGUAGE DeriveGeneric #-}
2+{-# LANGUAGE RecordWildCards #-}
3+
4+module Control.Distributed.Process.Management.Table
5+ ( MxTableRequest(..)
6+ , MxTableId(..)
7+ , mxTableCoordinator
8+ , startTableCoordinator
9+ , delete
10+ , purge
11+ , clear
12+ , set
13+ , get
14+ , fetch
15+ ) where
16+
17+import Control.Distributed.Process.Internal.Primitives
18+ ( receiveWait
19+ , receiveChan
20+ , match
21+ , matchAny
22+ , matchIf
23+ , matchChan
24+ , send
25+ , nsend
26+ , sendChan
27+ , getSelfPid
28+ , link
29+ , monitor
30+ , unwrapMessage
31+ , newChan
32+ , withMonitor
33+ )
34+import Control.Distributed.Process.Internal.Types
35+ ( Process
36+ , ProcessId
37+ , ProcessMonitorNotification(..)
38+ , SendPort
39+ , ReceivePort
40+ , Message
41+ , unsafeCreateUnencodedMessage
42+ )
43+import Control.Distributed.Process.Management.Types
44+ ( MxTableId(..)
45+ , MxAgentId(..)
46+ , MxAgentStart(..)
47+ , Fork)
48+import Control.Distributed.Process.Serializable (Serializable)
49+import Control.Monad.IO.Class (liftIO)
50+import Data.Accessor (Accessor, accessor, (^=), (^:))
51+import Data.Binary (Binary)
52+import Data.Map.Strict (Map)
53+import qualified Data.Map.Strict as Map
54+import Data.Typeable (Typeable)
55+
56+import GHC.Generics
57+
58+-- An extremely lightweight shared Map implementation, for use
59+-- by /management agents/ and their cohorts. Each agent is assigned
60+-- a table, into which any serializable @Message@ can be inserted.
61+-- Data are inserted, removed and searched for via their key, which
62+-- is a string. Tables can be purged, values can be set, fetched or
63+-- cleared/removed.
64+--
65+
66+data MxTableRequest =
67+ Delete
68+ | Purge
69+ | Clear !String
70+ | Set !String !Message
71+ | Get !String !(SendPort (Maybe Message)) -- see [note: un-typed send port]
72+ deriving (Typeable, Generic)
73+instance Binary MxTableRequest where
74+
75+data MxTableState = MxTableState
76+ {
77+ _name :: !String
78+ , _entries :: !(Map String Message)
79+ }
80+
81+type MxTables = Map MxAgentId ProcessId
82+
83+mxTableCoordinator :: String
84+mxTableCoordinator = "mx.table.coordinator"
85+
86+delete :: MxTableId -> Process ()
87+delete = sendReq Delete
88+
89+purge :: MxTableId -> Process ()
90+purge = sendReq Purge
91+
92+clear :: String -> MxTableId -> Process ()
93+clear k = sendReq (Clear k)
94+
95+set :: String -> Message -> MxTableId -> Process ()
96+set k v = sendReq (Set k v)
97+
98+fetch :: forall a. (Serializable a)
99+ => MxTableId
100+ -> String
101+ -> Process (Maybe a)
102+fetch (MxForPid pid) key = get pid key
103+fetch mxId@(MxForAgent _) key = do
104+ (sp, rp) <- newChan :: Process (SendPort (Maybe Message),
105+ ReceivePort (Maybe Message))
106+ sendReq (Get key sp) mxId
107+ receiveChan rp >>= maybe (return Nothing)
108+ (unwrapMessage :: Message -> Process (Maybe a))
109+
110+-- [note: un-typed send port]
111+-- Here, fetch uses a typed channel over a raw Message to obtain
112+-- its result, so type checking is deferred until receipt and will
113+-- be handled in the caller's thread. This is necessary because
114+-- the server portion of the code knows nothing about the types
115+-- involved, nor should it, since these tables can be used to
116+-- store arbitrary serializable data.
117+
118+get :: forall a. (Serializable a)
119+ => ProcessId
120+ -> String
121+ -> Process (Maybe a)
122+get pid key = do
123+ safeFetch pid key >>= maybe (return Nothing)
124+ (unwrapMessage :: Message -> Process (Maybe a))
125+
126+safeFetch :: ProcessId -> String -> Process (Maybe Message)
127+safeFetch pid key = do
128+ (sp, rp) <- newChan
129+ send pid $ Get key sp
130+ withMonitor pid $ do
131+ receiveWait [
132+ matchChan rp return
133+ , matchIf (\(ProcessMonitorNotification _ pid' _) -> pid' == pid)
134+ (\_ -> return $ Just (unsafeCreateUnencodedMessage ()))
135+ ]
136+
137+sendReq :: MxTableRequest -> MxTableId -> Process ()
138+sendReq req tid = (resolve tid) req
139+
140+resolve :: Serializable a => MxTableId -> (a -> Process ())
141+resolve (MxForAgent agent) = \msg -> nsend mxTableCoordinator (agent, msg)
142+resolve (MxForPid pid) = \msg -> send pid msg
143+
144+startTableCoordinator :: Fork -> Process ()
145+startTableCoordinator fork = run Map.empty
146+ where
147+
148+ -- TODO: rethink this design. routing via the
149+ -- coordinator will be a publishing bottle neck,
150+ -- plus the code in 'fetch' really requires a
151+ -- pid to send to, so that'll not work via an
152+ -- intermediary anyway
153+
154+ run :: MxTables -> Process ()
155+ run tables =
156+ receiveWait [
157+ match (\(MxAgentStart ch agent) ->
158+ lookupAgent tables agent >>= \(p, t) -> do
159+ sendChan ch p >> return t)
160+ , match (\(agent, Get k sp) -> do
161+ lookupAgent tables agent >>= \(p, t) -> do
162+ safeFetch p k >>= sendChan sp >> return t)
163+ , match $ handleRequest tables
164+ ] >>= run
165+
166+ handleRequest :: MxTables
167+ -> (MxAgentId, MxTableRequest)
168+ -> Process MxTables
169+ handleRequest tables' (agent, req) =
170+ lookupAgent tables' agent >>= \(p, t) -> do send p req >> return t
171+
172+ lookupAgent :: MxTables -> MxAgentId -> Process (ProcessId, MxTables)
173+ lookupAgent tables' agentId' =
174+ case Map.lookup agentId' tables' of
175+ Nothing -> launchNew agentId' tables'
176+ Just p -> return (p, tables')
177+
178+ launchNew :: MxAgentId
179+ -> MxTables
180+ -> Process (ProcessId, MxTables)
181+ launchNew mxId tblMap = do
182+ let initState = MxTableState { _name = (agentId mxId)
183+ , _entries = Map.empty
184+ }
185+ (pid, _) <- spawnSup $ tableHandler initState
186+ return $ (pid, Map.insert mxId pid tblMap)
187+
188+ spawnSup proc = do
189+ us <- getSelfPid
190+ -- we need to used that passed in "fork", in order to
191+ -- break an import cycle with Node.hs via the management
192+ -- agent, management API and the tracing modules
193+ them <- liftIO $ fork $ link us >> proc
194+ -- them <- spawnLocal nid $ link us >> proc
195+ ref <- monitor them
196+ return (them, ref)
197+
198+tableHandler :: MxTableState -> Process ()
199+tableHandler state = do
200+ ns <- receiveWait [
201+ match (\Delete -> return Nothing)
202+ , match (\Purge -> return $ Just $ (entries ^= Map.empty) $ state)
203+ , match (\(Clear k) -> return $ Just $ (entries ^: Map.delete k) $ state)
204+ , match (\(Set k v) -> return $ Just $ (entries ^: Map.insert k v) state)
205+ , match (\(Get k c) -> getEntry k c state >> return (Just state))
206+ ]
207+ case ns of
208+ Nothing -> return ()
209+ Just s' -> tableHandler s'
210+
211+getEntry :: String
212+ -> SendPort (Maybe Message)
213+ -> MxTableState
214+ -> Process ()
215+getEntry k m MxTableState{..} = sendChan m =<< return (Map.lookup k _entries)
216+
217+entries :: Accessor MxTableState (Map String Message)
218+entries = accessor _entries (\ls st -> st { _entries = ls })
219+
src/Control/Distributed/Process/Management/Types.hsnew file+72
@@ -0,0 +1,72 @@
1+{-# LANGUAGE DeriveGeneric #-}
2+module Control.Distributed.Process.Management.Types
3+ ( MxAgentId(..)
4+ , MxTableId(..)
5+ , MxAgentState(..)
6+ , MxAgent(..)
7+ , MxAgentStart(..)
8+ , Fork
9+ ) where
10+
11+import Control.Applicative (Applicative)
12+import Control.Distributed.Process.Internal.Types
13+ ( Process
14+ , ProcessId
15+ , Message
16+ , SendPort
17+ )
18+import Control.Monad.IO.Class (MonadIO)
19+import qualified Control.Monad.State as ST
20+ ( MonadState
21+ , StateT
22+ , get
23+ , lift
24+ , runStateT
25+ )
26+import Data.Binary (Binary)
27+import Data.Map.Strict (Map)
28+import Data.Typeable (Typeable)
29+import GHC.Generics
30+
31+-- | Gross though it is, this synonym represents a function
32+-- used to forking new processes, which has to be passed as a HOF
33+-- when calling mxAgentController, since there's no other way to
34+-- avoid a circular dependency with Node.hs
35+type Fork = (Process () -> IO ProcessId)
36+
37+-- | A newtype wrapper for an agent id (which is a string).
38+newtype MxAgentId = MxAgentId { agentId :: String }
39+ deriving (Typeable, Binary, Eq, Ord)
40+
41+data MxTableId =
42+ MxForAgent !MxAgentId
43+ | MxForPid !ProcessId
44+
45+data MxAgentState s = MxAgentState
46+ {
47+ mxAgentId :: !MxAgentId
48+ -- , mxBus :: !(TChan Message)
49+ , mxSharedTable :: !ProcessId
50+ , mxLocalState :: !s
51+ }
52+
53+newtype MxAgent s a =
54+ MxAgent
55+ {
56+ unAgent :: ST.StateT (MxAgentState s) Process a
57+ } deriving ( Functor
58+ , Monad
59+ , MonadIO
60+ , ST.MonadState (MxAgentState s)
61+ , Typeable
62+ , Applicative
63+ )
64+
65+data MxAgentStart = MxAgentStart
66+ {
67+ mxAgentTableChan :: SendPort ProcessId
68+ , mxAgentIdStart :: MxAgentId
69+ }
70+ deriving (Typeable, Generic)
71+instance Binary MxAgentStart where
72+
src/Control/Distributed/Process/Node.hs+5−5
@@ -56,7 +56,7 @@ import Control.Exception
5656 , Exception
5757 , throwTo
5858 )
59−import qualified Control.Exception as Exception (Handler(..), catch, catches, finally)
59+import qualified Control.Exception as Exception (Handler(..), catches, finally)
6060import Control.Concurrent (forkIO, myThreadId)
6161import Control.Distributed.Process.Internal.StrictMVar
6262 ( newMVar
@@ -188,7 +188,6 @@ import qualified Control.Distributed.Process.Internal.StrictContainerAccessors a
188188import Control.Distributed.Process.Management.Agent
189189 ( mxAgentController
190190 )
191−import System.Environment (getEnv)
192191import Unsafe.Coerce
193192
194193--------------------------------------------------------------------------------
@@ -251,8 +250,8 @@ startMxAgent node = do
251250 let fork = forkProcess node
252251 mv <- MVar.newEmptyMVar
253252 pid <- fork $ mxAgentController fork mv
254− (tracer, wqRef, mxNew) <- MVar.takeMVar mv
255− return node { localEventBus = (MxEventBus pid tracer wqRef mxNew) }
253+ (tracer', wqRef, mxNew') <- MVar.takeMVar mv
254+ return node { localEventBus = (MxEventBus pid tracer' wqRef mxNew') }
256255
257256startDefaultTracer :: LocalNode -> IO ()
258257startDefaultTracer node' = do
@@ -353,12 +352,13 @@ forkProcess node proc = modifyMVar (localState node) startProcess
353352 }
354353 return (tid', lproc)
355354
356−
357355 -- see note [tracer/forkProcess races]
358356 trace node (MxSpawned pid)
359357
360358 if lpidCounter lpid == maxBound
361359 then do
360+ -- TODO: this doesn't look right at all - how do we know
361+ -- that newUnique represents a process id that is available!?
362362 newUnique <- randomIO
363363 return ( (localProcessWithId lpid ^= Just lproc)
364364 . (localPidCounter ^= firstNonReservedProcessId)