git.iko.soy/distributed-process
on master help sign in
Start putting infrastructure in place for spawn
commit3850c692ca9691e8c042c2ff840e222c8062affe authorEdsko de Vries <edsko@edsko.net> dateTue 19 Jun 2012, 12:03 (14 years ago) parent5ab27fc
4 files changed+135−51
distributed-process/src/Control/Distributed/Process.hs+31−12 distributed-process/src/Control/Distributed/Process/Closure.hs+4−15 distributed-process/src/Control/Distributed/Process/Internal.hs+97−24 distributed-process/src/Control/Distributed/Process/Internal/NodeController.hs+3
distributed-process/src/Control/Distributed/Process.hs+31−12
@@ -43,6 +43,7 @@ module Control.Distributed.Process
4343 , closeLocalNode
4444 , catch
4545 , expectTimeout
46+ , spawnAsync
4647 ) where
4748
4849import Prelude hiding (catch)
@@ -56,7 +57,6 @@ import qualified Data.List as List (delete)
5657import Data.Set (Set)
5758import qualified Data.Set as Set (empty, insert, delete, member)
5859import Data.Foldable (forM_)
59−import Data.Typeable (Typeable)
6060import Control.Monad (void)
6161import Control.Monad.Reader (MonadReader(..), ReaderT, runReaderT)
6262import Control.Monad.IO.Class (MonadIO, liftIO)
@@ -104,6 +104,8 @@ import Control.Distributed.Process.Internal ( RemoteTable
104104 , LocalNodeState(..)
105105 , LocalProcess(..)
106106 , LocalProcessState(..)
107+ , Process(..)
108+ , Closure
107109 , Message(..)
108110 , MonitorRef(..)
109111 , MonitorNotification(..)
@@ -111,6 +113,7 @@ import Control.Distributed.Process.Internal ( RemoteTable
111113 , DidUnmonitor(..)
112114 , DidUnlink(..)
113115 , DiedReason(..)
116+ , SpawnRef(..)
114117 , NCMsg(..)
115118 , ProcessSignal(..)
116119 , createMessage
@@ -121,7 +124,8 @@ import Control.Distributed.Process.Internal ( RemoteTable
121124 , localPidCounter
122125 , localPidUnique
123126 , localProcessWithId
124− , localMonitorCounter
127+ , monitorCounter
128+ , spawnCounter
125129 , connectionTo
126130 )
127131
@@ -188,10 +192,6 @@ import Control.Distributed.Process.Internal ( RemoteTable
188192-- Basic Cloud Haskell API --
189193--------------------------------------------------------------------------------
190194
191−-- | The Cloud Haskell 'Process' type
192−newtype Process a = Process { unProcess :: ReaderT LocalProcess IO a }
193− deriving (Functor, Monad, MonadIO, MonadReader LocalProcess, Typeable)
194−
195195-- | Wait for a message of a specific type
196196expect :: forall a. Serializable a => Process a
197197expect = receiveWait [match return]
@@ -288,6 +288,15 @@ catch p h = do
288288expectTimeout :: forall a. Serializable a => Int -> Process (Maybe a)
289289expectTimeout timeout = receiveTimeout timeout [match return]
290290
291+-- | Asynchronous version of 'spawn'
292+--
293+-- ('spawn' is defined in terms of 'spawnAsync' and 'expect')
294+spawnAsync :: NodeId -> Closure (Process ()) -> Process SpawnRef
295+spawnAsync _nid proc = do
296+ spawnRef <- getSpawnRef
297+ postCtrlMsg (Spawn proc spawnRef)
298+ return spawnRef
299+
291300--------------------------------------------------------------------------------
292301-- Initialization --
293302--------------------------------------------------------------------------------
@@ -307,7 +316,6 @@ newLocalNode transport rtable = do
307316 { _localProcesses = Map.empty
308317 , _localPidCounter = 0
309318 , _localPidUnique = unq
310− , _localMonitorCounter = 0
311319 }
312320 ctrlChan <- newChan
313321 let node = LocalNode { localNodeId = NodeId $ NT.address endPoint
@@ -336,7 +344,9 @@ forkProcess node proc = modifyMVar (localState node) $ \st -> do
336344 let pid = ProcessId { processNodeId = localNodeId node
337345 , processLocalId = lpid
338346 }
339− pst <- newMVar LocalProcessState { _connections = Map.empty
347+ pst <- newMVar LocalProcessState { _connections = Map.empty
348+ , _monitorCounter = 0
349+ , _spawnCounter = 0
340350 }
341351 queue <- newCQueue
342352 (_, lproc) <- fixIO $ \ ~(tid, _) -> do
@@ -493,13 +503,22 @@ remoteProcessFailed node them = do
493503
494504getMonitorRefFor :: ProcessId -> Process MonitorRef
495505getMonitorRefFor pid = do
496− node <- processNode <$> ask
497− liftIO $ modifyMVar (localState node) $ \st -> do
498− let counter = st ^. localMonitorCounter
499− return ( localMonitorCounter ^: (+ 1) $ st
506+ proc <- ask
507+ liftIO $ modifyMVar (processState proc) $ \st -> do
508+ let counter = st ^. monitorCounter
509+ return ( monitorCounter ^: (+ 1) $ st
500510 , MonitorRef pid counter
501511 )
502512
513+getSpawnRef :: Process SpawnRef
514+getSpawnRef = do
515+ proc <- ask
516+ liftIO $ modifyMVar (processState proc) $ \st -> do
517+ let counter = st ^. spawnCounter
518+ return ( spawnCounter ^: (+ 1) $ st
519+ , SpawnRef counter
520+ )
521+
503522-- This is most definitely NOT exported
504523runLocalProcess :: LocalProcess -> Process a -> IO a
505524runLocalProcess lproc proc = runReaderT (unProcess proc) lproc
distributed-process/src/Control/Distributed/Process/Closure.hs+4−15
@@ -47,11 +47,11 @@ module Control.Distributed.Process.Closure
4747
4848import Prelude hiding (lookup)
4949import Data.ByteString.Lazy (ByteString)
50−import Data.Binary (Binary(..), encode, decode)
50+import Data.Binary (encode, decode)
5151import qualified Data.Map as Map (insert, (!))
5252import Data.Dynamic (toDyn, fromDyn)
5353import Data.Typeable (Typeable)
54−import Control.Applicative ((<$>), (<*>))
54+import Control.Applicative ((<$>))
5555import Control.Monad.Reader (ask)
5656import Control.Exception (throw)
5757import Language.Haskell.TH ( -- Q monad and operations
@@ -83,17 +83,10 @@ import Control.Distributed.Process (Process)
8383import Control.Distributed.Process.Internal ( RemoteTable
8484 , LocalProcess(processNode)
8585 , LocalNode(remoteTable)
86+ , Closure(..)
87+ , Static(..)
8688 )
8789
88−-- | A static value is one that is bound at top-level.
89−-- We represent it simply by a string
90−newtype Static a = Static String
91− deriving (Typeable)
92−
93−-- | A closure is a static value and an encoded environment
94−data Closure a = Closure (Static (ByteString -> a)) ByteString
95− deriving (Typeable)
96−
9790--------------------------------------------------------------------------------
9891-- Top-level API --
9992--------------------------------------------------------------------------------
@@ -127,10 +120,6 @@ lookupStatic label = do
127120 where
128121 typeError = userError "lookupStatic type error"
129122
130−instance Binary (Closure a) where
131− put (Closure (Static label) env) = put label >> put env
132− get = Closure <$> (Static <$> get) <*> get
133−
134123--------------------------------------------------------------------------------
135124-- Internal (Template Haskell) --
136125--------------------------------------------------------------------------------
distributed-process/src/Control/Distributed/Process/Internal.hs+97−24
@@ -6,22 +6,27 @@ module Control.Distributed.Process.Internal
66 NodeId(..)
77 , LocalProcessId(..)
88 , ProcessId(..)
9+ , Identifier
910 -- * Local nodes and processes
1011 , LocalNode(..)
1112 , LocalNodeState(..)
1213 , LocalProcess(..)
1314 , LocalProcessState(..)
15+ , Process(..)
16+ -- * Closures
17+ , Static(..)
18+ , Closure(..)
1419 -- * Messages
1520 , Message(..)
16− -- * Monitoring and linking
21+ -- * Node controller user-visible data types
1722 , MonitorRef(..)
1823 , MonitorNotification(..)
1924 , LinkException(..)
2025 , DiedReason(..)
2126 , DidUnmonitor(..)
2227 , DidUnlink(..)
23− -- * Node controller data types
24− , Identifier
28+ , SpawnRef(..)
29+ -- * Node controller internal data types
2530 , NCMsg(..)
2631 , ProcessSignal(..)
2732 -- * Serialization/deserialization
@@ -34,9 +39,10 @@ module Control.Distributed.Process.Internal
3439 , localProcesses
3540 , localPidCounter
3641 , localPidUnique
37− , localMonitorCounter
3842 , localProcessWithId
3943 , connections
44+ , monitorCounter
45+ , spawnCounter
4046 , connectionTo
4147 ) where
4248
@@ -51,6 +57,7 @@ import Data.Int (Int32)
5157import Data.Typeable (Typeable)
5258import Data.Dynamic (Dynamic)
5359import Data.Binary (Binary, encode, put, get, putWord8, getWord8)
60+import Data.ByteString.Lazy (ByteString)
5461import Data.Accessor (Accessor, accessor)
5562import qualified Data.Accessor.Container as DAC (mapMaybe)
5663import qualified Data.ByteString.Lazy as BSL (ByteString, toChunks, fromChunks, splitAt)
@@ -58,6 +65,8 @@ import qualified Data.ByteString as BSS (ByteString, concat, splitAt)
5865import qualified Network.Transport as NT (EndPoint, EndPointAddress, Connection)
5966import qualified Network.Transport.Internal as NTI (encodeInt32, decodeInt32)
6067import Control.Applicative ((<$>), (<*>))
68+import Control.Monad.Reader (MonadReader(..), ReaderT)
69+import Control.Monad.IO.Class (MonadIO)
6170import Control.Distributed.Process.Serializable ( Fingerprint
6271 , Serializable
6372 , encodeFingerprint
@@ -67,6 +76,21 @@ import Control.Distributed.Process.Serializable ( Fingerprint
6776 )
6877import Control.Distributed.Process.Internal.CQueue (CQueue)
6978
79+--------------------------------------------------------------------------------
80+-- Global state --
81+--------------------------------------------------------------------------------
82+
83+-- | Used to fake 'static' (see paper)
84+type RemoteTable = Map String Dynamic
85+
86+-- | Initial (empty) remote-call meta data
87+initRemoteTable :: RemoteTable
88+initRemoteTable = Map.empty
89+
90+--------------------------------------------------------------------------------
91+-- Node and process identifiers --
92+--------------------------------------------------------------------------------
93+
7094-- | Node identifier
7195newtype NodeId = NodeId { nodeAddress :: NT.EndPointAddress }
7296 deriving (Eq, Ord, Binary)
@@ -95,12 +119,12 @@ data ProcessId = ProcessId
95119instance Show ProcessId where
96120 show pid = show (processNodeId pid) ++ ":" ++ show (processLocalId pid)
97121
98−-- | Used to fake 'static' (see paper)
99−type RemoteTable = Map String Dynamic
122+-- | Node or process identifier
123+type Identifier = Either ProcessId NodeId
100124
101−-- | Initial (empty) remote-call meta data
102−initRemoteTable :: RemoteTable
103−initRemoteTable = Map.empty
125+--------------------------------------------------------------------------------
126+-- Local nodes and processes --
127+--------------------------------------------------------------------------------
104128
105129-- | Local nodes
106130data LocalNode = LocalNode
@@ -117,7 +141,6 @@ data LocalNodeState = LocalNodeState
117141 { _localProcesses :: Map LocalProcessId LocalProcess
118142 , _localPidCounter :: Int32
119143 , _localPidUnique :: Int32
120− , _localMonitorCounter :: Int32
121144 }
122145
123146-- | Processes running on our local node
@@ -132,17 +155,46 @@ data LocalProcess = LocalProcess
132155-- | Local process state
133156data LocalProcessState = LocalProcessState
134157 { _connections :: Map ProcessId NT.Connection
158+ , _monitorCounter :: Int32
159+ , _spawnCounter :: Int32
135160 }
136161
162+-- | The Cloud Haskell 'Process' type
163+newtype Process a = Process { unProcess :: ReaderT LocalProcess IO a }
164+ deriving (Functor, Monad, MonadIO, MonadReader LocalProcess, Typeable)
165+
166+--------------------------------------------------------------------------------
167+-- Closures --
168+--------------------------------------------------------------------------------
169+
170+-- | A static value is one that is bound at top-level.
171+-- We represent it simply by a string
172+newtype Static a = Static String
173+ deriving (Typeable, Show)
174+
175+-- | A closure is a static value and an encoded environment
176+data Closure a = Closure (Static (ByteString -> a)) ByteString
177+ deriving (Typeable, Show)
178+
179+--------------------------------------------------------------------------------
180+-- Messages --
181+--------------------------------------------------------------------------------
182+
137183-- | Messages consist of their typeRep fingerprint and their encoding
138184data Message = Message
139185 { messageFingerprint :: Fingerprint
140186 , messageEncoding :: BSL.ByteString
141187 }
142188
189+--------------------------------------------------------------------------------
190+-- Node controller user-visible data types --
191+--------------------------------------------------------------------------------
192+
143193-- | MonitorRef is opaque for regular Cloud Haskell processes
144194data MonitorRef = MonitorRef
145− { monitorRefPid :: ProcessId
195+ { -- | PID of the process to be monitored (for routing purposes)
196+ monitorRefPid :: ProcessId
197+ -- | Unique to distinguish multiple monitor requests by the same process
146198 , monitorRefCounter :: Int32
147199 }
148200 deriving (Eq, Ord, Show)
@@ -157,14 +209,6 @@ data LinkException = LinkException ProcessId DiedReason
157209
158210instance Exception LinkException
159211
160−-- | (Asynchronous) reply from unmonitor
161−newtype DidUnmonitor = DidUnmonitor MonitorRef
162− deriving (Typeable, Binary)
163−
164−-- | (Asynchronous) reply from unlink
165−newtype DidUnlink = DidUnlink ProcessId
166− deriving (Typeable, Binary)
167−
168212-- | Why did a process die?
169213data DiedReason =
170214 -- | Normal termination
@@ -180,8 +224,21 @@ data DiedReason =
180224 | DiedNoProc
181225 deriving (Show, Eq)
182226
183−-- | Node or process identifier
184−type Identifier = Either ProcessId NodeId
227+-- | (Asynchronous) reply from unmonitor
228+newtype DidUnmonitor = DidUnmonitor MonitorRef
229+ deriving (Typeable, Binary)
230+
231+-- | (Asynchronous) reply from unlink
232+newtype DidUnlink = DidUnlink ProcessId
233+ deriving (Typeable, Binary)
234+
235+-- | 'SpawnRef' are used to return pids of spawned processes
236+newtype SpawnRef = SpawnRef Int32
237+ deriving (Show, Binary, Typeable)
238+
239+--------------------------------------------------------------------------------
240+-- Node controller internal data types --
241+--------------------------------------------------------------------------------
185242
186243-- | Messages to the node controller
187244data NCMsg = NCMsg
@@ -197,6 +254,7 @@ data ProcessSignal =
197254 | Monitor ProcessId MonitorRef
198255 | Unmonitor MonitorRef
199256 | Died Identifier DiedReason
257+ | Spawn (Closure (Process ())) SpawnRef
200258 deriving Show
201259
202260--------------------------------------------------------------------------------
@@ -241,6 +299,10 @@ payloadToId bss = let (bs1, bss') = BSS.splitAt 4 . BSS.concat $ bss
241299 }
242300 _ -> fail "payloadToId"
243301
302+--------------------------------------------------------------------------------
303+-- Binary instances --
304+--------------------------------------------------------------------------------
305+
244306instance Binary LocalProcessId where
245307 put lpid = put (lpidUnique lpid) >> put (lpidCounter lpid)
246308 get = LocalProcessId <$> get <*> get
@@ -267,6 +329,7 @@ instance Binary ProcessSignal where
267329 put (Monitor pid ref) = putWord8 2 >> put pid >> put ref
268330 put (Unmonitor ref) = putWord8 3 >> put ref
269331 put (Died who reason) = putWord8 4 >> put who >> put reason
332+ put (Spawn proc ref) = putWord8 5 >> put proc >> put ref
270333 get = do
271334 header <- getWord8
272335 case header of
@@ -275,6 +338,7 @@ instance Binary ProcessSignal where
275338 2 -> Monitor <$> get <*> get
276339 3 -> Unmonitor <$> get
277340 4 -> Died <$> get <*> get
341+ 5 -> Spawn <$> get <*> get
278342 _ -> fail "ProcessSignal.get: invalid"
279343
280344instance Binary DiedReason where
@@ -293,6 +357,10 @@ instance Binary DiedReason where
293357 4 -> return DiedNoProc
294358 _ -> fail "DiedReason.get: invalid"
295359
360+instance Binary (Closure a) where
361+ put (Closure (Static label) env) = put label >> put env
362+ get = Closure <$> (Static <$> get) <*> get
363+
296364--------------------------------------------------------------------------------
297365-- Accessors --
298366--------------------------------------------------------------------------------
@@ -306,14 +374,19 @@ localPidCounter = accessor _localPidCounter (\ctr st -> st { _localPidCounter =
306374localPidUnique :: Accessor LocalNodeState Int32
307375localPidUnique = accessor _localPidUnique (\unq st -> st { _localPidUnique = unq })
308376
309−localMonitorCounter :: Accessor LocalNodeState Int32
310−localMonitorCounter = accessor _localMonitorCounter (\ctr st -> st { _localMonitorCounter = ctr })
311−
312377localProcessWithId :: LocalProcessId -> Accessor LocalNodeState (Maybe LocalProcess)
313378localProcessWithId lpid = localProcesses >>> DAC.mapMaybe lpid
314379
315380connections :: Accessor LocalProcessState (Map ProcessId NT.Connection)
316381connections = accessor _connections (\conns st -> st { _connections = conns })
317382
383+monitorCounter :: Accessor LocalProcessState Int32
384+monitorCounter = accessor _monitorCounter (\cnt st -> st { _monitorCounter = cnt })
385+
386+spawnCounter :: Accessor LocalProcessState Int32
387+spawnCounter = accessor _spawnCounter (\cnt st -> st { _spawnCounter = cnt })
388+
318389connectionTo :: ProcessId -> Accessor LocalProcessState (Maybe NT.Connection)
319390connectionTo pid = connections >>> DAC.mapMaybe pid
391+
392+
distributed-process/src/Control/Distributed/Process/Internal/NodeController.hs+3
@@ -104,6 +104,8 @@ nodeController = do
104104 ncEffectNodeDied nid reason
105105 NCMsg _from (Died (Left pid) reason) ->
106106 ncEffectProcessDied pid reason
107+ NCMsg _from (Spawn _ _) ->
108+ fail "spawn not implemented"
107109 -- Error cases
108110 NCMsg (Right _) (Link _) ->
109111 error "Link message from a node?"
@@ -305,6 +307,7 @@ destNid (Monitor pid _) = Just . processNodeId $ pid
305307destNid (Unmonitor ref) = Just . processNodeId . monitorRefPid $ ref
306308destNid (Died (Right _) _) = Nothing
307309destNid (Died (Left _pid) _) = fail "destNid: TODO"
310+destNid (Spawn _ _) = Nothing
308311
309312initNCState :: NCState
310313initNCState = NCState