git.iko.soy/distributed-process
on master help sign in
Continue cleanup, and add spawnSupervised
commit7f7e274d0c12b391611b0963799311056a225c32 authorEdsko de Vries <edsko@edsko.net> dateThu 28 Jun 2012, 11:26 (14 years ago) parent03fd8b4
13 files changed+413−227
distributed-process/src/Control/Distributed/Process.hs+30−16 distributed-process/src/Control/Distributed/Process/Closure.hs+53−156 distributed-process/src/Control/Distributed/Process/Internal/Closure.hs+5−11 distributed-process/src/Control/Distributed/Process/Internal/Closure/BuiltIn.hs+23 distributed-process/src/Control/Distributed/Process/Internal/Closure/BuiltIn.hs-boot+12 distributed-process/src/Control/Distributed/Process/Internal/Closure/Combinators.hs+182 distributed-process/src/Control/Distributed/Process/Internal/Closure/TH.hs+25−16 distributed-process/src/Control/Distributed/Process/Internal/MessageT.hs+1−1 distributed-process/src/Control/Distributed/Process/Internal/TypeRep.hs+1 distributed-process/src/Control/Distributed/Process/Internal/Types.hs+22−9 distributed-process/src/Control/Distributed/Process/Node.hs+15−15 distributed-process/src/Control/Distributed/Process/Serializable.hs+1 distributed-process/tests/TestClosure.hs+43−3
distributed-process/src/Control/Distributed/Process.hs+30−16
@@ -58,6 +58,7 @@ module Control.Distributed.Process
5858 , catch
5959 , expectTimeout
6060 , spawnAsync
61+ , spawnSupervised
6162 ) where
6263
6364import Prelude hiding (catch)
@@ -65,7 +66,6 @@ import Data.Binary (decode, encode)
6566import Data.Typeable (Typeable, typeOf)
6667import Control.Monad.Reader (ask)
6768import Control.Monad.IO.Class (MonadIO, liftIO)
68−import qualified Control.Monad.Trans.Class as Trans (lift)
6969import Control.Applicative ((<$>))
7070import Control.Exception (Exception, throw)
7171import qualified Control.Exception as Exception (catch)
@@ -107,7 +107,6 @@ import Control.Distributed.Process.Internal.Types
107107 , ProcessSignal(..)
108108 , monitorCounter
109109 , spawnCounter
110− , MessageT
111110 , Closure(..)
112111 , CallReply(..)
113112 , SendPort(..)
@@ -117,6 +116,7 @@ import Control.Distributed.Process.Internal.Types
117116 , TypedChannel(..)
118117 , ChannelId(..)
119118 , Identifier(..)
119+ , procMsg
120120 )
121121import Control.Distributed.Process.Internal.MessageT
122122 ( sendMessage
@@ -126,6 +126,8 @@ import Control.Distributed.Process.Internal.MessageT
126126import Control.Distributed.Process.Internal.Dynamic (fromDyn, dynTypeRep)
127127import Control.Distributed.Process.Internal.Closure (resolveClosure)
128128import Control.Distributed.Process.Internal.Node (runLocalProcess)
129+import {-# SOURCE #-} Control.Distributed.Process.Internal.Closure.BuiltIn (linkClosure)
130+import Control.Distributed.Process.Internal.Closure.Combinators (cpSeq)
129131
130132-- INTERNAL NOTES
131133--
@@ -194,7 +196,9 @@ import Control.Distributed.Process.Internal.Node (runLocalProcess)
194196send :: Serializable a => ProcessId -> a -> Process ()
195197-- This requires a lookup on every send. If we want to avoid that we need to
196198-- modify serializable to allow for stateful (IO) deserialization
197−send them msg = lift $ sendMessage (ProcessIdentifier them) msg
199+-- Warning: if we change how 'send' is implemented, might also want to change
200+-- the implementation of 'Internal.Closure.TH.generateSender'
201+send them msg = procMsg $ sendMessage (ProcessIdentifier them) msg
198202
199203-- | Wait for a message of a specific type
200204expect :: forall a. Serializable a => Process a
@@ -225,7 +229,7 @@ newChan = do
225229
226230-- | Send a message on a typed channel
227231sendChan :: Serializable a => SendPort a -> a -> Process ()
228−sendChan (SendPort them) msg = lift $ sendBinary (ChannelIdentifier them) msg
232+sendChan (SendPort them) msg = procMsg $ sendBinary (ChannelIdentifier them) msg
229233
230234-- | Wait for a message on a typed channel
231235receiveChan :: Serializable a => ReceivePort a -> Process a
@@ -337,28 +341,30 @@ getSelfPid = processId <$> ask
337341
338342-- | Get the node ID of our local node
339343getSelfNode :: Process NodeId
340−getSelfNode = localNodeId <$> lift getLocalNode
344+getSelfNode = localNodeId <$> procMsg getLocalNode
341345
342346--------------------------------------------------------------------------------
343347-- Monitoring and linking --
344348--------------------------------------------------------------------------------
345349
346−-- | Link to a remote process
350+-- | Link to a remote process (asynchronous)
351+--
352+-- Note that 'link' provides unidirectional linking (see 'spawnSupervised').
347353link :: ProcessId -> Process ()
348354link = sendCtrlMsg Nothing . Link
349355
350−-- | Remove a link
356+-- | Remove a link (asynchronous)
351357unlink :: ProcessId -> Process ()
352358unlink = sendCtrlMsg Nothing . Unlink
353359
354−-- | Monitor another process
360+-- | Monitor another process (asynchronous)
355361monitor :: ProcessId -> Process MonitorRef
356362monitor them = do
357363 monitorRef <- getMonitorRefFor them
358364 sendCtrlMsg Nothing $ Monitor them monitorRef
359365 return monitorRef
360366
361−-- | Remove a monitor
367+-- | Remove a monitor (asynchronous)
362368unmonitor :: MonitorRef -> Process ()
363369unmonitor = sendCtrlMsg Nothing . Unmonitor
364370
@@ -369,7 +375,7 @@ unmonitor = sendCtrlMsg Nothing . Unmonitor
369375-- | Deserialize a closure
370376unClosure :: forall a. Typeable a => Closure a -> Process a
371377unClosure (Closure (Static label) env) = do
372− rtable <- remoteTable <$> lift getLocalNode
378+ rtable <- remoteTable <$> procMsg getLocalNode
373379 let Just dyn = resolveClosure rtable label env
374380 return $ fromDyn dyn (throw (typeError dyn))
375381 where
@@ -384,7 +390,7 @@ unClosure (Closure (Static label) env) = do
384390-- | Catch exceptions within a process
385391catch :: Exception e => Process a -> (e -> Process a) -> Process a
386392catch p h = do
387− node <- lift getLocalNode
393+ node <- procMsg getLocalNode
388394 lproc <- ask
389395 let run :: Process a -> IO a
390396 run proc = runLocalProcess node proc lproc
@@ -403,6 +409,17 @@ spawnAsync nid proc = do
403409 sendCtrlMsg (Just nid) $ Spawn proc spawnRef
404410 return spawnRef
405411
412+-- | Spawn a child process, have the child link to the parent and the parent
413+-- monitor the child
414+spawnSupervised :: NodeId
415+ -> Closure (Process ())
416+ -> Process (ProcessId, MonitorRef)
417+spawnSupervised nid proc = do
418+ us <- getSelfPid
419+ them <- spawn nid (linkClosure us `cpSeq` proc)
420+ ref <- monitor them
421+ return (them, ref)
422+
406423--------------------------------------------------------------------------------
407424-- Auxiliary functions --
408425--------------------------------------------------------------------------------
@@ -436,10 +453,7 @@ sendCtrlMsg mNid signal = do
436453 }
437454 case mNid of
438455 Nothing -> do
439− ctrlChan <- localCtrlChan <$> lift getLocalNode
456+ ctrlChan <- localCtrlChan <$> procMsg getLocalNode
440457 liftIO $ writeChan ctrlChan msg
441458 Just nid ->
442− lift $ sendBinary (NodeIdentifier nid) msg
443−
444−lift :: MessageT IO a -> Process a
445−lift = Process . Trans.lift
459+ procMsg $ sendBinary (NodeIdentifier nid) msg
distributed-process/src/Control/Distributed/Process/Closure.hs+53−156
@@ -1,13 +1,31 @@
11-- | Implementation of 'Closure' that works around the absence of 'static'.
22--
3−-- [Example usage]
3+-- [Built-in closures]
4+--
5+-- We offer a number of standard commonly useful closures.
6+--
7+-- [Closure combinators]
8+--
9+-- Closures combinators allow to create closures from other closures. For
10+-- example, 'spawnSupervised' is defined as follows:
11+--
12+-- > spawnSupervised :: NodeId
13+-- > -> Closure (Process ())
14+-- > -> Process (ProcessId, MonitorRef)
15+-- > spawnSupervised nid proc = do
16+-- > us <- getSelfPid
17+-- > them <- spawn nid (linkClosure us `cpSeq` proc)
18+-- > ref <- monitor them
19+-- > return (them, ref)
20+--
21+-- [User-defined closures]
422--
523-- Suppose we have a monomorphic function
624--
725-- > addInt :: Int -> Int -> Int
826-- > addInt x y = x + y
927--
10−-- Then
28+-- Then the Template Haskell splice
1129--
1230-- > remotable ['addInt]
1331--
@@ -20,7 +38,7 @@
2038--
2139-- > unClosure :: Typeable a => Closure a -> Process a
2240--
23−-- In general, given a monomorphic function @f : a -> b@ the corresponding
41+-- In general, given a monomorphic function @f :: a -> b@ the corresponding
2442-- function @$(mkClosure 'f)@ will have type @a -> Closure b@.
2543--
2644-- The call to 'remotable' will also generate a function
@@ -39,9 +57,11 @@
3957--
4058-- See Section 6, /Faking It/, of /Towards Haskell in the Cloud/ for more info.
4159module Control.Distributed.Process.Closure
42− ( -- * Primitive closures
60+ ( -- * User-defined closures
4361 remotable
4462 , mkClosure
63+ -- * Built-in closures
64+ , linkClosure
4565 -- * Generic closure combinators
4666 , closureApply
4767 , closureConst
@@ -70,156 +90,33 @@ module Control.Distributed.Process.Closure
7090 , cpSeq
7191 ) where
7292
73−import Prelude hiding (lookup)
74−import qualified Data.ByteString.Lazy as BS (empty)
75−import Data.Binary (encode)
76−import Data.Typeable (typeOf, Typeable)
77−import Control.Distributed.Process.Internal.Types
78− ( Closure(..)
79− , Static(..)
80− , StaticLabel(..)
81− , Process
82− )
8393import Control.Distributed.Process.Internal.Closure.TH (remotable, mkClosure)
84−
85−--------------------------------------------------------------------------------
86−-- Generic closure combinators --
87−--------------------------------------------------------------------------------
88−
89−closureApply :: Closure (a -> b) -> Closure a -> Closure b
90−closureApply (Closure (Static labelf) envf) (Closure (Static labelx) envx) =
91− Closure (Static ClosureApply) $ encode (labelf, envf, labelx, envx)
92−
93−closureConst :: forall a b. (Typeable a, Typeable b)
94− => Closure (a -> b -> a)
95−closureConst = Closure (Static ClosureConst) (encode $ typeOf aux)
96− where
97− aux :: a -> b -> a
98− aux = undefined
99−
100−closureUnit :: Closure ()
101−closureUnit = Closure (Static ClosureUnit) BS.empty
102−
103−--------------------------------------------------------------------------------
104−-- Arrow combinators for processes --
105−--------------------------------------------------------------------------------
106−
107−type CP a b = Closure (a -> Process b)
108−
109−cpIntro :: (Typeable a, Typeable b)
110− => Closure (Process b) -> CP a b
111−cpIntro = closureApply closureConst
112−
113−cpElim :: Typeable a
114− => CP () a -> Closure (Process a)
115−cpElim = flip closureApply closureUnit
116−
117−cpId :: forall a. Typeable a
118− => CP a a
119−cpId = Closure (Static CpId) (encode $ typeOf aux)
120− where
121− aux :: a -> Process a
122− aux = undefined
123−
124−cpComp :: forall a b c. (Typeable a, Typeable b, Typeable c)
125− => CP a b -> CP b c -> CP a c
126−cpComp f g = comp `closureApply` f `closureApply` g
127− where
128− comp :: Closure ((a -> Process b) -> (b -> Process c) -> a -> Process c)
129− comp = Closure (Static CpComp) (encode $ typeOf aux)
130−
131− aux :: (a -> Process b) -> (b -> Process c) -> a -> Process c
132− aux = undefined
133−
134−cpFirst :: forall a b c. (Typeable a, Typeable b, Typeable c)
135− => CP a b -> CP (a, c) (b, c)
136−cpFirst = closureApply first
137− where
138− first :: Closure ((a -> Process b) -> (a, c) -> Process (b, c))
139− first = Closure (Static CpFirst) (encode $ typeOf aux)
140−
141− aux :: (a -> Process b) -> (a, c) -> Process (b, c)
142− aux = undefined
143−
144−cpSwap :: forall a b. (Typeable a, Typeable b)
145− => CP (a, b) (b, a)
146−cpSwap = Closure (Static CpSwap) (encode $ typeOf aux)
147− where
148− aux :: (a, b) -> Process (b, a)
149− aux = undefined
150−
151−cpSecond :: (Typeable a, Typeable b, Typeable c)
152− => CP a b -> CP (c, a) (c, b)
153−cpSecond f = cpSwap `cpComp` cpFirst f `cpComp` cpSwap
154−
155−cpPair :: (Typeable a, Typeable a', Typeable b, Typeable b')
156− => CP a b -> CP a' b' -> CP (a, a') (b, b')
157−cpPair f g = cpFirst f `cpComp` cpSecond g
158−
159−cpCopy :: forall a. Typeable a
160− => CP a (a, a)
161−cpCopy = Closure (Static CpCopy) (encode $ typeOf aux)
162− where
163− aux :: a -> Process (a, a)
164− aux = undefined
165−
166−cpFanOut :: (Typeable a, Typeable b, Typeable c)
167− => CP a b -> CP a c -> CP a (b, c)
168−cpFanOut f g = cpCopy `cpComp` (f `cpPair` g)
169−
170−cpLeft :: forall a b c. (Typeable a, Typeable b, Typeable c)
171− => CP a b -> CP (Either a c) (Either b c)
172−cpLeft = closureApply left
173− where
174− left :: Closure ((a -> Process b) -> Either a c -> Process (Either b c))
175− left = Closure (Static CpLeft) (encode $ typeOf aux)
176−
177− aux :: (a -> Process b) -> Either a c -> Process (Either b c)
178− aux = undefined
179−
180−cpMirror :: forall a b. (Typeable a, Typeable b)
181− => CP (Either a b) (Either b a)
182−cpMirror = Closure (Static CpMirror) (encode $ typeOf aux)
183− where
184− aux :: Either a b -> Process (Either b a)
185− aux = undefined
186−
187−cpRight :: forall a b c. (Typeable a, Typeable b, Typeable c)
188− => CP a b -> CP (Either c a) (Either c b)
189−cpRight f = cpMirror `cpComp` cpLeft f `cpComp` cpMirror
190−
191−cpEither :: (Typeable a, Typeable a', Typeable b, Typeable b')
192− => CP a b -> CP a' b' -> CP (Either a a') (Either b b')
193−cpEither f g = cpLeft f `cpComp` cpRight g
194−
195−cpUntag :: forall a. Typeable a
196− => CP (Either a a) a
197−cpUntag = Closure (Static CpUntag) (encode $ typeOf aux)
198− where
199− aux :: Either a a -> Process a
200− aux = undefined
201−
202−cpFanIn :: (Typeable a, Typeable b, Typeable c)
203− => CP a c -> CP b c -> CP (Either a b) c
204−cpFanIn f g = (f `cpEither` g) `cpComp` cpUntag
205−
206−cpApply :: forall a b. (Typeable a, Typeable b)
207− => CP (CP a b, a) b
208−cpApply = Closure (Static CpApply) $ encode ( typeOf aux
209− , typeOf (undefined :: a)
210− , typeOf (undefined :: Process b)
211− )
212− where
213− aux :: (Closure (a -> Process b), a) -> Process b
214− aux = undefined
215−
216−--------------------------------------------------------------------------------
217−-- Some derived operators for processes --
218−--------------------------------------------------------------------------------
219−
220−cpBind :: (Typeable a, Typeable b)
221− => Closure (Process a) -> Closure (a -> Process b) -> Closure (Process b)
222−cpBind x f = cpElim $ cpIntro x `cpComp` f
223−
224−cpSeq :: Closure (Process ()) -> Closure (Process ()) -> Closure (Process ())
225−cpSeq p q = p `cpBind` cpIntro q
94+import Control.Distributed.Process.Internal.Closure.BuiltIn (linkClosure)
95+import Control.Distributed.Process.Internal.Closure.Combinators
96+ ( -- Generic combinators
97+ closureApply
98+ , closureConst
99+ , closureUnit
100+ -- Arrow combinators for processes
101+ , CP
102+ , cpIntro
103+ , cpElim
104+ , cpId
105+ , cpComp
106+ , cpFirst
107+ , cpSwap
108+ , cpSecond
109+ , cpPair
110+ , cpCopy
111+ , cpFanOut
112+ , cpLeft
113+ , cpMirror
114+ , cpRight
115+ , cpEither
116+ , cpUntag
117+ , cpFanIn
118+ , cpApply
119+ -- Derived process operators
120+ , cpBind
121+ , cpSeq
122+ )
distributed-process/src/Control/Distributed/Process/Internal/Closure.hs+5−11
@@ -2,14 +2,12 @@
22module Control.Distributed.Process.Internal.Closure
33 ( -- * Runtime support
44 initRemoteTable
5− , registerLabel
6− , registerSender
75 , resolveClosure
86 ) where
97
108import qualified Data.Map as Map (empty)
11−import Data.Accessor ((^=), (^.))
12−import Data.Typeable (TypeRep, TyCon, typeRepTyCon, typeOf)
9+import Data.Accessor ((^.))
10+import Data.Typeable (TyCon, typeRepTyCon, typeOf)
1311import Data.ByteString.Lazy (ByteString)
1412import Data.Binary (decode)
1513import Control.Distributed.Process.Internal.Types
@@ -32,6 +30,8 @@ import Control.Distributed.Process.Internal.Dynamic
3230 , unsafeCoerce#
3331 )
3432import Control.Distributed.Process.Internal.TypeRep () -- Binary instances
33+import {-# SOURCE #-} qualified Control.Distributed.Process.Internal.Closure.BuiltIn as BuiltIn
34+ (remoteTable)
3535
3636--------------------------------------------------------------------------------
3737-- Runtime support for closures --
@@ -39,13 +39,7 @@ import Control.Distributed.Process.Internal.TypeRep () -- Binary instances
3939
4040-- | Initial (empty) remote-call meta data
4141initRemoteTable :: RemoteTable
42−initRemoteTable = RemoteTable Map.empty Map.empty
43−
44−registerLabel :: String -> Dynamic -> RemoteTable -> RemoteTable
45−registerLabel label dyn = remoteTableLabel label ^= Just dyn
46−
47−registerSender :: TypeRep -> Dynamic -> RemoteTable -> RemoteTable
48−registerSender typ dyn = remoteTableSender typ ^= Just dyn
42+initRemoteTable = BuiltIn.remoteTable (RemoteTable Map.empty Map.empty)
4943
5044resolveClosure :: RemoteTable -> StaticLabel -> ByteString -> Maybe Dynamic
5145-- Special support for call
distributed-process/src/Control/Distributed/Process/Internal/Closure/BuiltIn.hsnew file+23
@@ -0,0 +1,23 @@
1+{-# LANGUAGE TemplateHaskell #-}
2+module Control.Distributed.Process.Internal.Closure.BuiltIn
3+ ( remoteTable
4+ , linkClosure
5+ ) where
6+
7+import Control.Distributed.Process (link)
8+import Control.Distributed.Process.Internal.Types
9+ ( ProcessId
10+ , Closure
11+ , Process
12+ , RemoteTable
13+ )
14+import Control.Distributed.Process.Internal.Closure.TH (remotable, mkClosure)
15+
16+remotable ['link]
17+
18+remoteTable :: RemoteTable -> RemoteTable
19+remoteTable = __remoteTable
20+
21+-- | Closure version of 'link'
22+linkClosure :: ProcessId -> Closure (Process ())
23+linkClosure = $(mkClosure 'link)
distributed-process/src/Control/Distributed/Process/Internal/Closure/BuiltIn.hs-bootnew file+12
@@ -0,0 +1,12 @@
1+module Control.Distributed.Process.Internal.Closure.BuiltIn where
2+
3+import Control.Distributed.Process.Internal.Types
4+ ( ProcessId
5+ , Closure
6+ , Process
7+ , RemoteTable
8+ )
9+
10+remoteTable :: RemoteTable -> RemoteTable
11+
12+linkClosure :: ProcessId -> Closure (Process ())
distributed-process/src/Control/Distributed/Process/Internal/Closure/Combinators.hsnew file+182
@@ -0,0 +1,182 @@
1+module Control.Distributed.Process.Internal.Closure.Combinators
2+ ( -- * Generic combinators
3+ closureApply
4+ , closureConst
5+ , closureUnit
6+ -- * Arrow combinators for processes
7+ , CP
8+ , cpIntro
9+ , cpElim
10+ , cpId
11+ , cpComp
12+ , cpFirst
13+ , cpSwap
14+ , cpSecond
15+ , cpPair
16+ , cpCopy
17+ , cpFanOut
18+ , cpLeft
19+ , cpMirror
20+ , cpRight
21+ , cpEither
22+ , cpUntag
23+ , cpFanIn
24+ , cpApply
25+ -- * Derived process operators
26+ , cpBind
27+ , cpSeq
28+ ) where
29+
30+import Prelude hiding (lookup)
31+import qualified Data.ByteString.Lazy as BS (empty)
32+import Data.Binary (encode)
33+import Data.Typeable (typeOf, Typeable)
34+import Control.Distributed.Process.Internal.Types
35+ ( Closure(..)
36+ , Static(..)
37+ , StaticLabel(..)
38+ , Process
39+ )
40+import Control.Distributed.Process.Internal.TypeRep () -- Binary instances
41+
42+--------------------------------------------------------------------------------
43+-- Generic closure combinators --
44+--------------------------------------------------------------------------------
45+
46+closureApply :: Closure (a -> b) -> Closure a -> Closure b
47+closureApply (Closure (Static labelf) envf) (Closure (Static labelx) envx) =
48+ Closure (Static ClosureApply) $ encode (labelf, envf, labelx, envx)
49+
50+closureConst :: forall a b. (Typeable a, Typeable b)
51+ => Closure (a -> b -> a)
52+closureConst = Closure (Static ClosureConst) (encode $ typeOf aux)
53+ where
54+ aux :: a -> b -> a
55+ aux = undefined
56+
57+closureUnit :: Closure ()
58+closureUnit = Closure (Static ClosureUnit) BS.empty
59+
60+--------------------------------------------------------------------------------
61+-- Arrow combinators for processes --
62+--------------------------------------------------------------------------------
63+
64+type CP a b = Closure (a -> Process b)
65+
66+cpIntro :: (Typeable a, Typeable b)
67+ => Closure (Process b) -> CP a b
68+cpIntro = closureApply closureConst
69+
70+cpElim :: Typeable a
71+ => CP () a -> Closure (Process a)
72+cpElim = flip closureApply closureUnit
73+
74+cpId :: forall a. Typeable a
75+ => CP a a
76+cpId = Closure (Static CpId) (encode $ typeOf aux)
77+ where
78+ aux :: a -> Process a
79+ aux = undefined
80+
81+cpComp :: forall a b c. (Typeable a, Typeable b, Typeable c)
82+ => CP a b -> CP b c -> CP a c
83+cpComp f g = comp `closureApply` f `closureApply` g
84+ where
85+ comp :: Closure ((a -> Process b) -> (b -> Process c) -> a -> Process c)
86+ comp = Closure (Static CpComp) (encode $ typeOf aux)
87+
88+ aux :: (a -> Process b) -> (b -> Process c) -> a -> Process c
89+ aux = undefined
90+
91+cpFirst :: forall a b c. (Typeable a, Typeable b, Typeable c)
92+ => CP a b -> CP (a, c) (b, c)
93+cpFirst = closureApply first
94+ where
95+ first :: Closure ((a -> Process b) -> (a, c) -> Process (b, c))
96+ first = Closure (Static CpFirst) (encode $ typeOf aux)
97+
98+ aux :: (a -> Process b) -> (a, c) -> Process (b, c)
99+ aux = undefined
100+
101+cpSwap :: forall a b. (Typeable a, Typeable b)
102+ => CP (a, b) (b, a)
103+cpSwap = Closure (Static CpSwap) (encode $ typeOf aux)
104+ where
105+ aux :: (a, b) -> Process (b, a)
106+ aux = undefined
107+
108+cpSecond :: (Typeable a, Typeable b, Typeable c)
109+ => CP a b -> CP (c, a) (c, b)
110+cpSecond f = cpSwap `cpComp` cpFirst f `cpComp` cpSwap
111+
112+cpPair :: (Typeable a, Typeable a', Typeable b, Typeable b')
113+ => CP a b -> CP a' b' -> CP (a, a') (b, b')
114+cpPair f g = cpFirst f `cpComp` cpSecond g
115+
116+cpCopy :: forall a. Typeable a
117+ => CP a (a, a)
118+cpCopy = Closure (Static CpCopy) (encode $ typeOf aux)
119+ where
120+ aux :: a -> Process (a, a)
121+ aux = undefined
122+
123+cpFanOut :: (Typeable a, Typeable b, Typeable c)
124+ => CP a b -> CP a c -> CP a (b, c)
125+cpFanOut f g = cpCopy `cpComp` (f `cpPair` g)
126+
127+cpLeft :: forall a b c. (Typeable a, Typeable b, Typeable c)
128+ => CP a b -> CP (Either a c) (Either b c)
129+cpLeft = closureApply left
130+ where
131+ left :: Closure ((a -> Process b) -> Either a c -> Process (Either b c))
132+ left = Closure (Static CpLeft) (encode $ typeOf aux)
133+
134+ aux :: (a -> Process b) -> Either a c -> Process (Either b c)
135+ aux = undefined
136+
137+cpMirror :: forall a b. (Typeable a, Typeable b)
138+ => CP (Either a b) (Either b a)
139+cpMirror = Closure (Static CpMirror) (encode $ typeOf aux)
140+ where
141+ aux :: Either a b -> Process (Either b a)
142+ aux = undefined
143+
144+cpRight :: forall a b c. (Typeable a, Typeable b, Typeable c)
145+ => CP a b -> CP (Either c a) (Either c b)
146+cpRight f = cpMirror `cpComp` cpLeft f `cpComp` cpMirror
147+
148+cpEither :: (Typeable a, Typeable a', Typeable b, Typeable b')
149+ => CP a b -> CP a' b' -> CP (Either a a') (Either b b')
150+cpEither f g = cpLeft f `cpComp` cpRight g
151+
152+cpUntag :: forall a. Typeable a
153+ => CP (Either a a) a
154+cpUntag = Closure (Static CpUntag) (encode $ typeOf aux)
155+ where
156+ aux :: Either a a -> Process a
157+ aux = undefined
158+
159+cpFanIn :: (Typeable a, Typeable b, Typeable c)
160+ => CP a c -> CP b c -> CP (Either a b) c
161+cpFanIn f g = (f `cpEither` g) `cpComp` cpUntag
162+
163+cpApply :: forall a b. (Typeable a, Typeable b)
164+ => CP (CP a b, a) b
165+cpApply = Closure (Static CpApply) $ encode ( typeOf aux
166+ , typeOf (undefined :: a)
167+ , typeOf (undefined :: Process b)
168+ )
169+ where
170+ aux :: (Closure (a -> Process b), a) -> Process b
171+ aux = undefined
172+
173+--------------------------------------------------------------------------------
174+-- Some derived operators for processes --
175+--------------------------------------------------------------------------------
176+
177+cpBind :: (Typeable a, Typeable b)
178+ => Closure (Process a) -> Closure (a -> Process b) -> Closure (Process b)
179+cpBind x f = cpElim $ cpIntro x `cpComp` f
180+
181+cpSeq :: Closure (Process ()) -> Closure (Process ()) -> Closure (Process ())
182+cpSeq p q = p `cpBind` cpIntro q
distributed-process/src/Control/Distributed/Process/Internal/Closure/TH.hs+25−16
@@ -10,17 +10,9 @@ module Control.Distributed.Process.Internal.Closure.TH
1010import Prelude hiding (lookup)
1111import Data.ByteString.Lazy (ByteString)
1212import Data.Binary (encode, decode)
13−import Data.Typeable (typeOf, Typeable)
13+import Data.Typeable (typeOf, Typeable, TypeRep)
14+import Data.Accessor ((^=))
1415import Control.Applicative ((<$>))
15−import Control.Distributed.Process.Internal.Types
16− ( RemoteTable
17− , Closure(..)
18− , Static(..)
19− , StaticLabel(..)
20− , Process
21− , ProcessId
22− , CallReply(..)
23− )
2416import Language.Haskell.TH
2517 ( -- Q monad and operations
2618 Q
@@ -47,12 +39,21 @@ import Language.Haskell.TH
4739 , funD
4840 , sigD
4941 )
50−import Control.Distributed.Process (send)
51−import Control.Distributed.Process.Internal.Dynamic (toDyn)
52−import Control.Distributed.Process.Internal.Closure
53− ( registerLabel
54− , registerSender
42+import Control.Distributed.Process.Internal.Types
43+ ( RemoteTable
44+ , Closure(..)
45+ , Static(..)
46+ , StaticLabel(..)
47+ , Process
48+ , ProcessId
49+ , CallReply(..)
50+ , procMsg
51+ , Identifier(ProcessIdentifier)
52+ , remoteTableLabel
53+ , remoteTableSender
5554 )
55+import Control.Distributed.Process.Internal.Dynamic (Dynamic, toDyn)
56+import Control.Distributed.Process.Internal.MessageT (sendMessage)
5657
5758--------------------------------------------------------------------------------
5859-- User-level API --
@@ -139,12 +140,20 @@ generateDecoder n res = [| $(varE n) . decode :: ByteString -> $res |]
139140-- | Generate the sender. This is necessary to support 'call'
140141generateSender :: Q Type -> Q Exp
141142generateSender tp =
142− [| (\pid -> send pid . CallReply) :: ProcessId -> $tp -> Process () |]
143+ -- This duplicates the functionality of 'Process.send', but gets the TH out
144+ -- of the mutually recursive module cycle
145+ [| (\pid msg -> procMsg $ sendMessage (ProcessIdentifier pid) (CallReply msg)) :: ProcessId -> $tp -> Process () |]
143146
144147-- | The name for the function that generates the closure
145148closureName :: Name -> Name
146149closureName n = mkName $ nameBase n ++ "__closure"
147150
151+registerLabel :: String -> Dynamic -> RemoteTable -> RemoteTable
152+registerLabel label dyn = remoteTableLabel label ^= Just dyn
153+
154+registerSender :: TypeRep -> Dynamic -> RemoteTable -> RemoteTable
155+registerSender typ dyn = remoteTableSender typ ^= Just dyn
156+
148157--------------------------------------------------------------------------------
149158-- Generic Template Haskell auxiliary functions --
150159--------------------------------------------------------------------------------
distributed-process/src/Control/Distributed/Process/Internal/MessageT.hs+1−1
@@ -1,4 +1,4 @@
1−-- Add message sending capability to a monad
1+-- | Add message sending capability to a monad
22--
33-- NOTE: Not thread-safe (you should not do concurrent sends within the same
44-- monad).
distributed-process/src/Control/Distributed/Process/Internal/TypeRep.hs+1
@@ -1,3 +1,4 @@
1+-- | 'Binary' instances for 'TypeRep'
12{-# OPTIONS_GHC -fno-warn-orphans #-}
23module Control.Distributed.Process.Internal.TypeRep () where
34
distributed-process/src/Control/Distributed/Process/Internal/Types.hs+22−9
@@ -1,5 +1,8 @@
1−-- | We collect all types used internally in a single module to avoid using
2−-- mutually recursive modules
1+-- | Types used throughout the Cloud Haskell framework
2+--
3+-- We collect all types used internally in a single module because
4+-- many of these data types are mutually recursive and cannot be split across
5+-- modules.
36{-# LANGUAGE MagicHash #-}
47module Control.Distributed.Process.Internal.Types
58 ( -- * Node and process identifiers
@@ -13,6 +16,7 @@ module Control.Distributed.Process.Internal.Types
1316 , LocalProcess(..)
1417 , LocalProcessState(..)
1518 , Process(..)
19+ , procMsg
1620 -- * Typed channels
1721 , LocalChannelId
1822 , ChannelId(..)
@@ -77,6 +81,7 @@ import Control.Applicative (Applicative, (<$>), (<*>))
7781import Control.Monad.Reader (MonadReader(..), ReaderT)
7882import Control.Monad.IO.Class (MonadIO)
7983import Control.Monad.State (MonadState, StateT)
84+import qualified Control.Monad.Trans.Class as Trans (lift)
8085import Control.Distributed.Process.Serializable (Fingerprint, Serializable)
8186import Control.Distributed.Process.Internal.CQueue (CQueue)
8287import Control.Distributed.Process.Internal.Dynamic (Dynamic)
@@ -126,19 +131,24 @@ data Identifier =
126131
127132-- | Local nodes
128133data LocalNode = LocalNode
129− { localNodeId :: NodeId
134+ { -- | 'NodeId' of the node
135+ localNodeId :: NodeId
136+ -- | The network endpoint associated with this node
130137 , localEndPoint :: NT.EndPoint
131− , localState :: MVar LocalNodeState
138+ -- | Local node state
139+ , localState :: MVar LocalNodeState
140+ -- | Channel for the node controller
132141 , localCtrlChan :: Chan NCMsg
133− -- TODO: this should be part of the CH state, not the local endpoint state
134− , remoteTable :: RemoteTable
142+ -- | Runtime lookup table for supporting closures
143+ -- TODO: this should be part of the CH state, not the local endpoint state
144+ , remoteTable :: RemoteTable
135145 }
136146
137147-- | Local node state
138148data LocalNodeState = LocalNodeState
139− { _localProcesses :: Map LocalProcessId LocalProcess
140− , _localPidCounter :: Int32
141− , _localPidUnique :: Int32
149+ { _localProcesses :: Map LocalProcessId LocalProcess
150+ , _localPidCounter :: Int32
151+ , _localPidUnique :: Int32
142152 }
143153
144154-- | Processes running on our local node
@@ -163,6 +173,9 @@ newtype Process a = Process {
163173 }
164174 deriving (Functor, Monad, MonadIO, MonadReader LocalProcess, Typeable, Applicative)
165175
176+procMsg :: MessageT IO a -> Process a
177+procMsg = Process . Trans.lift
178+
166179--------------------------------------------------------------------------------
167180-- Typed channels --
168181--------------------------------------------------------------------------------
distributed-process/src/Control/Distributed/Process/Node.hs+15−15
@@ -306,8 +306,8 @@ data NCState = NCState
306306newtype NC a = NC { unNC :: StateT NCState (MessageT IO) a }
307307 deriving (Functor, Monad, MonadIO, MonadState NCState)
308308
309−lift :: MessageT IO a -> NC a
310−lift = NC . Trans.lift
309+ncMsg :: MessageT IO a -> NC a
310+ncMsg = NC . Trans.lift
311311
312312initNCState :: NCState
313313initNCState = NCState { _links = Map.empty
@@ -321,14 +321,14 @@ initNCState = NCState { _links = Map.empty
321321-- [Unified: Table 7]
322322nodeController :: NC ()
323323nodeController = do
324− node <- lift getLocalNode
324+ node <- ncMsg getLocalNode
325325 forever $ do
326326 msg <- liftIO $ readChan (localCtrlChan node)
327327
328328 -- [Unified: Table 7, rule nc_forward]
329329 case destNid (ctrlMsgSignal msg) of
330330 Just nid' | nid' /= localNodeId node ->
331− lift $ sendBinary (NodeIdentifier nid') msg
331+ ncMsg $ sendBinary (NodeIdentifier nid') msg
332332 _ ->
333333 return ()
334334
@@ -356,7 +356,7 @@ ncEffectMonitor :: ProcessId -- ^ Who's watching?
356356 -> Maybe MonitorRef -- ^ 'Nothing' to link
357357 -> NC ()
358358ncEffectMonitor from them mRef = do
359− node <- lift getLocalNode
359+ node <- ncMsg getLocalNode
360360 shouldLink <-
361361 if not (isLocal node them)
362362 then return True
@@ -370,7 +370,7 @@ ncEffectMonitor from them mRef = do
370370 (False, True) -> -- [Unified: second rule]
371371 notifyDied from them DiedNoProc mRef
372372 (False, False) -> -- [Unified: third rule]
373− lift $ sendBinary (NodeIdentifier $ processNodeId from) NCMsg
373+ ncMsg $ sendBinary (NodeIdentifier $ processNodeId from) NCMsg
374374 { ctrlMsgSender = NodeIdentifier (localNodeId node)
375375 , ctrlMsgSignal = Died (ProcessIdentifier them) DiedNoProc
376376 }
@@ -378,21 +378,21 @@ ncEffectMonitor from them mRef = do
378378-- [Unified: Table 11]
379379ncEffectUnlink :: ProcessId -> ProcessId -> NC ()
380380ncEffectUnlink from them = do
381− node <- lift getLocalNode
381+ node <- ncMsg getLocalNode
382382 when (isLocal node from) $ postMessage from $ DidUnlink them
383383 modify $ linksForProcess them ^: Set.delete from
384384
385385-- [Unified: Table 11]
386386ncEffectUnmonitor :: ProcessId -> MonitorRef -> NC ()
387387ncEffectUnmonitor from ref = do
388− node <- lift getLocalNode
388+ node <- ncMsg getLocalNode
389389 when (isLocal node from) $ postMessage from $ DidUnmonitor ref
390390 modify $ monitorsFor (monitorRefPid ref) from ^: Set.delete ref
391391
392392-- [Unified: Table 12, bottom rule]
393393ncEffectNodeDied :: NodeId -> DiedReason -> NC ()
394394ncEffectNodeDied nid reason = do
395− node <- lift getLocalNode
395+ node <- ncMsg getLocalNode
396396 lnks <- gets (^. linksForNode nid)
397397 mons <- gets (^. monitorsForNode nid)
398398
@@ -434,9 +434,9 @@ ncEffectSpawn pid cProc ref = do
434434 mProc <- unClosure cProc
435435 case mProc of
436436 Just proc -> do
437− node <- lift getLocalNode
437+ node <- ncMsg getLocalNode
438438 pid' <- liftIO $ forkProcess node proc
439− lift $ sendMessage (ProcessIdentifier pid) (DidSpawn ref pid')
439+ ncMsg $ sendMessage (ProcessIdentifier pid) (DidSpawn ref pid')
440440 Nothing ->
441441 -- TODO: what should we do when unClosure returns Nothing?
442442 liftIO . putStrLn $ "Error: closure " ++ show cProc ++ " not found"
@@ -452,7 +452,7 @@ notifyDied :: ProcessId -- ^ Who to notify?
452452 -> Maybe MonitorRef -- ^ 'Nothing' for linking
453453 -> NC ()
454454notifyDied dest src reason mRef = do
455− node <- lift getLocalNode
455+ node <- ncMsg getLocalNode
456456 case (isLocal node dest, mRef) of
457457 (True, Just ref) ->
458458 postMessage dest $ MonitorNotification ref src reason
@@ -461,7 +461,7 @@ notifyDied dest src reason mRef = do
461461 (False, _) ->
462462 -- TODO: why the change in sender? How does that affect 'reconnect' semantics?
463463 -- (see [Unified: Table 10]
464− lift $ sendBinary (NodeIdentifier $ processNodeId dest) NCMsg
464+ ncMsg $ sendBinary (NodeIdentifier $ processNodeId dest) NCMsg
465465 { ctrlMsgSender = NodeIdentifier (localNodeId node)
466466 , ctrlMsgSignal = Died (ProcessIdentifier src) reason
467467 }
@@ -494,7 +494,7 @@ isLocal nid pid = processNodeId pid == localNodeId nid
494494-- TODO: Duplication with onClosure in Process
495495unClosure :: Typeable a => Closure a -> NC (Maybe a)
496496unClosure (Closure (Static label) env) = do
497− rtable <- remoteTable <$> lift getLocalNode
497+ rtable <- remoteTable <$> ncMsg getLocalNode
498498 case resolveClosure rtable label env of
499499 Nothing -> return Nothing
500500 Just dyn -> return (fromDynamic dyn)
@@ -513,7 +513,7 @@ throwException pid e = withLocalProc pid $ \p ->
513513
514514withLocalProc :: ProcessId -> (LocalProcess -> IO ()) -> NC ()
515515withLocalProc pid p = do
516− node <- lift getLocalNode
516+ node <- ncMsg getLocalNode
517517 liftIO $ do
518518 -- By [Unified: table 6, rule missing_process] messages to dead processes
519519 -- can silently be dropped
distributed-process/src/Control/Distributed/Process/Serializable.hs+1
@@ -21,6 +21,7 @@ import qualified Data.ByteString.Internal as BSI ( unsafeCreate
2121import Foreign.Storable (pokeByteOff, peekByteOff, sizeOf)
2222import Foreign.ForeignPtr (withForeignPtr)
2323
24+-- | Objects that can be sent across the network
2425class (Binary a, Typeable a) => Serializable a
2526instance (Binary a, Typeable a) => Serializable a
2627
distributed-process/tests/TestClosure.hs+43−3
@@ -1,8 +1,9 @@
11module Main where
22
3−import Control.Monad (join)
3+import Control.Monad (join, replicateM)
44import Control.Monad.IO.Class (liftIO)
5−import Control.Concurrent (forkIO)
5+import Control.Exception (IOException, throw)
6+import Control.Concurrent (forkIO, threadDelay)
67import Control.Concurrent.MVar (MVar, newEmptyMVar, readMVar, takeMVar, putMVar)
78import Control.Applicative ((<$>))
89import Network.Transport (Transport)
@@ -39,7 +40,10 @@ returnInt = return
3940returnForTestApply :: (Closure (Int -> Process Int), Int) -> (Closure (Int -> Process Int), Int)
4041returnForTestApply = id
4142
42−$(remotable ['addInt, 'putInt, 'sendInt, 'sendPid, 'factorial, 'factorialOf, 'returnInt, 'returnForTestApply])
43+wait :: Int -> Process ()
44+wait = liftIO . threadDelay
45+
46+$(remotable ['addInt, 'putInt, 'sendInt, 'sendPid, 'factorial, 'factorialOf, 'returnInt, 'returnForTestApply, 'wait])
4347
4448testSendPureClosure :: Transport -> RemoteTable -> IO ()
4549testSendPureClosure transport rtable = do
@@ -205,6 +209,41 @@ testApply transport rtable = do
205209 foo :: Closure (Closure (Int -> Process Int), Int)
206210 foo = $(mkClosure 'returnForTestApply) ($(mkClosure 'factorialOf) (), 5)
207211
212+-- Test 'spawnSupervised'
213+--
214+-- Set up a supervisor, spawn a child, then have a third process monitor the
215+-- child. The supervisor then throws an exception, the child dies because it
216+-- was linked to the supervisor, and the third process notices that the child
217+-- dies.
218+testSpawnSupervised :: Transport -> RemoteTable -> IO ()
219+testSpawnSupervised transport rtable = do
220+ [node1, node2] <- replicateM 2 $ newLocalNode transport rtable
221+ [superPid, childPid] <- replicateM 2 $ newEmptyMVar
222+ thirdProcessDone <- newEmptyMVar
223+
224+ forkProcess node1 $ do
225+ us <- getSelfPid
226+ liftIO $ putMVar superPid us
227+ (child, _ref) <- spawnSupervised (localNodeId node2) ($(mkClosure 'wait) 1000000)
228+ liftIO $ do
229+ putMVar childPid child
230+ threadDelay 500000 -- Give the child a chance to link to us
231+ throw supervisorDeath
232+
233+ forkProcess node2 $ do
234+ [super, child] <- liftIO $ mapM readMVar [superPid, childPid]
235+ ref <- monitor child
236+ MonitorNotification ref' pid' (DiedException e) <- expect
237+ True <- return $ ref' == ref
238+ && pid' == child
239+ && e == show (LinkException super (DiedException (show supervisorDeath)))
240+ liftIO $ putMVar thirdProcessDone ()
241+
242+ takeMVar thirdProcessDone
243+ where
244+ supervisorDeath :: IOException
245+ supervisorDeath = userError "Supervisor died"
246+
208247main :: IO ()
209248main = do
210249 Right transport <- createTransport "127.0.0.1" "8080" defaultTCPParameters
@@ -219,4 +258,5 @@ main = do
219258 , ("CallBind", testCallBind transport rtable)
220259 , ("Seq", testSeq transport rtable)
221260 , ("Apply", testApply transport rtable)
261+ , ("SpawnSupervised", testSpawnSupervised transport rtable)
222262 ]