packages feed

distributed-process 0.2.1.3 → 0.2.1.4

raw patch · 11 files changed

+415/−355 lines, 11 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

- Control.Distributed.Process.Internal.MessageT: createMessage :: Serializable a => a -> Message
- Control.Distributed.Process.Internal.MessageT: data MessageT m a
- Control.Distributed.Process.Internal.MessageT: getLocalNode :: Monad m => MessageT m LocalNode
- Control.Distributed.Process.Internal.MessageT: payloadToMessage :: [ByteString] -> Message
- Control.Distributed.Process.Internal.MessageT: runMessageT :: Monad m => LocalNode -> MessageT m a -> m a
- Control.Distributed.Process.Internal.MessageT: sendBinary :: (MonadIO m, Binary a) => Identifier -> a -> MessageT m ()
- Control.Distributed.Process.Internal.MessageT: sendMessage :: (MonadIO m, Serializable a) => Identifier -> a -> MessageT m ()
- Control.Distributed.Process.Internal.MessageT: sendPayload :: MonadIO m => Identifier -> [ByteString] -> MessageT m ()
- Control.Distributed.Process.Internal.Node: runLocalProcess :: LocalNode -> Process a -> LocalProcess -> IO a
- Control.Distributed.Process.Internal.Types: MessageState :: LocalNode -> Map Identifier Connection -> MessageState
- Control.Distributed.Process.Internal.Types: MessageT :: StateT MessageState m a -> MessageT m a
- Control.Distributed.Process.Internal.Types: _messageConnections :: MessageState -> Map Identifier Connection
- Control.Distributed.Process.Internal.Types: data MessageState
- Control.Distributed.Process.Internal.Types: instance (Monad m, Functor m) => Applicative (MessageT m)
- Control.Distributed.Process.Internal.Types: instance Functor m => Functor (MessageT m)
- Control.Distributed.Process.Internal.Types: instance Monad m => Monad (MessageT m)
- Control.Distributed.Process.Internal.Types: instance Monad m => MonadState MessageState (MessageT m)
- Control.Distributed.Process.Internal.Types: instance MonadIO m => MonadIO (MessageT m)
- Control.Distributed.Process.Internal.Types: messageLocalNode :: MessageState -> LocalNode
- Control.Distributed.Process.Internal.Types: newtype MessageT m a
- Control.Distributed.Process.Internal.Types: procMsg :: MessageT IO a -> Process a
- Control.Distributed.Process.Internal.Types: unMessageT :: MessageT m a -> StateT MessageState m a
+ Control.Distributed.Process.Closure: functionTDict :: Name -> Q Exp
+ Control.Distributed.Process.Internal.Closure.TH: functionTDict :: Name -> Q Exp
+ Control.Distributed.Process.Internal.Node: sendBinary :: Binary a => LocalNode -> Identifier -> Identifier -> a -> IO ()
+ Control.Distributed.Process.Internal.Node: sendMessage :: Serializable a => LocalNode -> Identifier -> Identifier -> a -> IO ()
+ Control.Distributed.Process.Internal.Node: sendPayload :: LocalNode -> Identifier -> Identifier -> [ByteString] -> IO ()
+ Control.Distributed.Process.Internal.Types: _localConnections :: LocalNodeState -> Map (Identifier, Identifier) Connection
+ Control.Distributed.Process.Internal.Types: localConnectionBetween :: Identifier -> Identifier -> Accessor LocalNodeState (Maybe Connection)
+ Control.Distributed.Process.Internal.Types: localConnections :: Accessor LocalNodeState (Map (Identifier, Identifier) Connection)
+ Control.Distributed.Process.Internal.Types: processNode :: LocalProcess -> LocalNode
+ Control.Distributed.Process.Internal.Types: runLocalProcess :: LocalProcess -> Process a -> IO a
+ Control.Distributed.Process.Node: instance MonadReader LocalNode NC
- Control.Distributed.Process.Internal.Types: LocalNodeState :: Map LocalProcessId LocalProcess -> Int32 -> Int32 -> LocalNodeState
+ Control.Distributed.Process.Internal.Types: LocalNodeState :: Map LocalProcessId LocalProcess -> Int32 -> Int32 -> Map (Identifier, Identifier) Connection -> LocalNodeState
- Control.Distributed.Process.Internal.Types: LocalProcess :: CQueue Message -> ProcessId -> MVar LocalProcessState -> ThreadId -> LocalProcess
+ Control.Distributed.Process.Internal.Types: LocalProcess :: CQueue Message -> ProcessId -> MVar LocalProcessState -> ThreadId -> LocalNode -> LocalProcess
- Control.Distributed.Process.Internal.Types: Process :: ReaderT LocalProcess (MessageT IO) a -> Process a
+ Control.Distributed.Process.Internal.Types: Process :: ReaderT LocalProcess IO a -> Process a
- Control.Distributed.Process.Internal.Types: unProcess :: Process a -> ReaderT LocalProcess (MessageT IO) a
+ Control.Distributed.Process.Internal.Types: unProcess :: Process a -> ReaderT LocalProcess IO a

Files

