splice 0.3.2 → 0.4
raw patch · 3 files changed
+101/−40 lines, 3 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Network.Socket.Splice: fdSplice :: ChunkSize -> Fd -> Fd -> IO ()
+ Network.Socket.Splice: hSplice :: Int -> Handle -> Handle -> IO ()
- Network.Socket.Splice: splice :: ChunkSize -> Socket -> Socket -> IO ()
+ Network.Socket.Splice: splice :: ChunkSize -> (Socket, Maybe Handle) -> (Socket, Maybe Handle) -> IO ()
Files
- splice.cabal +4/−4
- src/Network/Socket/Splice.hs +6/−6
- src/Network/Socket/Splice/Internal.hsc +91/−30
splice.cabal view
@@ -1,5 +1,5 @@ name: splice-version: 0.3.2+version: 0.4 stability: stable on GNU/Linux, experimental on other operating systems synopsis: Socket to Socket Data Splicing (supports all operating systems) description: A library that implements most efficient socket to socket@@ -33,7 +33,7 @@ flag portable- description: explicitly enable portable splice implemented in Haskell+ description: force portable 'splice' implementation on GNU\/Linux default : False @@ -50,9 +50,9 @@ network >= 2 && <= 4, unix >= 2 && <= 4 cpp-options: -DLINUX_SPLICE- ghc-options: -W -O2 -O3 -fllvm -optlo-O3+ ghc-options: -Wall -O2 -O3 -fllvm -optlo-O3 else build-depends: base >= 4 && <= 6, network >= 2 && <= 4 cpp-options: -ULINUX_SPLICE- ghc-options: -W -O2 -O3+ ghc-options: -Wall -O2 -O3
src/Network/Socket/Splice.hs view
@@ -5,12 +5,12 @@ On GNU\/Linux, it uses and exposes the zero-copy @splice()@ system call: <http://kerneltrap.org/node/6505>. - On other operating systems, it currently falls back to a portable Haskell- implementation – which first allocates a constant-sized memory buffer in user- address, then enters an inner loop which uses 'System.IO.hGetBufSome'- and 'System.IO.hPutBuf' on this user-space buffer. This avoids tony of tiny- allocations as might otherwise be caused by 'Network.Socket.ByteString.recv'- and 'Network.Socket.ByteString.sendAll' from the @bytestring@ package.+ On all other operating systems, it currently falls back to a portable Haskell+ implementation – which allocates a single memory buffer in user address space,+ then enters an inner loop that uses 'System.IO.hGetBufSome' and+ 'System.IO.hPutBuf'. This avoids lots of tiny allocations as would otherwise+ be caused by 'Network.Socket.ByteString.recv' and+ 'Network.Socket.ByteString.sendAll' from the @bytestring@ package. -} -- -- Module : Network.Socket.Splice
src/Network/Socket/Splice/Internal.hsc view
@@ -1,4 +1,5 @@ -- | Implementation.+-- -- Module : Network.Socket.Splice.Internal -- Copyright : (c) Cetin Sert 2012 -- License : BSD3@@ -20,11 +21,14 @@ {- | 'splice' is the cross-platform API for continous, uni-directional data transfer between two network sockets. - It is an /infinite loop/ that is intended to be used with- 'Control.Concurrent.forkIO':+ 'splice' and its implementation primitives 'hSplice' and 'fdSplice' are+ /infinite/ loops that are intended to be used with exception handlers and+ 'Control.Concurrent.forkIO'. - > void . forkIO . try_ $ splice 1024 sourceSocket targetSocket- > void . forkIO . try_ $ splice 1024 targetSocket sourceSocket+ [Initiate bi-directional continuous data transfer between two sockets:]+ + > void . forkIO . try_ $ splice 1024 (sourceSocket, _) (targetSocket, _)+ > void . forkIO . try_ $ splice 1024 (targetSocket, _) (sourceSocket, _) -} splice@@ -34,15 +38,26 @@ -- * Combinators for Exception Handling , try_ + -- * Implementation Primitives+ {- | Infinite loops used in the cross-platform implementation of 'splice'.+ -}++ , hSplice+#ifdef LINUX_SPLICE+ , fdSplice+#endif+ ) where import Data.Word import Foreign.Ptr +import System.IO import Network.Socket import Control.Monad import Control.Exception+import Foreign.Marshal.Alloc #ifdef LINUX_SPLICE import Data.Int@@ -54,9 +69,7 @@ import System.Posix.Internals import qualified System.IO.Splice.Linux as L #else-import System.IO-import GHC.IO.Handle.FD-import Foreign.Marshal.Alloc+import Data.Maybe #endif @@ -85,38 +98,74 @@ #endif +throwRecv0 :: a+throwRecv0 = error "Network.Socket.Splice.splice ended"+ -- | Pipes data from one socket to another in an /infinite loop/. ----- On Linux this uses the @splice()@ system call and eliminates copying--- between kernel and user address spaces.+-- 'splice' currently has two implementations: ----- On other operating systems, a portable Haskell implementation utilizes a--- user space buffer.+-- [on GNU\/Linux using 'fdSplice' ≅]+--+-- > splice len (sIn, _ ) (sOut, _ ) = ... fdSplice ...+--+-- [on all other operating systems using 'hSplice' ≅]+--+-- > splice len (_ , Just hIn) (_ , Just hOut) = ... hSplice ...+--+-- [Notes]+--+-- * 'fdSplice' and 'fdSplice' implementation of 'splice' are only available+-- on GNU\/Linux.+--+-- * 'hSplice' is always available and the 'hSplice' implementation of+-- 'splice' can be forced on GNU\/Linux by defining the @portable@ flag at+-- compile time.+-- splice- :: ChunkSize -- ^ chunk size.- -> Socket -- ^ source socket.- -> Socket -- ^ target socket.- -> IO () -- ^ infinite loop.-splice len sIn sOut = do+ :: ChunkSize -- ^ chunk size.+ -> (Socket, Maybe Handle) -- ^ source socket and possibly its opened handle.+ -> (Socket, Maybe Handle) -- ^ target socket and possibly its opened handle.+ -> IO () -- ^ infinite loop.+#ifdef LINUX_SPLICE+splice len (sIn, _ ) (sOut, _ ) = do+#else+splice len (_ , hIn) (_ , hOut) = do+#endif+#ifdef LINUX_SPLICE+ let s = Fd $ fdSocket sIn+ let t = Fd $ fdSocket sOut+ fdSplice len s t+#else+ let s = fromJust hIn+ let t = fromJust hOut+ hSplice (fromIntegral len) s t+#endif - let throwRecv0 = error "Network.Socket.Splice.splice ended" - let fdIn = fdSocket sIn- let fdOut = fdSocket sOut #ifdef LINUX_SPLICE - (r,w) <- createPipe -- r,w: read / write ends of pipe- let s = Fd fdIn -- s : source socket- let t = Fd fdOut -- t : target socket+{- | GNU\/Linux @splice()@ system call loop.++ 1. creates a pipe in kernel address space++ 2. uses it until the loop terminates by exception++ 3. closes the pipe and returns+-}+fdSplice :: ChunkSize -> Fd -> Fd -> IO ()+fdSplice len s@(Fd fdIn) t@(Fd fdOut) = do++ (r,w) <- createPipe let n = nullPtr let u = unsafeCoerce :: (#type ssize_t) -> (#type size_t) let check = throwErrnoIfMinus1 "Network.Socket.Splice.splice" let flags = L.sPLICE_F_MOVE .|. L.sPLICE_F_MORE let setNonBlockingMode v = do setNonBlockingFD fdIn v setNonBlockingFD fdOut v- setNonBlockingMode False+ finally (forever $ do bytes <- check $ L.c_splice s n w n len flags@@ -127,12 +176,25 @@ closeFd w try_ $ setNonBlockingMode True) -#else+#endif - s <- fdToHandle fdIn ; hSetBuffering s NoBuffering- t <- fdToHandle fdOut; hSetBuffering t NoBuffering- a <- mallocBytes len :: IO (Ptr Word8) ++{- | The portable Haskell loop.++ 1. allocates a /single/ memory buffer in user address space++ 2. uses it until the loop terminates by exception++ 3. frees the buffer and returns+-} +hSplice :: Int -> Handle -> Handle -> IO ()+hSplice len s t = do++ sb <- hGetBuffering s; hSetBuffering s NoBuffering+ tb <- hGetBuffering s; hSetBuffering t NoBuffering+ a <- mallocBytes len :: IO (Ptr Word8)+ finally (forever $ do bytes <- hGetBufSome s a len@@ -140,10 +202,9 @@ then hPutBuf t a bytes else throwRecv0) (do free a- try_ $ hClose s- try_ $ hClose t)+ try_ $ hSetBuffering s sb+ try_ $ hSetBuffering s tb) -#endif -- | Similar to 'Control.Exception.Base.try' but used when an obvious exception