git.iko.soy/distributed-process
on master help sign in
Merge remote-tracking branch 'origin/master'

Conflicts: .gitignore

commit8f0e1afa3e7b74735daec857ff9de411f8fb6424 authorNicolas Wu <nick@well-typed.com> dateTue 3 Jan 2012, 08:49 (15 years ago) parentcf2f2ee307d0d1
9 files changed+238−87
.gitignore+2−2 distributed-process/src/Control/Distributed/Process.hs+2 examples/DemoIP.hs−50 examples/DemoProcess.hs+7−6 examples/DemoTCP.hs+56 examples/DemoTransport.hs+70−15 network-transport/network-transport.cabal+3−1 network-transport/src/Network/Transport/MVar.hs+1−2 network-transport/src/Network/Transport/TCP.hs+97−11
.gitignore+2−2
@@ -1,5 +1,5 @@
1−*~
2−*#
31tags
42TAGS
53dist/
4+\#*#
5+*~
distributed-process/src/Control/Distributed/Process.hs+2
@@ -109,6 +109,8 @@ newLocalNode trans = do
109109 ndTransport = trans
110110 }
111111
112+-- | `runProcess` executes a process on a given node, and waits for the process
113+-- to finish before returning.
112114runProcess :: LocalNode -> Process () -> IO ()
113115runProcess node proc = do
114116 waitVar <- newEmptyMVar
examples/DemoIP.hsdeleted−50
@@ -1,50 +0,0 @@
1−module Main where
2−
3−import Network.Transport
4−import Network.Transport.IP
5−
6−import System.Environment
7−import Control.Monad
8−import qualified Data.ByteString.Char8 as BS
9−
10−main :: IO ()
11−main = do
12− [mode, numberStr, addressStr, portStr] <- getArgs
13−
14− let number = read numberStr
15− let address = Address addressStr
16− let port = read portStr
17−
18− transport <- initTransport (Config undefined)
19−
20− case mode of
21− "master" -> do
22− (clients, masterReceiveEnd) <- demoMaster number transport address port
23− zipWithM_ (\clientSendEnd clientId -> send clientSendEnd [BS.pack . show $ clientId])
24− clients [0 .. number-1]
25− replicateM_ number $ do
26− [clientId] <- receive masterReceiveEnd
27− print clientId
28− "slave" -> do
29− (masterSendEnd, slaveReceiveEnd) <- demoSlave transport address port
30− [clientId] <- receive slaveReceiveEnd
31− let message = "Connected to slave: " ++ BS.unpack clientId
32− send masterSendEnd [BS.pack message]
33−
34−demoMaster :: Int -> Transport -> Address -> Port -> IO ([SendEnd], ReceiveEnd)
35−demoMaster numSlaves transport address port = do
36− receiveEnd <- newReceiveEnd address port
37− sendEnds <- replicateM numSlaves $ do
38− [bytes] <- receive receiveEnd
39− case deserialize transport bytes of
40− Nothing -> fail "Garbage message from slave"
41− Just sendAddr -> connect sendAddr
42− return (sendEnds, receiveEnd)
43−
44−demoSlave :: Transport -> Address -> Port -> IO (SendEnd, ReceiveEnd)
45−demoSlave transport address port = do
46− (selfSendAddr, selfReceiveEnd) <- newConnection transport
47− sendEnd <- connect (newSendAddr address port)
48− send sendEnd [serialize selfSendAddr]
49− return (sendEnd, selfReceiveEnd)
50−
examples/DemoProcess.hs+7−6
@@ -3,18 +3,19 @@ module DemoProcess where
33import Control.Distributed.Process
44import Control.Concurrent (threadDelay)
55
6−import Network.Transport.MVar (mkTransport)
6+import Network.Transport.TCP (mkTransport, TCPConfig (..))
7+
8+import Debug.Trace
79
810demo1 = do
9− trans <- mkTransport
11+ trans <- mkTransport $ TCPConfig undefined "127.0.0.1" "8080"
1012 node <- newLocalNode trans
11− print "foo"
13+ print "Starting node"
1214 runProcess node $ do
13− liftIO $ print "bar"
15+ liftIO $ print "Starting process"
1416 lpid1 <- spawnLocal (logger 1)
1517-- liftIO $ print "baz"
1618-- lpid2 <- spawnLocal (logger 2)
17−
1819 spawnLocal (chatty "jim1" lpid1)
1920-- spawnLocal (chatty "jim2" lpid2)
2021 spawnLocal (chatty "bob1" lpid1)
@@ -31,5 +32,5 @@ chatty name target = do
3132logger :: Int -> Process ()
3233logger n = do
3334 str <- expect
34− liftIO $ putStrLn $ show n ++ ": " ++ str
35+ liftIO . putStrLn $ show n ++ ": " ++ str
3536 logger (n+1)
examples/DemoTCP.hsnew file+56
@@ -0,0 +1,56 @@
1+module Main where
2+
3+import Network.Transport
4+import Network.Transport.TCP
5+
6+import System.Environment
7+import Control.Monad
8+import qualified Data.ByteString.Char8 as BS
9+
10+main :: IO ()
11+main = do
12+ [mode, numberStr, host, service] <- getArgs
13+
14+ let number = read numberStr
15+
16+ transport <- initTransport (Config undefined)
17+
18+ case mode of
19+ "master" -> do
20+ (clients, masterReceiveEnd) <- demoMaster number transport host service
21+ zipWithM_
22+ (\clientSendEnd clientId -> send clientSendEnd [BS.pack . show $ clientId])
23+ clients [0 .. number-1]
24+ replicateM_ number $ do
25+ [clientMessage] <- receive masterReceiveEnd
26+ print clientMessage
27+ "slave" -> do
28+ (masterSendEnd, slaveReceiveEnd) <- demoSlave transport host service
29+ [clientId] <- receive slaveReceiveEnd
30+ let message = "Connected to slave: " ++ BS.unpack clientId
31+ send masterSendEnd [BS.pack message]
32+
33+demoMaster :: Int -- ^ Number of slaves
34+ -> Transport -- ^ Transport
35+ -> HostName -- ^ HostName of the master node
36+ -> ServiceName -- ^ ServiceName of the master node
37+ -> IO ([SendEnd], ReceiveEnd) -- ^
38+demoMaster numSlaves transport host service = do
39+ receiveEnd <- newReceiveEnd host service
40+ sendEnds <- replicateM numSlaves $ do
41+ [bytes] <- receive receiveEnd
42+ case deserialize transport bytes of
43+ Nothing -> fail "Garbage message from slave"
44+ Just sendAddr -> connect sendAddr
45+ return (sendEnds, receiveEnd)
46+
47+demoSlave :: Transport -- ^ Transport
48+ -> HostName -- ^
49+ -> ServiceName -- ^
50+ -> IO (SendEnd, ReceiveEnd) -- ^
51+demoSlave transport host service = do
52+ (selfSendAddr, selfReceiveEnd) <- newConnection transport
53+ sendEnd <- connect (newSendAddr host service)
54+ send sendEnd [serialize selfSendAddr]
55+ return (sendEnd, selfReceiveEnd)
56+
examples/DemoTransport.hs+70−15
@@ -1,23 +1,44 @@
11module DemoTransport where
22
33import Network.Transport
4−import Network.Transport.MVar (mkTransport)
4+import qualified Network.Transport.MVar
5+import Network.Transport.TCP (mkTransport, TCPConfig (..))
56
67import Control.Concurrent
8+import Control.Monad
9+
710import qualified Data.ByteString.Char8 as BS
811
12+import Debug.Trace
13+
914-------------------------------------------
1015-- Example program using backend directly
1116--
1217
13−-- check the endpoint serialisation works
18+-- | Check if multiple messages can be sent on the same connection.
19+demo0 :: IO ()
20+demo0 = do
21+ trans <- mkTransport $ TCPConfig undefined "127.0.0.1" "8080"
22+
23+ (sendAddr, receiveEnd) <- newConnection trans
24+
25+ forkIO $ logServer "logServer" receiveEnd
26+ threadDelay 100000
27+
28+ sendEnd <- connect sendAddr
29+
30+ mapM_ (\n -> send sendEnd [BS.pack ("hello " ++ show n)]) [1 .. 10]
31+ threadDelay 100000
32+
33+-- | Check endpoint serialization and deserialization.
1434demo1 :: IO ()
1535demo1 = do
16− trans <- mkTransport
36+ -- trans <- mkTransport
37+ trans <- mkTransport $ TCPConfig undefined "127.0.0.1" "8080"
1738
1839 (sendAddr, receiveEnd) <- newConnection trans
1940
20− forkIO $ logServer receiveEnd
41+ forkIO $ logServer "logServer" receiveEnd
2142 threadDelay 100000
2243
2344 sendEnd <- connect sendAddr
@@ -30,24 +51,58 @@ demo1 = do
3051 send sendEnd' [BS.pack "hello 2"]
3152 threadDelay 100000
3253
33−-- check that we can send before any corresponding receive
54+-- | Check that messages can be sent before receive is set up.
3455demo2 :: IO ()
3556demo2 = do
36− trans <- mkTransport
57+ -- trans <- mkTransport
58+ trans <- mkTransport $ TCPConfig undefined "127.0.0.1" "8080"
3759
3860 (sendAddr, receiveEnd) <- newConnection trans
3961
4062 sendEnd <- connect sendAddr
41−
4263 forkIO $ send sendEnd [BS.pack "hello 1"]
4364 threadDelay 100000
44− forkIO $ logServer receiveEnd
45− threadDelay 100000
46− forkIO $ send sendEnd [BS.pack "hello 2"]
47− threadDelay 100000
4865
49−logServer :: ReceiveEnd -> IO ()
50−logServer receiveEnd = do
66+ forkIO $ logServer "logServer" receiveEnd
67+ return ()
68+
69+-- | Check that two different transports can be created.
70+demo3 :: IO ()
71+demo3 = do
72+ trans1 <- mkTransport $ TCPConfig undefined "127.0.0.1" "8080"
73+ trans2 <- mkTransport $ TCPConfig undefined "127.0.0.1" "8081"
74+
75+ (sendAddr1, receiveEnd1) <- newConnection trans1
76+ (sendAddr2, receiveEnd2) <- newConnection trans2
77+
78+ forkIO $ logServer "logServer1" receiveEnd1
79+ forkIO $ logServer "logServer2" receiveEnd2
80+
81+ sendEnd1 <- connect sendAddr1
82+ sendEnd2 <- connect sendAddr2
83+
84+ send sendEnd1 [BS.pack "hello1"]
85+ send sendEnd2 [BS.pack "hello2"]
86+
87+-- | Check that two different connections on the same transport can be created.
88+demo4 :: IO ()
89+demo4 = do
90+ trans <- mkTransport $ TCPConfig undefined "127.0.0.1" "8080"
91+ -- trans <- Network.Transport.MVar.mkTransport
92+
93+ (sendAddr1, receiveEnd1) <- newConnection trans
94+ (sendAddr2, receiveEnd2) <- newConnection trans
95+
96+ forkIO $ logServer "logServer1" receiveEnd1
97+ forkIO $ logServer "logServer2" receiveEnd2
98+
99+ sendEnd1 <- connect sendAddr1
100+ sendEnd2 <- connect sendAddr2
101+
102+ send sendEnd1 [BS.pack "hello1"]
103+ send sendEnd2 [BS.pack "hello2"]
104+
105+logServer :: String -> ReceiveEnd -> IO ()
106+logServer name receiveEnd = forever $ do
51107 x <- receive receiveEnd
52− print x
53− logServer receiveEnd
108+ trace (name ++ ": " ++ show x) $ return ()
network-transport/network-transport.cabal+3−1
@@ -12,7 +12,9 @@ Cabal-Version: >=1.2
1212Library
1313 Build-Depends: base >= 3 && < 5,
1414 bytestring >= 0.9,
15− containers >= 0.4
15+ containers >= 0.4,
16+ network >= 2.3,
17+ safe >= 0.3
1618 Exposed-modules: Network.Transport,
1719 Network.Transport.MVar,
1820 Network.Transport.TCP
network-transport/src/Network/Transport/MVar.hs+1−2
@@ -36,10 +36,9 @@ mkTransport = do
3636 where
3737 mkSendAddr :: Chans -> Int -> SendAddr
3838 mkSendAddr channels addr = SendAddr
39− { connectWith = \_ -> mkSendEnd channels $ addr
39+ { connectWith = \_ -> mkSendEnd channels addr
4040 , serialize = BS.pack (show addr)
4141 }
42− -- mkSendEnd channels sendAddr
4342
4443 mkSendEnd :: Chans -> Int -> IO SendEnd
4544 mkSendEnd channels addr = do
network-transport/src/Network/Transport/TCP.hs+97−11
@@ -1,28 +1,114 @@
11module Network.Transport.TCP
22 ( mkTransport
3+ , TCPConfig (..)
34 ) where
45
56import Network.Transport
7+
8+import Control.Concurrent (forkIO)
9+import Control.Concurrent.Chan
10+import Control.Concurrent.MVar
11+import Control.Monad (forever, unless)
12+import Data.ByteString.Char8 (ByteString)
13+import Data.IntMap (IntMap)
614import Data.Word
15+import Network.Socket
16+import Safe
17+
18+import qualified Data.ByteString.Char8 as BS
19+import qualified Data.IntMap as IntMap
20+import qualified Network.Socket as N
21+import qualified Network.Socket.ByteString as NBS
722
8−data Address = Address String
9−type Port = Word16
23+type Chans = MVar (Int, IntMap (Chan ByteString))
24+type ChanId = Int
1025
1126-- | This deals with several different configuration properties:
1227-- * Buffer size, specified in Hints
1328-- * LAN/WAN, since we can inspect the addresses
14−data TCPConfig = TCPConfig Hints Port Address Address
29+-- Note that HostName could be an IP address, and ServiceName could be
30+-- a port number
31+data TCPConfig = TCPConfig Hints HostName ServiceName
1532
33+-- | This creates a TCP connection between a server and a number of
34+-- clients. Behind the scenes, the server hostname is passed as the SendAddr
35+-- and when a connection is made, messages sent down the SendEnd go
36+-- via a socket made for the client that connected.
37+-- Messages are all queued using an unbounded Chan.
1638mkTransport :: TCPConfig -> IO Transport
17−mkTransport tcpConfig =
18− -- create buffer
19− -- allocate listening socket
20− -- fork thread on that socket
39+mkTransport (TCPConfig _hints host service) = withSocketsDo $ do
40+ channels <- newMVar (0, IntMap.empty)
41+ serverAddrs <- getAddrInfo
42+ (Just (N.defaultHints { addrFlags = [AI_PASSIVE] }))
43+ Nothing
44+ (Just service)
45+ let serverAddr = case serverAddrs of
46+ [] -> error "mkTransport: getAddrInfo returned []"
47+ as -> head as
48+ sock <- socket (addrFamily serverAddr) Stream defaultProtocol
49+ bindSocket sock (addrAddress serverAddr)
50+ listen sock 5
51+ forkIO $ procConnections channels sock
52+
2153 return Transport
22− { newConnectionWith = undefined
23− -- create listening mailbox
24− -- send back mailbox details
54+ { newConnectionWith = \_ -> do
55+ (chanId, chanMap) <- takeMVar channels
56+ chan <- newChan
57+ putMVar channels (chanId + 1, IntMap.insert chanId chan chanMap)
58+ return (mkSendAddr host service chanId, mkReceiveEnd chan)
2559 , newMulticastWith = undefined
26− , deserialize = undefined
60+ , deserialize = \bs ->
61+ case readMay . BS.unpack $ bs of
62+ Nothing -> error "deserialize: cannot parse"
63+ Just (host, service, chanId) -> Just $ mkSendAddr host service chanId
2764 }
2865
66+ where
67+ mkSendAddr :: HostName -> ServiceName -> ChanId -> SendAddr
68+ mkSendAddr host service chanId = SendAddr
69+ { connectWith = \_ -> mkSendEnd chanId
70+ , serialize = BS.pack . show $ (host, service, chanId)
71+ }
72+
73+ mkSendEnd :: ChanId -> IO SendEnd
74+ mkSendEnd chanId = withSocketsDo $ do
75+ serverAddrs <- getAddrInfo Nothing (Just host) (Just service)
76+ let serverAddr = case serverAddrs of
77+ [] -> error "mkSendEnd: getAddrInfo returned []"
78+ as -> head as
79+ sock <- socket (addrFamily serverAddr) Stream defaultProtocol
80+ setSocketOption sock KeepAlive 1
81+ N.connect sock (addrAddress serverAddr)
82+ NBS.sendAll sock $ BS.pack . show $ chanId
83+ return $ SendEnd
84+ { Network.Transport.send = \bss -> NBS.sendMany sock bss
85+ }
86+
87+ mkReceiveEnd :: Chan ByteString -> ReceiveEnd
88+ mkReceiveEnd chan = ReceiveEnd
89+ { -- for now we will implement this as a Chan
90+ receive = do
91+ bs <- readChan chan
92+ return [bs]
93+ }
94+
95+ procConnections :: Chans -> Socket -> IO ()
96+ procConnections chans sock = forever $ do
97+ (clientSock, _clientAddr) <- accept sock
98+ -- decode the first message to find the correct chanId
99+ bs <- NBS.recv clientSock 4096
100+ case BS.readInt bs of
101+ Nothing -> error "procConnections: cannot parse chanId"
102+ Just (chanId, bs') -> do
103+ -- lookup the channel
104+ (_, chanMap) <- readMVar chans
105+ case IntMap.lookup chanId chanMap of
106+ Nothing -> error "procConnections: cannot find chanId"
107+ Just chan -> forkIO $ do
108+ unless (BS.null bs') $ writeChan chan bs'
109+ procMessages chan clientSock
110+
111+ procMessages :: Chan ByteString -> Socket -> IO ()
112+ procMessages chan sock = forever $ do
113+ bs <- NBS.recv sock 4096
114+ writeChan chan bs