distributed-process.cabal view
@@ -1,5 +1,5 @@ Name:          distributed-process -Version:       0.2.1.3+Version:       0.2.1.4 Cabal-Version: >=1.8 Build-Type:    Simple License:       BSD3 @@ -52,7 +52,6 @@                      Control.Distributed.Process.Internal.CQueue,                      Control.Distributed.Process.Internal.Dynamic,                      Control.Distributed.Process.Internal.TypeRep,-                     Control.Distributed.Process.Internal.MessageT,                      Control.Distributed.Process.Internal.Types,                      Control.Distributed.Process.Internal.Closure.Static,                      Control.Distributed.Process.Internal.Closure.MkClosure,
src/Control/Distributed/Process.hs view
@@ -89,7 +89,7 @@   , spawnMonitor   , spawnChannel   , DidSpawn(..)-    -- * Local versions of spawn+    -- * Local versions of 'spawn'    , spawnLocal   , spawnChannelLocal   ) where@@ -101,6 +101,8 @@  import Data.Typeable (Typeable) import Control.Monad.IO.Class (liftIO)+import Control.Applicative ((<$>))+import Control.Monad.Reader (ask) import Control.Distributed.Process.Internal.Types    ( RemoteTable   , NodeId(..)@@ -124,7 +126,7 @@   , SerializableDict(..)   , SendPortId(..)   , WhereIsReply(..)-  , procMsg+  , LocalProcess(processNode)   ) import Control.Distributed.Process.Internal.Closure.CP   ( cpSeq@@ -194,7 +196,6 @@   , spawnAsync   ) import Control.Distributed.Process.Serializable (Serializable)-import Control.Distributed.Process.Internal.MessageT (getLocalNode) import Control.Distributed.Process.Node (forkProcess)  -- INTERNAL NOTES@@ -369,7 +370,7 @@ -- | Spawn a process on the local node spawnLocal :: Process () -> Process ProcessId spawnLocal proc = do-  node <- procMsg $ getLocalNode+  node <- processNode <$> ask    liftIO $ forkProcess node proc  -- | Create a new typed channel, spawn a process on the local node, passing it@@ -378,7 +379,7 @@                   => (ReceivePort a -> Process ())                    -> Process (SendPort a) spawnChannelLocal proc = do -  node <- procMsg $ getLocalNode+  node <- processNode <$> ask    (sport, rport) <- newChan   _ <- liftIO $ forkProcess node (proc rport)   return sport
src/Control/Distributed/Process/Closure.hs view
@@ -12,7 +12,7 @@ -- -- you can use a Template Haskell splice to create a static version of 'f': -- --- > $(mkStatic 'f) :: forall a1 .. an. Static T+-- > $(mkStatic 'f) :: forall a1 .. an. (Typeable a1, .., Typeable an) => Static T --  -- Every module that you write that contains calls to 'mkStatic' needs to -- have a call to 'remotable':@@ -35,29 +35,90 @@ -- >            . Mn.__remoteTable -- >            $ initRemoteTable  --+-- [Dealing with type class qualifiers]+-- +-- Although 'mkStatic' supports polymorphic types, it does not support+-- qualified types. For instance, you cannot call 'mkStatic' on+--+-- > decode :: Serializable a => ByteString -> a+--+-- Instead, you will need to reify the type class dictionary. Cloud Haskell+-- comes with a reified version of 'Serializable':+--+-- > data SerializableDict a where+-- >   SerializableDict :: Serializable a => SerializableDict a+--+-- Using the reified dictionary you can define +-- +-- > decodeDict :: SerializableDict a -> ByteString -> a+-- > decodeDict SerializableDict = decode+--+-- where 'decodeDict' is a normal (unqualified) polymorphic value and hence+-- can be passed as an argument to remotable:+--+-- > $(mkStatic 'decodeDict) :: Typeable a => Static (SerializableDict a -> ByteString -> a)+-- -- [Composing static values] ----- We generalize the notion of 'static' as described in the paper, and also--- provide+-- The version of 'static' provided by this implementation of Cloud Haskell is+-- strictly more expressive than the one proposed in the paper, and additionally+-- supports  -- -- > staticApply :: Static (a -> b) -> Static a -> Static b ----- This makes it possible to define a rich set of combinators on 'static'--- values, a number of which are provided in this module.+-- This is extremely useful. For example, Cloud Haskell comes with+-- 'staticDecode' defined as  --+-- > staticDecode :: Typeable a => Static (SerializableDict a) -> Static (ByteString -> a)+-- > staticDecode dict = $(mkStatic 'decodeDict) `staticApply` dict +--+-- 'staticDecode' is used when defining closures (see below), and makes+-- essential use of 'staticApply'. +--+-- Support for 'staticApply' also makes it possible to define a rich set of+-- combinators on 'static' values, a number of which are provided in this+-- module. +--+-- [Static serialization dictionaries]+--+-- Many Cloud Haskell primitives (like 'staticDecode', above) require static+-- serialization dictionaries. In principle these dictionaries require nothing+-- special; for instance, given some serializable type 'T' you can define +--+-- > sdictT :: SerializableDict T+-- > sdictT = SerializableDict+--+-- and then have+-- +-- > $(mkStatic 'sdictT) :: Static (SerializableDict T)+--+-- However, since these dictionaries are so frequently required Cloud Haskell+-- provides special support for them.  When you call 'remotable' on a+-- /monomorphic/ function @f :: T1 -> T2@+-- +-- > remotable ['f]+--+-- then a serialization dictionary is automatically created for you, which you+-- can access with+--+-- > $(functionSDict 'f) :: Static (SerializableDict T1)+--+-- In addition, if @f :: T1 -> Process T2@, then a second dictionary is created+--+-- > $(functionTDict 'f) :: Static (SerializableDict T2)+-- -- [Closures] -- -- Suppose you have a process ----- > factorial :: Int -> Process Int+-- > isPrime :: Integer -> Process Bool  ----- Then you can use the supplied Template Haskell function 'mkClosure' to define+-- Then  ----- > factorialClosure :: Int -> Closure (Process Int)--- > factorialClosure = $(mkClosure 'factorial)+-- > $(mkClosure 'isPrime) :: Integer -> Closure (Process Bool) ----- You can then pass 'factorialClosure n' to 'spawn', for example, to have a--- remote node compute a factorial number.+-- which you can then 'call', for example, to have a remote node check if +-- a number is prime. -- -- In general, if you have a /monomorphic/ function --@@ -76,55 +137,17 @@ --  -- > data Closure a = Closure (Static (ByteString -> a)) ByteString ----- The splice @$(mkClosure 'factorial)@ above expands to (prettified a bit): +-- The splice @$(mkClosure 'isPrime)@ above expands to (prettified a bit):  -- --- > factorialClosure :: Int -> Closure (Process Int)--- > factorialClosure n = Closure decoder (encode n)--- >   where--- >     decoder :: Static (ByteString -> Process Int)--- >     decoder = $(mkStatic 'factorial) +-- > let decoder :: Static (ByteString -> Process Bool) +-- >     decoder = $(mkStatic 'isPrime)  -- >             `staticCompose`  --- >               staticDecode $(functionSDict 'factorial)------ 'mkStatic' we have already seen:------ > $(mkStatic 'factorial) :: Static (Int -> Process Int)------ 'staticCompose' is function composition on static functions. 'staticDecode'--- has type (**)------ > staticDecode :: Typeable a --- >              => Static (SerializableDict a) -> Static (ByteString -> a)------ and gives you a static decoder, given a static Serializable dictionary.--- 'SerializableDict' is a reified type class dictionary, and defined simply as--- --- > data SerializableDict a where--- >   SerializableDict :: Serializable a => SerializableDict a------ That means that for any serialziable type 'T', you can define------ > sdictForMyType :: SerializableDict T--- > sdictForMyType = SerializableDict------ and then use------ > $(mkStatic 'sdictForMyType) :: Static (SerializableDict T)------ to obtain a static serializable dictionary for 'T' (make sure to pass--- 'sdictForMyType' to 'remotable'). --- --- However, since these serialization dictionaries are so frequently required,--- when you call 'remotable' on a monomorphic function @f : T1 -> T2@--- --- > remotable ['f]------ then a serialization dictionary is automatically created for you, which you--- can access with------ > $(functionSDict 'f) :: Static (SerializableDict T1)+-- >               staticDecode $(functionSDict 'isPrime)+-- > in Closure decoder (encode n) ----- This is the dictionary that 'mkClosure' uses.+-- where 'staticCompose' is composition of static functions. Note that+-- 'mkClosure' makes use of the static serialization dictionary +-- ('functionSDict') created by 'remotable'. -- -- [Combinators on Closures] --@@ -133,6 +156,47 @@ -- the most important of which is 'cpBind'. Have a look at the implementation -- of 'Control.Distributed.Process.call' for an example use. --+-- [Example]+--+-- Here is a small self-contained example that uses closures and serialization+-- dictionaries. It makes use of the Control.Distributed.Process.SimpleLocalnet+-- Cloud Haskell backend.+--+-- > {-# LANGUAGE TemplateHaskell #-}+-- > import System.Environment (getArgs)+-- > import Control.Distributed.Process+-- > import Control.Distributed.Process.Closure+-- > import Control.Distributed.Process.Backend.SimpleLocalnet+-- > import Control.Distributed.Process.Node (initRemoteTable)+-- > +-- > isPrime :: Integer -> Process Bool+-- > isPrime n = return . (n `elem`) . takeWhile (<= n) . sieve $ [2..]+-- >   where+-- >     sieve :: [Integer] -> [Integer]+-- >     sieve (p : xs) = p : sieve [x | x <- xs, x `mod` p > 0]+-- > +-- > remotable ['isPrime]+-- > +-- > master :: [NodeId] -> Process ()+-- > master [] = liftIO $ putStrLn "no slaves"+-- > master (slave:_) = do+-- >   isPrime79 <- call $(functionTDict 'isPrime) slave ($(mkClosure 'isPrime) (79 :: Integer))+-- >   liftIO $ print isPrime79 +-- > +-- > main :: IO ()+-- > main = do+-- >   args <- getArgs+-- >   case args of+-- >     ["master", host, port] -> do+-- >       backend <- initializeBackend host port rtable +-- >       startMaster backend master +-- >     ["slave", host, port] -> do+-- >       backend <- initializeBackend host port rtable +-- >       startSlave backend+-- >   where+-- >     rtable :: RemoteTable+-- >     rtable = __remoteTable initRemoteTable +-- -- [Notes] -- -- (*) If 'T1' is not serializable you will get a type error in the generated@@ -147,11 +211,13 @@ --      instance into scope, but unless proper 'static' support is added to --      ghc we need both the type class argument and the explicit dictionary.  module Control.Distributed.Process.Closure -  ( -- * User-defined closures+  ( -- * Creating static values     remotable   , mkStatic+    -- * Template-Haskell support for creating closures   , mkClosure   , functionSDict+  , functionTDict     -- * Primitive operations on static values   , staticApply   , staticDuplicate@@ -207,6 +273,7 @@   ( remotable   , mkStatic   , functionSDict+  , functionTDict   ) import Control.Distributed.Process.Internal.Closure.Static   ( -- Static functionals
src/Control/Distributed/Process/Internal/Closure/CP.hs view
@@ -31,6 +31,7 @@ import Data.Typeable (Typeable, typeOf, typeRepTyCon, TyCon) import Control.Applicative ((<$>)) import Control.Monad ((>=>))+import Control.Monad.Reader (ask) import Control.Distributed.Process.Serializable (Serializable) import Control.Distributed.Process.Internal.Types   ( Closure(Closure)@@ -43,7 +44,7 @@   , typeOfStaticLabel   , ProcessId   , LocalNode(remoteTable)-  , procMsg+  , LocalProcess(processNode)   , SendPort   , ReceivePort   )@@ -55,7 +56,6 @@   , newChan   ) import Control.Distributed.Process.Internal.Closure.TH (remotable, mkStatic)-import Control.Distributed.Process.Internal.MessageT (getLocalNode) import Control.Distributed.Process.Internal.Closure.Resolution (resolveClosure) import Control.Distributed.Process.Internal.Closure.Static    ( staticCompose@@ -118,7 +118,7 @@ -- | Resolve a closure unClosure :: Static a -> ByteString -> Process Dynamic unClosure (Static label) env = do-  rtable <- remoteTable <$> procMsg getLocalNode +  rtable <- remoteTable . processNode <$> ask    case resolveClosure rtable label env of     Nothing  -> fail "Derived.unClosure: resolveClosure failed"     Just dyn -> return dyn
src/Control/Distributed/Process/Internal/Closure/TH.hs view
@@ -7,6 +7,7 @@     remotable   , mkStatic   , functionSDict+  , functionTDict   ) where  import Prelude hiding (lookup)@@ -47,6 +48,7 @@   , StaticLabel(StaticLabel)   , remoteTableLabel   , SerializableDict(SerializableDict)+  , Process   ) import Control.Distributed.Process.Internal.Dynamic    ( Dynamic(..)@@ -74,18 +76,19 @@ mkStatic :: Name -> Q Exp mkStatic = varE . staticName --- | Serialization dictionary for a function argument (see module header)+-- | If @f : T1 -> T2@ is a monomorphic function +-- then @$(functionSDict 'f) :: Static (SerializableDict T1)@.+-- +-- Be sure to pass 'f' to 'remotable'. functionSDict :: Name -> Q Exp functionSDict = varE . sdictName -{---- | Create a closure--- --- See module documentation header for "Control.Distributed.Process.Closure"--- for a detailed explanation and examples.-mkClosure :: Name -> Q Exp-mkClosure = varE . closureName --}+-- | If @f : T1 -> Process T2@ is a monomorphic function+-- then @$(functionTDict 'f) :: Static (SerializableDict T2)@.+--+-- Be sure to pass 'f' to 'remotable'.+functionTDict :: Name -> Q Exp+functionTDict = varE . tdictName  -------------------------------------------------------------------------------- -- Internal (Template Haskell)                                                --@@ -100,44 +103,59 @@  generateDefs :: Name -> Q ([Dec], [Q Exp]) generateDefs n = do-  mType <- getType n-  case mType of-    Just (origName, typ) -> do-      let (typVars, typ') = case typ of ForallT vars [] mono -> (vars, mono)-                                        _                    -> ([], typ)-      (static, register) <- do-        static <- generateStatic origName typVars typ' -        let dyn = case typVars of -                    [] -> [| toDyn $(varE origName) |]-                    _  -> [| Dynamic (error "Polymorphic value") -                                     (unsafeCoerce# $(varE origName)) -                           |]-        return ( static-               , [ [| registerStatic $(stringE (show origName)) $dyn |] ]+    proc <- [t| Process |]+    mType <- getType n+    case mType of+      Just (origName, typ) -> do+        let (typVars, typ') = case typ of ForallT vars [] mono -> (vars, mono)+                                          _                    -> ([], typ)++        -- The main "static" entry                                  +        (static, register) <- makeStatic origName typVars typ' +         +        -- If n :: T1 -> T2, static serializable dictionary for T1 +        -- TODO: we should check if arg is an instance of Serializable, but we cannot+        -- http://hackage.haskell.org/trac/ghc/ticket/7066+        (sdict, registerSDict) <- case (typVars, typ') of+          ([], ArrowT `AppT` arg `AppT` _res) -> +            makeDict (sdictName origName) arg+          _ -> +            return ([], [])+        +        -- If n :: T1 -> Process T2, static serializable dictionary for T2+        -- TODO: check if T2 is serializable (same as above)+        (tdict, registerTDict) <- case (typVars, typ') of+          ([], ArrowT `AppT` _arg `AppT` (proc' `AppT` res)) | proc' == proc -> +            makeDict (tdictName origName) res +          _ ->+            return ([], [])+        +        return ( concat [static, sdict, tdict]+               , concat [register, registerSDict, registerTDict]                )+      _ -> +        fail $ "remotable: " ++ show n ++ " not found"+  where+    makeStatic :: Name -> [TyVarBndr] -> Type -> Q ([Dec], [Q Exp])+    makeStatic origName typVars typ = do +      static <- generateStatic origName typVars typ+      let dyn = case typVars of +                  [] -> [| toDyn $(varE origName) |]+                  _  -> [| Dynamic (error "Polymorphic value") +                                   (unsafeCoerce# $(varE origName)) +                         |]+      return ( static+             , [ [| registerStatic $(stringE (show origName)) $dyn |] ]+             ) -      (sdict, registerSDict) <- case (typVars, typ') of-        ([], ArrowT `AppT` arg `AppT` _res) -> do-          -- TODO: we should check if arg is an instance of Serializable, but we cannot-          -- http://hackage.haskell.org/trac/ghc/ticket/7066-          sdict <- generateSDict origName arg-          let dyn' = [| toDyn (SerializableDict -                                 :: SerializableDict $(return arg)-                              ) -                      |]-          return ( sdict-                 , [ [| registerStatic $(stringE (show (sdictName origName))) -                                       $dyn' -                      |] ]-                 )-        _ ->-          return ([], [])-      -      return ( static ++ sdict-             , register ++ registerSDict+    makeDict :: Name -> Type -> Q ([Dec], [Q Exp]) +    makeDict dictName typ = do+      sdict <- generateDict dictName typ +      let dyn = [| toDyn (SerializableDict :: SerializableDict $(return typ)) |]+      return ( sdict+             , [ [| registerStatic $(stringE (show dictName)) $dyn |] ]               )-    _ -> -      fail $ "remotable: " ++ show n ++ " not found"+        registerStatic :: String -> Dynamic -> RemoteTable -> RemoteTable registerStatic label dyn = remoteTableLabel label ^= Just dyn @@ -163,14 +181,14 @@     typeable (PlainTV v)    = ClassP (mkName "Typeable") [VarT v]      typeable (KindedTV v _) = ClassP (mkName "Typeable") [VarT v] --- | Generate a function serialization dictionary-generateSDict :: Name -> Type -> Q [Dec]-generateSDict n typ = do+-- | Generate a serialization dictionary with name 'n' for type 'typ' +generateDict :: Name -> Type -> Q [Dec]+generateDict n typ = do     sequence-      [ sigD (sdictName n) $ [t| Static (SerializableDict $(return typ)) |]-      , sfnD (sdictName n) +      [ sigD n $ [t| Static (SerializableDict $(return typ)) |]+      , sfnD n           [| Static $ StaticLabel -              $(stringE (show (sdictName n))) +              $(stringE (show n))                (typeOf (undefined :: SerializableDict $(return typ)))           |]       ]@@ -180,6 +198,9 @@  sdictName :: Name -> Name sdictName n = mkName $ nameBase n ++ "__sdict"++tdictName :: Name -> Name+tdictName n = mkName $ nameBase n ++ "__tdict"  -------------------------------------------------------------------------------- -- Generic Template Haskell auxiliary functions                               --
− src/Control/Distributed/Process/Internal/MessageT.hs
@@ -1,130 +0,0 @@--- | Add message sending capability to a monad--- --- NOTE: Not thread-safe (you should not do concurrent sends within the same--- monad).-module Control.Distributed.Process.Internal.MessageT -  ( MessageT-  , runMessageT-  , getLocalNode-  , sendPayload-  , sendBinary-  , sendMessage-  , payloadToMessage-  , createMessage-  ) where--import Data.Binary (Binary, encode)-import qualified Data.ByteString as BSS (ByteString)-import qualified Data.ByteString.Lazy as BSL (toChunks)-import Data.Map (Map)-import qualified Data.Map as Map (empty)-import Data.Accessor (Accessor, accessor, (^=), (^.))-import qualified Data.Accessor.Container as DAC (mapMaybe)-import Control.Category ((>>>))-import Control.Monad (unless, liftM)-import Control.Monad.State (gets, modify, evalStateT)-import Control.Monad.IO.Class (MonadIO, liftIO)-import Control.Concurrent.Chan (writeChan)-import Control.Distributed.Process.Internal.Types-  ( Identifier(..)-  , nodeOf-  , NodeId(nodeAddress) -  , LocalNode(localCtrlChan, localEndPoint)-  , NCMsg(NCMsg, ctrlMsgSender, ctrlMsgSignal)-  , DiedReason(DiedDisconnect)-  , ProcessSignal(Died)-  , MessageT(..)-  , MessageState(..)-  , createMessage-  , messageToPayload-  , payloadToMessage-  )-import Control.Distributed.Process.Serializable (Serializable)  -import qualified Network.Transport as NT -  ( EndPoint -  , Connection-  , connect-  , send-  , Reliability(ReliableOrdered)-  , defaultConnectHints-  )------------------------------------------------------------------------------------- API                                                                        -------------------------------------------------------------------------------------runMessageT :: Monad m => LocalNode -> MessageT m a -> m a-runMessageT localNode m = -  evalStateT (unMessageT m) $ initMessageState localNode --getLocalNode :: Monad m => MessageT m LocalNode-getLocalNode = gets messageLocalNode--sendPayload :: MonadIO m => Identifier -> [BSS.ByteString] -> MessageT m ()-sendPayload them payload = do-  mConn <- connTo them-  didSend <- case mConn of-    Just conn -> do-      didSend <- liftIO $ NT.send conn payload-      case didSend of-        Left _  -> return False-        Right _ -> return True -    Nothing -> return False-  unless didSend $ do-    node <- getLocalNode-    liftIO . writeChan (localCtrlChan node) $ NCMsg-      { ctrlMsgSender = them-      , ctrlMsgSignal = Died them DiedDisconnect-      }--sendBinary :: (MonadIO m, Binary a) => Identifier -> a -> MessageT m ()-sendBinary them = sendPayload them . BSL.toChunks . encode--sendMessage :: (MonadIO m, Serializable a) => Identifier -> a -> MessageT m ()-sendMessage them = sendPayload them . messageToPayload . createMessage------------------------------------------------------------------------------------- Internal                                                                   -------------------------------------------------------------------------------------initMessageState :: LocalNode -> MessageState-initMessageState localNode = MessageState {-     messageLocalNode   = localNode -  , _messageConnections = Map.empty-  }--setupConnTo :: MonadIO m => Identifier -> MessageT m (Maybe NT.Connection)-setupConnTo them = do-    endPoint <- localEndPoint `liftM` getLocalNode -    mConn    <- liftIO $ NT.connect endPoint -                                    (nodeAddress . nodeOf $ them) -                                    NT.ReliableOrdered -                                    NT.defaultConnectHints-    case mConn of -      Right conn -> do-        didSend <- liftIO $ NT.send conn (BSL.toChunks . encode $ them)-        case didSend of-          Left _ ->-            return Nothing-          Right () -> do-            modify $ messageConnectionTo them ^= Just conn-            return $ Just conn-      Left _ ->-        return Nothing--connTo :: MonadIO m => Identifier -> MessageT m (Maybe NT.Connection)-connTo them = do-  mConn <- gets (^. messageConnectionTo them)-  case mConn of-    Just conn -> return $ Just conn-    Nothing   -> setupConnTo them------------------------------------------------------------------------------------- Accessors                                                                  -------------------------------------------------------------------------------------messageConnections :: Accessor MessageState (Map Identifier NT.Connection)-messageConnections = accessor _messageConnections (\conns st -> st { _messageConnections = conns })--messageConnectionTo :: Identifier -> Accessor MessageState (Maybe NT.Connection)-messageConnectionTo them = messageConnections >>> DAC.mapMaybe them
src/Control/Distributed/Process/Internal/Node.hs view
@@ -1,16 +1,91 @@ module Control.Distributed.Process.Internal.Node -  ( runLocalProcess+  ( -- * Message sending+    sendPayload+  , sendBinary+  , sendMessage   ) where -import Control.Monad.Reader (runReaderT)+import Data.Accessor ((^.), (^=))+import Data.Binary (Binary, encode)+import qualified Data.ByteString.Lazy as BSL (toChunks)+import qualified Data.ByteString as BSS (ByteString)+import Control.Concurrent.MVar (withMVar, modifyMVar_)+import Control.Concurrent.Chan (writeChan)+import Control.Monad (unless)+import qualified Network.Transport as NT +  ( Connection+  , send+  , defaultConnectHints+  , connect+  , Reliability(ReliableOrdered)+  ) import Control.Distributed.Process.Internal.Types -  ( LocalNode-  , LocalProcess-  , Process(unProcess)+  ( LocalNode(localState, localEndPoint, localCtrlChan)+  , Identifier+  , localConnectionBetween+  , nodeAddress+  , nodeOf+  , messageToPayload+  , createMessage+  , NCMsg(..)+  , ProcessSignal(Died)+  , DiedReason(DiedDisconnect)   )-import Control.Distributed.Process.Internal.MessageT (runMessageT)  +import Control.Distributed.Process.Serializable (Serializable) --- | Deconstructor for 'Process' (not exported to the public API) -runLocalProcess :: LocalNode -> Process a -> LocalProcess -> IO a-runLocalProcess node proc = runMessageT node . runReaderT (unProcess proc) +--------------------------------------------------------------------------------+-- Message sending                                                            -- +-------------------------------------------------------------------------------- +sendPayload :: LocalNode -> Identifier -> Identifier -> [BSS.ByteString] -> IO ()+sendPayload node from to payload = do+  mConn <- connBetween node from to+  didSend <- case mConn of+    Just conn -> do+      didSend <- NT.send conn payload+      case didSend of+        Left _  -> return False+        Right _ -> return True +    Nothing -> return False+  unless didSend $+    writeChan (localCtrlChan node) NCMsg+      { ctrlMsgSender = to +      , ctrlMsgSignal = Died to DiedDisconnect+      }++sendBinary :: Binary a => LocalNode -> Identifier -> Identifier -> a -> IO ()+sendBinary node from to = sendPayload node from to . BSL.toChunks . encode++sendMessage :: Serializable a => LocalNode -> Identifier -> Identifier -> a -> IO ()+sendMessage node from to = sendPayload node from to . messageToPayload . createMessage++setupConnBetween :: LocalNode -> Identifier -> Identifier -> IO (Maybe NT.Connection)+setupConnBetween node from to = do+    mConn    <- NT.connect endPoint +                           (nodeAddress . nodeOf $ to) +                           NT.ReliableOrdered +                           NT.defaultConnectHints+    case mConn of +      Right conn -> do+        didSend <- NT.send conn (BSL.toChunks . encode $ to)+        case didSend of+          Left _ ->+            return Nothing+          Right () -> do+            modifyMVar_ nodeState $ return . +              (localConnectionBetween from to ^= Just conn)+            return $ Just conn+      Left _ ->+        return Nothing+  where+    endPoint  = localEndPoint node+    nodeState = localState node++connBetween :: LocalNode -> Identifier -> Identifier -> IO (Maybe NT.Connection)+connBetween node from to = do+    mConn <- withMVar nodeState $ return . (^. localConnectionBetween from to)+    case mConn of+      Just conn -> return $ Just conn+      Nothing   -> setupConnBetween node from to +  where+    nodeState = localState node
src/Control/Distributed/Process/Internal/Primitives.hs view
@@ -68,7 +68,7 @@ import Control.Monad.IO.Class (MonadIO, liftIO) import Control.Applicative ((<$>)) import Control.Exception (Exception, throw)-import qualified Control.Exception as Exception (catch)+import qualified Control.Exception as Ex (catch) import Control.Concurrent.MVar (modifyMVar) import Control.Concurrent.Chan (writeChan) import Control.Concurrent.STM @@ -106,7 +106,6 @@   , TypedChannel(..)   , SendPortId(..)   , Identifier(..)-  , procMsg   , DidUnmonitor(..)   , DidUnlinkProcess(..)   , DidUnlinkNode(..)@@ -114,13 +113,9 @@   , WhereIsReply(..)   , createMessage   , Static(..)+  , runLocalProcess   )-import Control.Distributed.Process.Internal.MessageT -  ( sendMessage-  , sendBinary-  , getLocalNode-  )  -import Control.Distributed.Process.Internal.Node (runLocalProcess)+import Control.Distributed.Process.Internal.Node (sendMessage, sendBinary)  import Control.Distributed.Process.Internal.Closure.Resolution (resolveClosure) import Control.Distributed.Process.Internal.Dynamic (fromDyn, dynTypeRep) @@ -132,7 +127,12 @@ send :: Serializable a => ProcessId -> a -> Process () -- This requires a lookup on every send. If we want to avoid that we need to -- modify serializable to allow for stateful (IO) deserialization-send them msg = procMsg $ sendMessage (ProcessIdentifier them) msg +send them msg = do+  proc <- ask +  liftIO $ sendMessage (processNode proc) +                       (ProcessIdentifier (processId proc)) +                       (ProcessIdentifier them)+                       msg  -- | Wait for a message of a specific type expect :: forall a. Serializable a => Process a@@ -163,7 +163,12 @@  -- | Send a message on a typed channel sendChan :: Serializable a => SendPort a -> a -> Process ()-sendChan (SendPort cid) msg = procMsg $ sendBinary (SendPortIdentifier cid) msg +sendChan (SendPort cid) msg = do+  proc <- ask+  liftIO $ sendBinary (processNode proc)+                      (ProcessIdentifier (processId proc))+                      (SendPortIdentifier cid) +                      msg   -- | Wait for a message on a typed channel receiveChan :: Serializable a => ReceivePort a -> Process a@@ -261,7 +266,7 @@  -- | Get the node ID of our local node getSelfNode :: Process NodeId-getSelfNode = localNodeId <$> procMsg getLocalNode+getSelfNode = localNodeId . processNode <$> ask   -------------------------------------------------------------------------------- -- Monitoring and linking                                                     --@@ -318,11 +323,8 @@ -- | Catch exceptions within a process catch :: Exception e => Process a -> (e -> Process a) -> Process a catch p h = do-  node  <- procMsg getLocalNode   lproc <- ask-  let run :: Process a -> IO a-      run proc = runLocalProcess node proc lproc -  liftIO $ Exception.catch (run p) (run . h) +  liftIO $ Ex.catch (runLocalProcess lproc p) (runLocalProcess lproc . h)   -- | Like 'expect' but with a timeout expectTimeout :: forall a. Serializable a => Int -> Process (Maybe a)@@ -460,7 +462,7 @@ -- | Deserialize a closure unClosure :: forall a. Typeable a => Closure a -> Process a unClosure (Closure (Static label) env) = do-    rtable <- remoteTable <$> procMsg getLocalNode +    rtable <- remoteTable . processNode <$> ask      case resolveClosure rtable label env of       Nothing  -> throw . userError $ "Unregistered closure " ++ show label       Just dyn -> return $ fromDyn dyn (throw (typeError dyn))@@ -507,14 +509,16 @@             -> ProcessSignal -- ^ Message to send              -> Process () sendCtrlMsg mNid signal = do            -  us <- getSelfPid-  let msg = NCMsg { ctrlMsgSender = ProcessIdentifier us+  proc <- ask+  let msg = NCMsg { ctrlMsgSender = ProcessIdentifier (processId proc)                    , ctrlMsgSignal = signal                   }   case mNid of     Nothing -> do-      ctrlChan <- localCtrlChan <$> procMsg getLocalNode +      ctrlChan <- localCtrlChan . processNode <$> ask        liftIO $ writeChan ctrlChan msg      Just nid ->-      procMsg $ sendBinary (NodeIdentifier nid) msg-+      liftIO $ sendBinary (processNode proc)+                          (ProcessIdentifier (processId proc))+                          (NodeIdentifier nid)+                          msg
src/Control/Distributed/Process/Internal/Types.hs view
@@ -17,7 +17,7 @@   , LocalProcess(..)   , LocalProcessState(..)   , Process(..)-  , procMsg+  , runLocalProcess     -- * Typed channels   , LocalSendPortId    , SendPortId(..)@@ -58,14 +58,13 @@     -- * Node controller internal data types    , NCMsg(..)   , ProcessSignal(..)-    -- * MessageT monad-  , MessageT(..)-  , MessageState(..)     -- * Accessors   , localProcesses   , localPidCounter   , localPidUnique+  , localConnections   , localProcessWithId+  , localConnectionBetween   , monitorCounter   , spawnCounter   , channelCounter@@ -97,10 +96,8 @@ import Control.Concurrent.STM (TChan, TVar) import qualified Network.Transport as NT (EndPoint, EndPointAddress, Connection) import Control.Applicative (Applicative, (<$>), (<*>))-import Control.Monad.Reader (MonadReader(..), ReaderT)+import Control.Monad.Reader (MonadReader(..), ReaderT, runReaderT) import Control.Monad.IO.Class (MonadIO)-import Control.Monad.State (MonadState, StateT)-import qualified Control.Monad.Trans.Class as Trans (lift) import Control.Distributed.Process.Serializable    ( Fingerprint   , Serializable@@ -131,10 +128,7 @@   { lpidUnique  :: Int32   , lpidCounter :: Int32   }-  deriving (Eq, Ord, Typeable)--instance Show LocalProcessId where-  show = show . lpidCounter +  deriving (Eq, Ord, Typeable, Show)  -- | Process identifier data ProcessId = ProcessId @@ -187,9 +181,10 @@  -- | Local node state data LocalNodeState = LocalNodeState -  { _localProcesses  :: Map LocalProcessId LocalProcess-  , _localPidCounter :: Int32-  , _localPidUnique  :: Int32+  { _localProcesses   :: Map LocalProcessId LocalProcess+  , _localPidCounter  :: Int32+  , _localPidUnique   :: Int32+  , _localConnections :: Map (Identifier, Identifier) NT.Connection   }  -- | Processes running on our local node@@ -198,8 +193,13 @@   , processId     :: ProcessId   , processState  :: MVar LocalProcessState   , processThread :: ThreadId+  , processNode   :: LocalNode   } +-- | Deconstructor for 'Process' (not exported to the public API) +runLocalProcess :: LocalProcess -> Process a -> IO a+runLocalProcess lproc proc = runReaderT (unProcess proc) lproc+ -- | Local process state data LocalProcessState = LocalProcessState   { _monitorCounter :: Int32@@ -210,13 +210,10 @@  -- | The Cloud Haskell 'Process' type newtype Process a = Process { -    unProcess :: ReaderT LocalProcess (MessageT IO) a +    unProcess :: ReaderT LocalProcess IO a    }   deriving (Functor, Monad, MonadIO, MonadReader LocalProcess, Typeable, Applicative) -procMsg :: MessageT IO a -> Process a-procMsg = Process . Trans.lift - -------------------------------------------------------------------------------- -- Typed channels                                                             -- --------------------------------------------------------------------------------@@ -574,9 +571,15 @@ localPidUnique :: Accessor LocalNodeState Int32 localPidUnique = accessor _localPidUnique (\unq st -> st { _localPidUnique = unq }) +localConnections :: Accessor LocalNodeState (Map (Identifier, Identifier) NT.Connection)+localConnections = accessor _localConnections (\conns st -> st { _localConnections = conns })+ localProcessWithId :: LocalProcessId -> Accessor LocalNodeState (Maybe LocalProcess) localProcessWithId lpid = localProcesses >>> DAC.mapMaybe lpid +localConnectionBetween :: Identifier -> Identifier -> Accessor LocalNodeState (Maybe NT.Connection)+localConnectionBetween from to = localConnections >>> DAC.mapMaybe (from, to)+ monitorCounter :: Accessor LocalProcessState Int32 monitorCounter = accessor _monitorCounter (\cnt st -> st { _monitorCounter = cnt }) @@ -597,15 +600,3 @@  remoteTableLabel :: String -> Accessor RemoteTable (Maybe Dynamic) remoteTableLabel label = remoteTableLabels >>> DAC.mapMaybe label------------------------------------------------------------------------------------- MessageT monad                                                             -------------------------------------------------------------------------------------newtype MessageT m a = MessageT { unMessageT :: StateT MessageState m a }-  deriving (Functor, Monad, MonadIO, MonadState MessageState, Applicative)--data MessageState = MessageState { -     messageLocalNode   :: LocalNode-  , _messageConnections :: Map Identifier NT.Connection -  }
src/Control/Distributed/Process/Node.hs view
@@ -38,7 +38,7 @@ import Control.Monad (void, when, forever) import Control.Monad.IO.Class (MonadIO, liftIO) import Control.Monad.State (MonadState, StateT, evalStateT, gets, modify)-import qualified Control.Monad.Trans.Class as Trans (lift)+import Control.Monad.Reader (MonadReader, ReaderT, runReaderT, ask) import Control.Exception (throwIO, SomeException, Exception, throwTo) import qualified Control.Exception as Exception (catch) import Control.Concurrent (forkIO)@@ -101,7 +101,6 @@   , Closure(..)   , Static(..)   , Message-  , MessageT   , TypedChannel(..)   , Identifier(..)   , nodeOf@@ -110,20 +109,18 @@   , WhereIsReply(..)   , messageToPayload   , RemoteTable(..)+  , payloadToMessage+  , createMessage+  , runLocalProcess   ) import Control.Distributed.Process.Serializable (Serializable)-import Control.Distributed.Process.Internal.MessageT +import Control.Distributed.Process.Internal.Dynamic (fromDynamic)+import Control.Distributed.Process.Internal.Closure.Resolution (resolveClosure) +import Control.Distributed.Process.Internal.Node    ( sendBinary   , sendMessage   , sendPayload-  , getLocalNode-  , runMessageT-  , payloadToMessage-  , createMessage   )-import Control.Distributed.Process.Internal.Dynamic (fromDynamic)-import Control.Distributed.Process.Internal.Closure.Resolution (resolveClosure) -import Control.Distributed.Process.Internal.Node (runLocalProcess) import Control.Distributed.Process.Internal.Primitives (expect, register) import qualified Control.Distributed.Process.Internal.Closure.Static as Static (__remoteTable) import qualified Control.Distributed.Process.Internal.Closure.CP as CP (__remoteTable)@@ -156,9 +153,10 @@ createBareLocalNode endPoint rtable = do   unq <- randomIO   state <- newMVar LocalNodeState -    { _localProcesses      = Map.empty-    , _localPidCounter     = 0 -    , _localPidUnique      = unq +    { _localProcesses   = Map.empty+    , _localPidCounter  = 0 +    , _localPidUnique   = unq +    , _localConnections = Map.empty     }   ctrlChan <- newChan   let node = LocalNode { localNodeId   = NodeId $ NT.address endPoint@@ -214,10 +212,11 @@                              , processId     = pid                              , processState  = pst                               , processThread = tid+                             , processNode   = node                              }     tid' <- forkIO $ do       reason <- Exception.catch -        (runLocalProcess node proc lproc >> return DiedNormal)+        (runLocalProcess lproc proc >> return DiedNormal)         (return . DiedException . (show :: SomeException -> String))       -- [Unified: Table 4, rules termination and exiting]       modifyMVar_ (localState node) $ @@ -287,7 +286,7 @@                          (Map.insert cid proc procs)                          chans                          ctrls-                    Nothing ->+                    Nothing ->                        -- Request for an unknown process.                        --                       -- TODO: We should treat this as a fatal error on the@@ -367,8 +366,8 @@ --------------------------------------------------------------------------------  runNodeController :: LocalNode -> IO ()-runNodeController node =-  runMessageT node (evalStateT (unNC nodeController) initNCState)+runNodeController =+  runReaderT (evalStateT (unNC nodeController) initNCState)  -------------------------------------------------------------------------------- -- Internal data types                                                        --@@ -383,11 +382,8 @@   , _registry :: Map String ProcessId   } -newtype NC a = NC { unNC :: StateT NCState (MessageT IO) a }-  deriving (Functor, Monad, MonadIO, MonadState NCState)--ncMsg :: MessageT IO a -> NC a-ncMsg = NC . Trans.lift+newtype NC a = NC { unNC :: StateT NCState (ReaderT LocalNode IO) a }+  deriving (Functor, Monad, MonadIO, MonadState NCState, MonadReader LocalNode)  initNCState :: NCState initNCState = NCState { _links    = Map.empty@@ -402,14 +398,17 @@ -- [Unified: Table 7] nodeController :: NC () nodeController = do-  node <- ncMsg getLocalNode +  node <- ask    forever $ do     msg  <- liftIO $ readChan (localCtrlChan node)      -- [Unified: Table 7, rule nc_forward]      case destNid (ctrlMsgSignal msg) of       Just nid' | nid' /= localNodeId node -> -        ncMsg $ sendBinary (NodeIdentifier nid') msg+        liftIO $ sendBinary node+                            (ctrlMsgSender msg)+                            (NodeIdentifier nid') +                            msg       _ ->          return () @@ -441,7 +440,7 @@                 -> Maybe MonitorRef -- ^ 'Nothing' to link                 -> NC () ncEffectMonitor from them mRef = do-  node <- ncMsg getLocalNode +  node <- ask    shouldLink <-      if not (isLocal node them)        then return True@@ -454,15 +453,21 @@     (False, True) -> -- [Unified: second rule]       notifyDied from them DiedUnknownId mRef      (False, False) -> -- [Unified: third rule]-      ncMsg $ sendBinary (NodeIdentifier $ processNodeId from) NCMsg -        { ctrlMsgSender = NodeIdentifier (localNodeId node)-        , ctrlMsgSignal = Died them DiedUnknownId-        }+      -- TODO: this is the right sender according to the Unified semantics,+      -- but perhaps having 'them' as the sender would make more sense+      -- (see also: notifyDied)+      liftIO $ sendBinary node+                          (NodeIdentifier $ localNodeId node)+                          (NodeIdentifier $ processNodeId from)+        NCMsg  +          { ctrlMsgSender = NodeIdentifier (localNodeId node)+          , ctrlMsgSignal = Died them DiedUnknownId+          }  -- [Unified: Table 11] ncEffectUnlink :: ProcessId -> Identifier -> NC () ncEffectUnlink from them = do-  node <- ncMsg getLocalNode +  node <- ask    when (isLocal node (ProcessIdentifier from)) $      case them of       ProcessIdentifier pid -> @@ -476,7 +481,7 @@ -- [Unified: Table 11] ncEffectUnmonitor :: ProcessId -> MonitorRef -> NC () ncEffectUnmonitor from ref = do-  node <- ncMsg getLocalNode +  node <- ask    when (isLocal node (ProcessIdentifier from)) $      postAsMessage from $ DidUnmonitor ref   modify $ monitorsFor (monitorRefIdent ref) ^: Set.delete (from, ref)@@ -484,7 +489,7 @@ -- [Unified: Table 12] ncEffectDied :: Identifier -> DiedReason -> NC () ncEffectDied ident reason = do-  node <- ncMsg getLocalNode+  node <- ask   (affectedLinks, unaffectedLinks) <- gets (splitNotif ident . (^. links))   (affectedMons,  unaffectedMons)  <- gets (splitNotif ident . (^. monitors)) @@ -509,9 +514,12 @@   -- If the closure does not exist, we spawn a process that throws an exception   -- This allows the remote node to find out what's happening   let proc = fromMaybe (fail $ "Error: unknown closure " ++ show cProc) mProc-  node <- ncMsg getLocalNode+  node <- ask   pid' <- liftIO $ forkProcess node proc-  ncMsg $ sendMessage (ProcessIdentifier pid) (DidSpawn ref pid') +  liftIO $ sendMessage node+                       (NodeIdentifier (localNodeId node))+                       (ProcessIdentifier pid) +                       (DidSpawn ref pid')   -- Unified semantics does not explicitly describe how to implement 'register', -- but mentions it's "very similar to nsend" (Table 14)@@ -525,18 +533,24 @@ -- Unified semantics does not explicitly describe 'whereis' ncEffectWhereIs :: ProcessId -> String -> NC () ncEffectWhereIs from label = do+  node <- ask   mPid <- gets (^. registryFor label)-  ncMsg $ sendMessage (ProcessIdentifier from) (WhereIsReply label mPid)+  liftIO $ sendMessage node+                       (NodeIdentifier (localNodeId node))+                       (ProcessIdentifier from) +                       (WhereIsReply label mPid)  -- [Unified: Table 14] ncEffectNamedSend :: Identifier -> String -> Message -> NC ()-ncEffectNamedSend _from label msg = do+ncEffectNamedSend from label msg = do   mPid <- gets (^. registryFor label)   -- If mPid is Nothing, we just ignore the named send (as per Table 14)-  -- TODO: messages don't carry a "from", but if they do, we should set it-  -- to be the 'from' of the original named send, not this node controller+  node <- ask   forM_ mPid $ \pid -> -    ncMsg $ sendPayload (ProcessIdentifier pid) (messageToPayload msg) +    liftIO $ sendPayload node+                         from+                         (ProcessIdentifier pid) +                         (messageToPayload msg)   -------------------------------------------------------------------------------- -- Auxiliary                                                                  --@@ -548,7 +562,7 @@            -> Maybe MonitorRef  -- ^ 'Nothing' for linking            -> NC () notifyDied dest src reason mRef = do-  node <- ncMsg getLocalNode +  node <- ask    case (isLocal node (ProcessIdentifier dest), mRef, src) of     (True, Just ref, ProcessIdentifier pid) ->       postAsMessage dest $ ProcessMonitorNotification ref pid reason @@ -565,10 +579,13 @@     (False, _, _) ->       -- TODO: why the change in sender? How does that affect 'reconnect' semantics?       -- (see [Unified: Table 10]-      ncMsg $ sendBinary (NodeIdentifier $ processNodeId dest) NCMsg-        { ctrlMsgSender = NodeIdentifier (localNodeId node) -        , ctrlMsgSignal = Died src reason-        }+      liftIO $ sendBinary node+                          (NodeIdentifier $ localNodeId node)+                          (NodeIdentifier $ processNodeId dest)+        NCMsg+          { ctrlMsgSender = NodeIdentifier (localNodeId node) +          , ctrlMsgSignal = Died src reason+          }        -- | [Unified: Table 8] destNid :: ProcessSignal -> Maybe NodeId@@ -592,13 +609,13 @@ -- | Lookup a local closure  unClosure :: Typeable a => Closure a -> NC (Maybe a) unClosure (Closure (Static label) env) = do-  rtable <- remoteTable <$> ncMsg getLocalNode+  rtable <- remoteTable <$> ask   return (resolveClosure rtable label env >>= fromDynamic)  -- | Check if an identifier refers to a valid local object isValidLocalIdentifier :: Identifier -> NC Bool isValidLocalIdentifier ident = do-  node <- ncMsg getLocalNode+  node <- ask   liftIO . withMVar (localState node) $ \nSt ->      case ident of       NodeIdentifier nid ->@@ -631,7 +648,7 @@  withLocalProc :: ProcessId -> (LocalProcess -> IO ()) -> NC ()  withLocalProc pid p = do-  node <- ncMsg getLocalNode +  node <- ask    liftIO $ do      -- By [Unified: table 6, rule missing_process] messages to dead processes     -- can silently be dropped
tests/TestClosure.hs view
@@ -36,6 +36,12 @@ wait :: Int -> Process () wait = liftIO . threadDelay +isPrime :: Integer -> Process Bool+isPrime n = return . (n `elem`) . takeWhile (<= n) . sieve $ [2..]+  where+    sieve :: [Integer] -> [Integer]+    sieve (p : xs) = p : sieve [x | x <- xs, x `mod` p > 0]+ -- | First argument indicates empty closure environment typedPingServer :: () -> ReceivePort (SendPort ()) -> Process () typedPingServer () rport = forever $ do@@ -49,6 +55,7 @@           , 'sdictInt           , 'wait           , 'typedPingServer+          , 'isPrime           ]  factorialClosure :: Int -> Closure (Process Int)@@ -296,6 +303,13 @@     sendChan pingServer sendReply     receiveChan receiveReply +testTDict :: Transport -> RemoteTable -> IO ()+testTDict transport rtable = do+  [node1, node2] <- replicateM 2 $ newLocalNode transport rtable+  runProcess node1 $ do+    True <- call $(functionTDict 'isPrime) (localNodeId node2) ($(mkClosure 'isPrime) (79 :: Integer))+    return ()+ main :: IO () main = do   Right transport <- createTransport "127.0.0.1" "8080" defaultTCPParameters@@ -314,4 +328,5 @@     , ("SpawnInvalid",    testSpawnInvalid    transport rtable)     , ("ClosureExpect",   testClosureExpect   transport rtable)     , ("SpawnChannel",    testSpawnChannel    transport rtable)+    , ("TDict",           testTDict           transport rtable)     ]