git.iko.soy/distributed-process
on master help sign in
Merge pull request #5 from acfoltzer/master

FIXED: Switched from Binary to cereal, allowed switching between lazy and strict ByteString, fixed some bugs

commitacbc141c5f8b033fb3a7d6f19e43f81c3b9288fc authorNicolas Wu <zenzike@gmail.com> dateFri 24 Feb 2012, 02:27 (15 years ago) parent95bf372f5494b3
10 files changed+266−122
benchmarks/PingTCP.hs+26−7 benchmarks/PingTransport.hs+27−8 benchmarks/SendTCP.hs+23−5 benchmarks/SendTransport.hs+31−11 examples/DemoTCP.hs+6−3 network-transport-pipes/src/Network/Transport/Pipes.hs+12−13 network-transport/network-transport.cabal+1 network-transport/src/Network/Transport.hs+6 network-transport/src/Network/Transport/MVar.hs+22−6 network-transport/src/Network/Transport/TCP.hs+112−69
benchmarks/PingTCP.hs+26−7
@@ -1,9 +1,13 @@
1+{-# LANGUAGE CPP #-}
2+
13module Main where
24
35import Control.Monad
46import Criterion.Main (Benchmark, bench, defaultMain, nfIO, bgroup)
5−import Data.Binary
7+
68import Data.Int
9+import qualified Data.Serialize as Ser
10+import Data.Word (Word8)
711import Network.Socket
812 ( AddrInfoFlag (AI_PASSIVE), HostName, ServiceName, Socket
913 , SocketType (Stream), SocketOption (ReuseAddr)
@@ -13,10 +17,25 @@ import Network.Socket
1317import System.Environment (getArgs, withArgs)
1418
1519import qualified Network.Socket as N
16−import qualified Network.Socket.ByteString.Lazy as NBS
1720
1821import Debug.Trace
1922
23+#ifndef LAZY
24+import Data.ByteString (ByteString)
25+import qualified Network.Socket.ByteString as NBS
26+encode = Ser.encode
27+decode = Ser.decode
28+#else
29+import Data.ByteString.Lazy (ByteString)
30+import qualified Network.Socket.ByteString.Lazy as NBS
31+encode = Ser.encodeLazy
32+decode = Ser.decodeLazy
33+#endif
34+{-# INLINE encode #-}
35+{-# INLINE decode #-}
36+encode :: Ser.Serialize a => a -> ByteString
37+decode :: Ser.Serialize a => ByteString -> Either String a
38+
2039-- | This performs a ping benchmark on a TCP connection created by
2140-- Network.Socket. To compile this file, you might use:
2241--
@@ -71,15 +90,15 @@ main = do
7190
7291-- | Each `ping` sends a single byte, and expects to receive one
7392-- back in return.
74−ping :: Socket -> IO Word8
93+ping :: Socket -> IO Int64
7594ping sock = do
76− NBS.send sock $ encode (0 :: Word8)
77− bs <- NBS.recv sock 1
78− return $ decode bs
95+ NBS.send sock $ encode (0 :: Int64)
96+ bs <- NBS.recv sock 8
97+ either error return $ decode bs
7998
8099pong :: Socket -> IO ()
81100pong sock = do
82− bs <- NBS.recv sock 1
101+ bs <- NBS.recv sock 8
83102 NBS.sendAll sock bs
84103 return ()
85104
benchmarks/PingTransport.hs+27−8
@@ -1,3 +1,5 @@
1+{-# LANGUAGE CPP #-}
2+
13module Main where
24
35import Network.Transport
@@ -5,12 +7,28 @@ import Network.Transport.TCP (mkTransport, TCPConfig (..))
57
68import Control.Monad (forever, replicateM_)
79import Criterion.Main (Benchmark, bench, defaultMain, nfIO)
8−import Data.Binary
10+import qualified Data.Serialize as Ser
911import Data.Maybe (fromJust)
1012import Data.Int
1113import System.Environment (getArgs, withArgs)
1214
15+#ifndef LAZY
16+import qualified Data.ByteString.Char8 as BS
17+import qualified Network.Socket.ByteString as NBS
18+encode = Ser.encode
19+decode = Ser.decode
20+#else
1321import qualified Data.ByteString.Lazy.Char8 as BS
22+import qualified Network.Socket.ByteString.Lazy as NBS
23+encode = Ser.encodeLazy
24+decode = Ser.decodeLazy
25+#endif
26+{-# INLINE encode #-}
27+{-# INLINE decode #-}
28+encode :: Ser.Serialize a => a -> BS.ByteString
29+decode :: Ser.Serialize a => BS.ByteString -> Either String a
30+
31+
1432
1533-- | This performs a ping benchmark on the TCP transport. This can be
1634-- compiled using:
@@ -64,8 +82,8 @@ main = do
6482 send sourceEndPing [serialize sourceAddrPong]
6583
6684 -- benchmark the pings
67−-- withArgs args' $ defaultMain [ benchPing sourceEndPing targetEndPong pings]
68− replicateM_ pings (ping sourceEndPing targetEndPong)
85+ withArgs args' $ defaultMain [ benchPing sourceEndPing targetEndPong pings]
86+-- replicateM_ pings (ping sourceEndPing targetEndPong)
6987
7088-- | This function takes a `TargetEnd` for the pings, and a `SourceEnd` for
7189-- pongs. Whenever a ping is received from the `TargetEnd`, a pong is sent
@@ -78,16 +96,17 @@ pong targetEndPing sourceEndPong = do
7896-- | The effect of `ping sourceEndPing targetEndPong n` is to send the number
7997-- `n` using `sourceEndPing`, and to then receive the a number from
8098-- `targetEndPong`, which is then returned.
81−ping :: SourceEnd -> TargetEnd -> IO Int64
82−ping sourceEndPing targetEndPong = do
83− send sourceEndPing [BS.empty]
99+ping :: SourceEnd -> TargetEnd -> Int64 -> IO Int64
100+ping sourceEndPing targetEndPong n = do
101+ send sourceEndPing [encode n]
84102 [bs] <- receive targetEndPong
85− return $ decode bs
103+ let (Right n) = decode bs
104+ return $! n
86105
87106-- | The effect of `benchPing sourceEndPing targetEndPong n` is to send
88107-- `n` pings down `sourceEndPing` using the `ping` function. The time
89108-- taken is benchmarked.
90109benchPing :: SourceEnd -> TargetEnd -> Int64 -> Benchmark
91110benchPing sourceEndPing targetEndPong n = bench "PingTransport" $
92− nfIO (replicateM_ (fromIntegral n) (ping sourceEndPing targetEndPong))
111+ nfIO (replicateM_ (fromIntegral n) (ping sourceEndPing targetEndPong 42))
93112
benchmarks/SendTCP.hs+23−5
@@ -1,10 +1,12 @@
1+{-# LANGUAGE CPP #-}
2+
13module Main where
24
35import Control.Monad
46import Criterion.Main (Benchmark, bench, defaultMain, nfIO, bgroup)
5−import Data.Binary
6−import Data.ByteString.Lazy (ByteString)
77import Data.Int
8+import qualified Data.Serialize as Ser
9+import Data.Word (Word8)
810import Network.Socket
911 ( AddrInfoFlag (AI_PASSIVE), HostName, ServiceName, Socket
1012 , SocketType (Stream), SocketOption (ReuseAddr)
@@ -13,9 +15,25 @@ import Network.Socket
1315 , getAddrInfo, listen, setSocketOption, socket, sClose, withSocketsDo )
1416import System.Environment (getArgs, withArgs)
1517
16−import qualified Data.ByteString.Lazy as BS
1718import qualified Network.Socket as N
19+
20+#ifdef LAZY
21+import Data.ByteString (ByteString)
22+import qualified Data.ByteString as BS
23+import qualified Network.Socket.ByteString as NBS
24+encode = Ser.encode
25+decode = Ser.decode
26+#else
27+import Data.ByteString.Lazy (ByteString)
28+import qualified Data.ByteString.Lazy as BS
1829import qualified Network.Socket.ByteString.Lazy as NBS
30+encode = Ser.encodeLazy
31+decode = Ser.decodeLazy
32+#endif
33+{-# INLINE encode #-}
34+{-# INLINE decode #-}
35+encode :: Ser.Serialize a => a -> ByteString
36+decode :: Ser.Serialize a => ByteString -> Either String a
1937
2038-- | This performs a benchmark on a TCP connection to measure how long it takes
2139-- to transfer a number of bytes.
@@ -78,9 +96,9 @@ ping :: Socket -> ByteString -> IO Word8
7896ping sock bs = do
7997 NBS.send sock bs
8098 bs' <- NBS.recv sock 1
81− return $ decode bs
99+ either error return $ decode bs
82100
83−pong :: Socket -> Int64 -> IO ()
101+-- pong :: Socket -> Int64 -> IO ()
84102pong sock size = do
85103 bs <- NBS.recv sock size
86104 NBS.sendAll sock (encode (0 :: Word8))
benchmarks/SendTransport.hs+31−11
@@ -1,17 +1,37 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE NoMonomorphismRestriction #-}
3+
14module Main where
25
36import Network.Transport
47import Network.Transport.TCP (mkTransport, TCPConfig (..))
58
6−import Control.Monad (forever, replicateM_)
9+import Control.Applicative
10+import Control.Monad (forever, replicateM, replicateM_)
711import Criterion.Main (Benchmark, bench, defaultMain, nfIO)
8−import Data.Binary
9−import Data.ByteString.Lazy (ByteString)
12+import qualified Data.Serialize as Ser
1013import Data.Maybe (fromJust)
1114import Data.Int
1215import System.Environment (getArgs, withArgs)
13−
14−import qualified Data.ByteString.Lazy as BS
16+import System.Random
17+
18+#ifndef LAZY
19+import Data.ByteString (ByteString)
20+import qualified Data.ByteString.Char8 as BS
21+import qualified Network.Socket.ByteString as NBS
22+encode = Ser.encode
23+decode = Ser.decode
24+#else
25+import Data.ByteString.Lazy (ByteString)
26+import qualified Data.ByteString.Lazy.Char8 as BS
27+import qualified Network.Socket.ByteString.Lazy as NBS
28+encode = Ser.encodeLazy
29+decode = Ser.decodeLazy
30+#endif
31+{-# INLINE encode #-}
32+{-# INLINE decode #-}
33+encode :: Ser.Serialize a => a -> ByteString
34+decode :: Ser.Serialize a => ByteString -> Either String a
1535
1636-- | This performs a benchmark on the TCP transport to measure how long
1737-- it takes to transfer a number of bytes. This can be compiled using:
@@ -67,17 +87,17 @@ main = do
6787 send sourceEndPing [serialize sourceAddrPong]
6888
6989 -- benchmark the data
70− let bs = BS.replicate size 0
90+ bs <- BS.pack <$> replicateM size randomIO
7191 withArgs args' $ defaultMain [ benchSend sourceEndPing targetEndPong bs ]
7292
7393-- | The effect of `ping sourceEndPing targetEndPong bs` is to send the
7494-- `ByteString` `bs` using `sourceEndPing`, and to then receive
75−-- a single byte back from `targetEndPong`.
76−ping :: SourceEnd -> TargetEnd -> ByteString -> IO Word8
95+-- that string back. Returns the length of the received message.
96+ping :: SourceEnd -> TargetEnd -> ByteString -> IO Int
7797ping sourceEndPing targetEndPong bs = do
7898 send sourceEndPing [bs]
79− [cs] <- receive targetEndPong
80− return $ decode cs
99+ cs <- receive targetEndPong
100+ return . fromIntegral $! sum (map BS.length cs)
81101
82102-- | This function takes a `TargetEnd` for the pings, and a `SourceEnd` for
83103-- pongs. Whenever a ping is received from the `TargetEnd`, a pong is sent
@@ -85,7 +105,7 @@ ping sourceEndPing targetEndPong bs = do
85105pong :: TargetEnd -> SourceEnd -> IO ()
86106pong targetEndPing sourceEndPong = do
87107 bs <- receive targetEndPing
88− send sourceEndPong [encode (0 :: Word8)]
108+ send sourceEndPong bs
89109
90110-- | The effect of `benchSend sourceEndPing targetEndPong bs` is to send
91111-- `bs` pings down `sourceEndPing` using the `ping` function. The time
examples/DemoTCP.hs+6−3
@@ -4,10 +4,12 @@ import Network.Transport
44import Network.Transport.TCP
55
66import Control.Monad
7+import Data.Char
8+import Data.String
79import Network.Socket (HostName, ServiceName)
810import System.Environment
911
10−import qualified Data.ByteString.Lazy.Char8 as BS
12+import qualified Data.ByteString.Char8 as BS
1113
1214import Debug.Trace
1315
@@ -39,7 +41,8 @@ main = do
3941 transport <- mkTransport $ TCPConfig defaultHints host service
4042 (clientSourceEnds, masterTargetEnd) <- demoMaster transport sourceAddrFilePath numSlaves
4143 zipWithM_
42− (\clientSourceEnd clientId -> send clientSourceEnd [BS.pack . show $ clientId])
44+ (\clientSourceEnd clientId ->
45+ send clientSourceEnd [BS.pack . show $ clientId])
4346 clientSourceEnds [0 .. numSlaves-1]
4447 replicateM_ numSlaves $ do
4548 [clientMessage] <- receive masterTargetEnd
@@ -56,7 +59,7 @@ main = do
5659 (masterSourceEnd, slaveTargetEnd) <- demoSlave transport sourceAddrFilePath
5760 [clientId] <- receive slaveTargetEnd
5861 putStrLn $ "slave: received clientId: " ++ show clientId
59− let message = "Connected to slave: " ++ BS.unpack clientId
62+ let message = "Connected to slave: " ++ (BS.unpack clientId)
6063
6164 send masterSourceEnd [BS.pack message]
6265 putStrLn "slave: sent message to master"
network-transport-pipes/src/Network/Transport/Pipes.hs+12−13
@@ -25,8 +25,7 @@ import Data.Word
2525import Data.Int
2626import Data.IORef
2727import qualified Data.IntMap as IntMap
28−import qualified Data.ByteString.Char8 as BSS
29−import qualified Data.ByteString.Lazy.Char8 as BS
28+import qualified Data.ByteString.Char8 as BS
3029
3130-- For some STRANGE reason this is not working with Data.Binary [2012.02.20]:
3231#define CEREAL
@@ -54,18 +53,18 @@ import qualified "unix-bytestring" System.Posix.IO.ByteString as PIO
5453import System.Posix.IO as PIO (openFd, closeFd, -- append, exclusive, noctty, nonBlock, trunc,
5554 OpenFileFlags(..), OpenMode(ReadOnly, WriteOnly))
5655-- (fromS,toS) = (BS.pack, BS.unpack)
57−(fromS,toS) = (BSS.pack, BSS.unpack)
56+(fromS,toS) = (BS.pack, BS.unpack)
5857fromBS = id
5958readit fd n = PIO.fdRead fd n
6059#else
6160import qualified System.Posix.IO as PIO
6261(toS,fromS) = (id,id)
63−fromBS = BSS.unpack
62+fromBS = BS.unpack
6463readit fd n = do (s,_) <- PIO.fdRead fd n
65− return (BSS.pack s)
64+ return (BS.pack s)
6665#endif
6766-- readit :: Fd -> Int -> IO BS.ByteString
68−readit :: Fd -> ByteCount -> IO BSS.ByteString
67+readit :: Fd -> ByteCount -> IO BS.ByteString
6968
7069----------------------------------------------------------------------------------------------------
7170
@@ -135,7 +134,7 @@ mkTransport = do
135134 -- Otherwise it's just a simple write:
136135 -- We append the length as a header. TODO - REMOVE EXTRA COPY HERE:
137136
138− let hdrbss :: BSS.ByteString
137+ let hdrbss :: BS.ByteString
139138#ifdef CEREAL
140139 hdrbss = encode msgsize
141140#else
@@ -144,13 +143,13 @@ mkTransport = do
144143#endif
145144
146145-- let finalmsg = BS.concat (encode msgsize : bss)
147− let finalmsg = BSS.concat (hdrbss : concatMap BS.toChunks bsls)
146+ let finalmsg = BS.concat (hdrbss : bsls)
148147
149148 fd <- readMVar mv -- Synchronize with file opening.
150149 ----------------------------------------
151150 cnt <- PIO.fdWrite fd (fromBS finalmsg) -- inefficient to use String here!
152− unless (fromIntegral cnt == BSS.length finalmsg) $
153− error$ "Failed to write message in one go, length: "++ show (BSS.length finalmsg)
151+ unless (fromIntegral cnt == BS.length finalmsg) $
152+ error$ "Failed to write message in one go, length: "++ show (BS.length finalmsg)
154153 ----------------------------------------
155154 return ()
156155 , closeSourceEnd = do
@@ -167,13 +166,13 @@ mkTransport = do
167166 -- happen on multiple threads so we grab a lock.
168167 takeMVar lock
169168
170− let spinread :: Int -> IO BSS.ByteString
169+ let spinread :: Int -> IO BS.ByteString
171170 spinread desired = do
172171
173172 bs <- tryUntilNoIOErr$
174173 readit fd (fromIntegral desired)
175174
176− case BSS.length bs of
175+ case BS.length bs of
177176 n | n == desired -> return bs
178177 0 -> do threadDelay (10*1000)
179178 spinread desired
@@ -193,7 +192,7 @@ mkTransport = do
193192 Left err -> error$ "ERROR: "++ err
194193 Right size -> spinread (fromIntegral (size::Word32))
195194 putMVar lock ()
196− return [BS.fromChunks [payload]] -- How terribly listy.
195+ return [payload] -- How terribly listy.
197196 , closeTargetEnd = do
198197 fd <- readMVar mv
199198 PIO.closeFd fd
network-transport/network-transport.cabal+1
@@ -13,6 +13,7 @@ Library
1313 Build-Depends: base >= 3 && < 5,
1414 binary >= 0.5,
1515 bytestring >= 0.9,
16+ cereal >= 0.3,
1617 containers >= 0.4,
1718 network >= 2.3,
1819 safe >= 0.3,
network-transport/src/Network/Transport.hs+6
@@ -1,3 +1,5 @@
1+{-# LANGUAGE CPP #-}
2+
13module Network.Transport
24 ( Hints (..)
35 , TargetEnd (..)
@@ -12,7 +14,11 @@ module Network.Transport
1214 , newMulticast
1315 ) where
1416
17+#ifndef LAZY
18+import Data.ByteString.Char8 (ByteString)
19+#else
1520import Data.ByteString.Lazy.Char8 (ByteString)
21+#endif
1622
1723-- | The `Hints` and `SourceHints` provide hints to the underlying transport
1824-- about the kind of connection that is required. This might include details
network-transport/src/Network/Transport/MVar.hs+22−6
@@ -1,4 +1,5 @@
11{-# LANGUAGE BangPatterns #-}
2+{-# LANGUAGE CPP #-}
23
34module Network.Transport.MVar
45 ( mkTransport
@@ -6,13 +7,28 @@ module Network.Transport.MVar
67
78import Control.Concurrent.MVar
89import Data.IntMap (IntMap)
9−import Data.ByteString.Lazy.Char8 (ByteString)
10+import qualified Data.Serialize as Ser
1011
1112import qualified Data.IntMap as IntMap
12−import qualified Data.ByteString.Lazy.Char8 as BS
1313
1414import Network.Transport
1515
16+#ifndef LAZY
17+import Data.ByteString.Char8 (ByteString)
18+import qualified Data.ByteString.Char8 as BS
19+encode = Ser.encode
20+decode = Ser.decode
21+#else
22+import Data.ByteString.Lazy.Char8 (ByteString)
23+import qualified Data.ByteString.Lazy.Char8 as BS
24+encode = Ser.encodeLazy
25+decode = Ser.decodeLazy
26+#endif
27+{-# INLINE encode #-}
28+{-# INLINE decode #-}
29+encode :: Ser.Serialize a => a -> ByteString
30+decode :: Ser.Serialize a => ByteString -> Either String a
31+
1632type Chans = MVar (Int, IntMap (MVar [ByteString]))
1733
1834mkTransport :: IO Transport
@@ -29,16 +45,16 @@ mkTransport = do
2945 return (mkSourceAddr channels sourceAddr, mkTargetEnd receiveChan)
3046 , newMulticastWith = undefined
3147 , deserialize = \bs ->
32− case BS.readInt bs of
33− Nothing -> error "dummyBackend.deserializeSourceEnd: cannot parse"
34− Just (n,_) -> Just . mkSourceAddr channels $ n
48+ either (error "dummyBackend.deserializeSourceEnd: cannot parse")
49+ (Just . mkSourceAddr channels)
50+ (decode bs)
3551 , closeTransport = return ()
3652 }
3753 where
3854 mkSourceAddr :: Chans -> Int -> SourceAddr
3955 mkSourceAddr channels addr = SourceAddr
4056 { connectWith = \_ -> mkSourceEnd channels addr
41− , serialize = BS.pack (show addr)
57+ , serialize = encode addr
4258 }
4359
4460 mkSourceEnd :: Chans -> Int -> IO SourceEnd
network-transport/src/Network/Transport/TCP.hs+112−69
@@ -1,3 +1,5 @@
1+{-# LANGUAGE CPP #-}
2+
13module Network.Transport.TCP
24 ( mkTransport
35 , TCPConfig (..)
@@ -5,30 +7,58 @@ module Network.Transport.TCP
57
68import Network.Transport
79
10+import Control.Applicative
811import Control.Concurrent (forkIO, ThreadId, killThread, myThreadId)
912import Control.Concurrent.Chan
1013import Control.Concurrent.MVar
1114import Control.Exception (SomeException, IOException, AsyncException(ThreadKilled),
1215 fromException, throwTo, throw, catch, handle)
1316import Control.Monad (forever, forM_)
14−import Data.ByteString.Lazy.Char8 (ByteString)
17+
18+import qualified Data.ByteString.Internal as BSI
19+
1520import Data.IntMap (IntMap)
16−import Data.Int
17−import Data.Word
21+import qualified Data.IntMap as IntMap
22+
23+import Data.Int (Int32)
24+import qualified Data.Serialize as Ser
25+import Data.Word (Word8)
26+
27+import Foreign.ForeignPtr (withForeignPtr)
28+import Foreign.Ptr (Ptr, plusPtr)
29+import Foreign.Storable (sizeOf)
30+
1831import Network.Socket
1932 ( AddrInfoFlag (AI_PASSIVE), HostName, ServiceName, Socket
2033 , SocketType (Stream), SocketOption (ReuseAddr)
2134 , accept, addrAddress, addrFlags, addrFamily, bindSocket, defaultProtocol
2235 , getAddrInfo, listen, setSocketOption, socket, sClose, withSocketsDo )
36+import qualified Network.Socket as N
37+
38+import Text.Printf
39+
2340import Safe
2441import System.IO (stderr, hPutStrLn)
2542
26−import qualified Data.Binary as B
27−import qualified Data.ByteString.Char8 as BSS
43+#ifndef LAZY
44+import Data.ByteString.Char8 (ByteString)
45+import qualified Data.ByteString.Char8 as BS
46+import qualified Network.Socket.ByteString as NBS
47+sendNBS = NBS.sendMany
48+encode = Ser.encode
49+decode = Ser.decode
50+#else
51+import Data.ByteString.Lazy.Char8 (ByteString)
2852import qualified Data.ByteString.Lazy.Char8 as BS
29−import qualified Data.IntMap as IntMap
30−import qualified Network.Socket as N
3153import qualified Network.Socket.ByteString.Lazy as NBS
54+sendNBS sock = NBS.sendAll sock . BS.concat
55+encode = Ser.encodeLazy
56+decode = Ser.decodeLazy
57+#endif
58+{-# INLINE encode #-}
59+{-# INLINE decode #-}
60+encode :: Ser.Serialize a => a -> ByteString
61+decode :: Ser.Serialize a => ByteString -> Either String a
3262
3363type ChanId = Int
3464type Chans = MVar (ChanId, IntMap (Chan [ByteString], [(ThreadId, Socket)]))
@@ -70,7 +100,9 @@ mkTransport (TCPConfig _hints host service) = withSocketsDo $ do
70100 return (mkSourceAddr host service chanId, mkTargetEnd chans chanId chan)
71101 , newMulticastWith = error "newMulticastWith: not defined"
72102 , deserialize = {-# SCC "deserialize" #-} \bs ->
73− let (host, service, chanId) = B.decode bs in
103+ let (host, service, chanId) =
104+ either (\m -> error $ printf "deserialize: %s" m)
105+ id $ decode bs in
74106 Just $ mkSourceAddr host service chanId
75107 , closeTransport = {-# SCC "closeTransport" #-} do
76108 -- Kill the transport channel process
@@ -87,29 +119,26 @@ mkTransport (TCPConfig _hints host service) = withSocketsDo $ do
87119mkSourceAddr :: HostName -> ServiceName -> ChanId -> SourceAddr
88120mkSourceAddr host service chanId = SourceAddr
89121 { connectWith = {-# SCC "connectWith" #-} \_ -> mkSourceEnd host service chanId
90− , serialize = {-# SCC "serialize" #-} B.encode (host, service, chanId)
122+ , serialize = {-# SCC "serialize" #-} encode (host, service, chanId)
91123 }
92124
93125mkSourceEnd :: HostName -> ServiceName -> ChanId -> IO SourceEnd
94126mkSourceEnd host service chanId = withSocketsDo $ do
127+ let err m = error $ printf "mkSourceEnd: %s" m
95128 serverAddrs <- getAddrInfo Nothing (Just host) (Just service)
96129 let serverAddr = case serverAddrs of
97− [] -> error "mkSourceEnd: getAddrInfo returned []"
130+ [] -> err "getAddrInfo returned []"
98131 as -> head as
99132 sock <- socket (addrFamily serverAddr) Stream defaultProtocol
100133 setSocketOption sock ReuseAddr 1
101134 N.connect sock (addrAddress serverAddr)
102− NBS.sendAll sock $ B.encode (fromIntegral chanId :: Int64)
135+ NBS.sendAll sock $ encode (fromIntegral chanId :: Int32)
103136 return SourceEnd
104137 { send = {-# SCC "send" #-} \bss -> do
105− let size = fromIntegral (sum . map BS.length $ bss) :: Int64
106− if size < 255
107− then
108− NBS.sendAll sock (B.encode (fromIntegral size :: Word8))
109− else do
110− NBS.sendAll sock (B.encode (255 :: Word8))
111− NBS.sendAll sock (B.encode size)
112− mapM_ (NBS.sendAll sock) bss
138+ let size = fromIntegral (sum . map BS.length $ bss) :: Int32
139+ sizeStr | size < 255 = BS.singleton (toEnum $ fromEnum size)
140+ | otherwise = BS.cons (toEnum 255) (encode size)
141+ sendNBS sock (sizeStr:bss)
113142 , closeSourceEnd = {-# SCC "closeSourceEnd" #-} sClose sock
114143 }
115144
@@ -133,19 +162,19 @@ mkTargetEnd chans chanId chan = TargetEnd
133162-- for some reason, an error is raised.
134163procConnections :: Chans -> Socket -> IO ()
135164procConnections chans sock = forever $ do
165+ let err m = error $ printf "procConnections: %s" m
136166 (clientSock, _clientAddr) <- accept sock
137167 -- decode the first message to find the correct chanId
138− bss <- recvExact clientSock 8
168+ bss <- recvExact clientSock (fromIntegral $ sizeOf (0 :: Int32))
139169 case bss of
140− [] -> error "procConnections: inbound chanId aborted"
141− bss -> do
142− let bs = BS.concat bss
143− let chanId = fromIntegral (B.decode bs :: Int64)
170+ Left _ -> err "inbound chanId aborted"
171+ Right bs -> do
172+ let chanId = either err fromIntegral (decode bs :: Either String Int32)
144173 (chanId', chanMap) <- takeMVar chans
145174 case IntMap.lookup chanId chanMap of
146175 Nothing -> do
147176 putMVar chans (chanId', chanMap)
148− error "procConnections: cannot find chanId"
177+ err "cannot find chanId"
149178 Just (chan, socks) -> do
150179 threadId <- forkWithExceptions forkIO "Message Listener" $
151180 procMessages chans chanId chan clientSock
@@ -154,80 +183,94 @@ procConnections chans sock = forever $ do
154183
155184-- | This function first extracts a header of type Word8, which determines
156185-- the size of the ByteString that follows. If this size is 0, this indicates
157−-- that the ByteString is large, so the next value is an Int64, which
186+-- that the ByteString is large, so the next value is an Int32, which
158187-- determines the size of the ByteString that follows. The ByteString is then
159188-- extracted from the socket, and then written to the Chan only when
160189-- complete. If either of the first header size is null this indicates the
161190-- socket has closed.
162191procMessages :: Chans -> ChanId -> Chan [ByteString] -> Socket -> IO ()
163192procMessages chans chanId chan sock = do
164− sizeBSs <- recvExact sock 1
165− case sizeBSs of
166− [] -> closeSocket
167− [onebyte] -> do
168− let size = fromIntegral (B.decode onebyte :: Word8)
169− if size == 255
170− then do
171− sizeBSs' <- recvExact sock 8
172− case sizeBSs' of
173− [] -> closeSocket
174− _ -> procMessage (B.decode . BS.concat$ sizeBSs' :: Int64)
175− else procMessage size
176− ls -> error "Shouldn't receive more than one bytestring when expecting a single byte!"
193+ esizeBS <- recvExact sock 1
194+ case esizeBS of
195+ Left _ -> closeSocket
196+ Right sizeBS -> do
197+ case decode sizeBS :: Either String Word8 of
198+ Right 255 -> do
199+ esizeBS' <- recvExact sock (fromIntegral $ sizeOf (0 :: Int32))
200+ case esizeBS' of
201+ Left _ -> closeSocket
202+ Right sizeBS' -> procMessage $ decode sizeBS'
203+ esize@(Right _) -> procMessage (fromIntegral <$> esize)
204+ Left msg -> error msg
177205 where
206+ err m = error $ printf "procMessages: %s" m
178207 closeSocket :: IO ()
179208 closeSocket = do
180209 (chanId', chanMap) <- takeMVar chans
181210 case IntMap.lookup chanId chanMap of
182211 Nothing -> do
183212 putMVar chans (chanId', chanMap)
184− error "procMessages: chanId not found."
213+ err "chanId not found."
185214 Just (chan, socks) -> do
186215 let socks' = filter ((/= sock) . snd) socks
187216 let chanMap' = IntMap.insert chanId (chan, socks') chanMap
188217 putMVar chans (chanId', chanMap')
189218 sClose sock
190− procMessage :: Int64 -> IO ()
191− procMessage size = do
192− bss <- recvExact sock size
193− case bss of
194− [] -> closeSocket
195− _ -> do
196− writeChan chan bss
219+ procMessage :: Either String Int32 -> IO ()
220+ procMessage (Left msg) = err msg
221+ procMessage (Right size) = do
222+ ebs <- recvExact sock size
223+ case ebs of
224+ Left _ -> closeSocket
225+ Right bs -> do
226+ writeChan chan [bs]
197227 procMessages chans chanId chan sock
198228
199−-- | The result of `recvExact sock n` is a `[ByteString]` whose concatenation
200−-- is of length `n`, received from `sock`. No more bytes than necessary are
201−-- read from the socket.
202−-- NB: This uses Network.Socket.ByteString.recv, which may *discard*
203−-- superfluous input depending on the socket type. Also note that
204−-- if `recv` returns an empty `ByteString` then this means that the socket
205−-- was closed: in this case, we return an empty list.
206−-- NB: It may be appropriate to change the return type to
207−-- IO (Either ByteString ByteString), where `return Left bs` indicates
208−-- a partial retrieval since the socket was closed, and `return Right bs`
209−-- indicates success. This hasn't been implemented, since a Transport `receive`
210−-- represents an atomic receipt of a message.
211−recvExact :: Socket -> Int64 -> IO [ByteString]
229+-- | The normal result of `recvExact sock n` is `Right ByteString`
230+-- whose string of length `n`, received from `sock`. If fewer than `n`
231+-- bytes are read from `sock` before it closes, the result is `Left
232+-- ByteString` whose string is those bytes that were received. No more
233+-- bytes than necessary are read from the socket. NB: This uses
234+-- Network.Socket.ByteString.recv, which may *discard* superfluous
235+-- input depending on the socket type.
236+#ifndef LAZY
237+recvExact :: Socket -> Int32 -> IO (Either ByteString ByteString)
238+recvExact sock l = interceptAllExn "recvExact" $ do
239+ res <- BSI.createAndTrim (fromIntegral l) (go 0)
240+ case BS.length res of
241+ n | n == (fromIntegral l) -> return $ Right res
242+ n -> return $ Left res
243+ where
244+ go :: Int -> Ptr Word8 -> IO Int
245+ go n ptr | n == (fromIntegral l) = return n
246+ go n ptr = do
247+ (p, off, len) <- BSI.toForeignPtr <$> NBS.recv sock (min (fromIntegral l-n) 4096)
248+ if len == 0
249+ then return n
250+ else withForeignPtr p $ \p -> do
251+ BSI.memcpy (ptr `plusPtr` n) (p `plusPtr` off) (fromIntegral len)
252+ go (n+len) ptr
253+#else
254+recvExact :: Socket -> Int32 -> IO (Either ByteString ByteString)
212255recvExact sock n =
213− interceptAllExn "recvExact" $
256+ interceptAllExn "recvExact" $
214257 go [] sock n
215258 where
216− go :: [ByteString] -> Socket -> Int64 -> IO [ByteString]
217− go bss _ 0 = return (reverse bss)
259+ go :: [ByteString] -> Socket -> Int32 -> IO (Either ByteString ByteString)
260+ go bss _ 0 = return (Right $ BS.concat (reverse bss))
218261 go bss sock n = do
219− bs <- NBS.recv sock n
262+ bs <- NBS.recv sock (min (fromIntegral n) 4096)
220263 if BS.null bs
221− then return []
222− else go (bs:bss) sock (n - BS.length bs)
223−
264+ then return (Left $ BS.concat (reverse bss))
265+ else go (bs:bss) sock (n - (fromIntegral $ BS.length bs))
266+#endif
224267
225268interceptAllExn msg =
226269 Control.Exception.handle $ \ e ->
227270 case fromException e of
228271 Just ThreadKilled -> throw e
229272 Nothing -> do
230− BSS.hPutStrLn stderr $ BSS.pack$ "Exception inside "++msg++": "++show e
273+ BS.hPutStrLn stderr $ BS.pack$ "Exception inside "++msg++": "++show e
231274 throw e
232275-- throw (e :: SomeException)
233276
@@ -242,7 +285,7 @@ forkWithExceptions forkit descr action = do
242285-- BSS.hPutStrLn stderr $ BSS.pack$ "Note: Child thread killed: "++descr
243286 return ()
244287 _ -> do
245− BSS.hPutStrLn stderr $ BSS.pack$ "Exception inside child thread "++descr++": "++show e
288+ BS.hPutStrLn stderr $ BS.pack$ "Exception inside child thread "++descr++": "++show e
246289 throwTo parent (e::SomeException)
247290 )
248291