packages feed

splice 0.2 → 0.3

raw patch · 5 files changed

+276/−274 lines, 5 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Network.Socket.Splice: c_splice :: Fd -> Ptr (Int64) -> Fd -> Ptr (Int64) -> (Word32) -> Word -> IO (Int32)
- Network.Socket.Splice: sPLICE_F_MORE :: Word
- Network.Socket.Splice: sPLICE_F_MOVE :: Word
- Network.Socket.Splice: sPLICE_F_NONBLOCK :: Word
- Network.Socket.Splice: type ChunkSize = Word32
+ Network.Socket.Splice: type ChunkSize = Int

Files

splice.cabal view
@@ -1,27 +1,25 @@ name:          splice-version:       0.2+version:       0.3 stability:     stable on Linux, experimental on other operating systems-synopsis:      Socket to Socket Data Splicing-description:   A library that implements efficient socket to socket-               data transfer loops for proxy servers.-               .-               On Linux, it uses the zero-copy splice() system call:-               <http://kerneltrap.org/node/6505>.+synopsis:      Socket to Socket Data Splicing (supports all operating systems)+description:   A library that implements most efficient socket to socket+               data transfer loops for proxy servers on each operating system.                .-               On all other operating systems, it currently falls back-               to a portable Haskell implementation that allocates a-               constant-sized memory buffer before it enters an inner-               loop which then uses hGetBufSome and hPutBuf; this avoids-               lots of tiny allocations as would otherwise be caused by-               recv and sendAll functions from Network.Socket.ByteString.+               On Linux, it uses and exposes the zero-copy @splice()@ system+               call: <http://kerneltrap.org/node/6505>.                .-               This work is funded by Corsis Research and used in:+               On other operating systems, it currently falls back to a portable+               Haskell implementation.                .-               PortFusion ]-[ayabusa – German-               research project for building the simplest-               high-performance distributed reverse / forward proxy.+               [Corsis Research]+               This work is funded by Corsis Research+               (<http://corsis.eu>) for the development of                .-               <https://sourceforge.net/p/portfusion/wiki/RoadMap/>+               [PortFusion \]-\[ayabusa (はやぶさ) (Hayabusa)]+               – German-Japanese joint research project for building the+               simplest and most concise high-performance distributed reverse /+               forward proxy possible+               (<https://sourceforge.net/p/portfusion/wiki/RoadMap/>). license:       BSD3 license-file:  LICENSE author:        Cetin Sert <fusion@corsis.eu>@@ -31,12 +29,12 @@ build-type:    Simple cabal-version: >=1.2 copyright:     Copyright © 2012 Cetin Sert-tested-with:   GHC >= 7.4.3+tested-with:   GHC >= 7.0   flag portable     description: explicitly enable portable splice implemented in Haskell-    default    : False+    default    : True   library@@ -44,15 +42,17 @@     hs-source-dirs:  src      exposed-modules: Network.Socket.Splice+    other-modules:   Network.Socket.Splice.Internal      if os(linux) && !flag(portable)+      exposed-modules: System.IO.Splice.Linux       build-depends: base    >= 4 && <= 6,                      network >= 2 && <= 4,                      unix    >= 2 && <= 4       cpp-options: -DLINUX_SPLICE-      ghc-options: -O2 -O3 -fllvm -optlo-O3+      ghc-options: -W -O2 -O3 -fllvm -optlo-O3     else       build-depends: base    >= 4 && <= 6,                      network >= 2 && <= 4       cpp-options: -ULINUX_SPLICE-      ghc-options: -O2 -O3+      ghc-options: -W -O2 -O3
+ src/Network/Socket/Splice.hs view
@@ -0,0 +1,29 @@+{- |+  This library implements most efficient socket to socket data transfer loops+  for proxy servers on each operating system.+               +  On 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.+-}+--  +-- Module      : Network.Socket.Splice+-- Copyright   : (c) Cetin Sert 2012+-- License     : BSD3+-- Maintainer  : fusion@corsis.eu+-- Stability   : stable+-- Portability : GHC-only, works on all OSes++module Network.Socket.Splice (++  module Network.Socket.Splice.Internal++  ) where++import Network.Socket.Splice.Internal
− src/Network/Socket/Splice.hsc
@@ -1,252 +0,0 @@-{- |-  This library implements efficient socket to socket-  data transfer loops for proxy servers.-               -   On Linux, it uses the zero-copy splice() system call:-   <http://kerneltrap.org/node/6505>.--   On all other operating systems, it currently falls back-   to a portable Haskell implementation that allocates a-   constant-sized memory buffer before it enters an inner-   loop which then uses hGetBufSome and hPutBuf; this avoids-   lots of tiny allocations as would otherwise be caused by-   recv and sendAll functions from Network.Socket.ByteString.--}---  --- Module      : Network.Socket.Splice--- Copyright   : (c) Cetin Sert 2012--- License     : BSD-style--- Maintainer  : fusion@corsis.eu--- Stability   : stable--- Portability : GHC-only, works on all OSes--#ifdef LINUX_SPLICE-#include <fcntl.h>-{-# LANGUAGE CPP #-}-{-# LANGUAGE ForeignFunctionInterface #-}-#endif--module Network.Socket.Splice (--  -- * Cross-platform API for Socket to Socket Data Transfer Loops-  {- | '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':--       > void . forkIO . try_ $ splice 1024 sourceSocket targetSocket-       > void . forkIO . try_ $ splice 1024 targetSocket sourceSocket-  -}--    splice-  , ChunkSize-  , zeroCopy--  -- * Combinators for Exception Handling-  , try_---  -- * Linux splice() Components-  {- | These are available only on Linux and-       will be moved to a different namespace-       in later releases. Their names will stay-       the same.-  -}--#ifdef LINUX_SPLICE-  , c_splice-  , sPLICE_F_MOVE-  , sPLICE_F_MORE-  , sPLICE_F_NONBLOCK-#endif--  ) where--import Data.Word-import Foreign.Ptr--import Network.Socket-import Control.Monad-import Control.Exception-import System.IO-import System.Posix.Types-import System.Posix.Internals-import GHC.IO.Handle.FD--#ifdef LINUX_SPLICE-import Data.Int-import Data.Bits-import Unsafe.Coerce-import Foreign.C.Types-import Foreign.C.Error-import System.Posix.IO-#else-import Foreign.Marshal.Alloc-#endif----- | Indicates whether 'splice' uses zero-copy system calls---   or the portable user mode Haskell substitue implementation.-zeroCopy :: Bool -- ^ True: uses zero-copy system calls; otherwise: portable.-zeroCopy =-#ifdef LINUX_SPLICE-  True-#else-  False-#endif--------------------------------------------------------------------------------------- | The numeric type used to recommend chunk sizes for moving---   data between sockets used by both the Linux 'splice' and---   the portable implementation of 'splice'.-type ChunkSize =-#ifdef LINUX_SPLICE-  (#type size_t)-#else-  Int-#endif----- | Pipes data from one socket to another in an---   **infinite loop**.------   On Linux this happens in kernel space with---   zero copying between kernel and user spaces.------   On other operating systems, a portable---   implementation utilizes a user space buffer---   and works on handles instead of file descriptors.-splice-  :: ChunkSize -- ^ Chunk size.-  -> Socket    -- ^ Source socket.-  -> Socket    -- ^ Target socket.-  -> IO ()     -- ^ Infinite loop.-splice len sIn sOut = do--  let throwRecv0 = error "Network.Socket.Splice.splice ended"--  let fdIn  = fdSocket sIn-  let fdOut = fdSocket sOut --#ifdef LINUX_SPLICE--  print "LINUX-SPLICE"--  (r,w) <- createPipe     -- r: read  end of pipe-  print ('+',r,w)         -- w: write end of pipe-  let s = Fd fdIn         -- s: source socket-  let t = Fd fdOut        -- t: target socket-  let n = nullPtr  -  let u = unsafeCoerce :: (#type ssize_t) -> (#type size_t)-  let check = throwErrnoIfMinus1 "Network.Socket.Splice.splice"-  let flags = sPLICE_F_MOVE .|. sPLICE_F_MORE-  let setNonBlockingMode v = do setNonBlockingFD fdIn  v-                                setNonBlockingFD fdOut v--  setNonBlockingMode False-  finally-    (forever $ do -       bytes <- check $ c_splice s n w n    len    flags-       if bytes > 0-         then           c_splice r n t n (u bytes) flags-         else           throwRecv0)-    (do closeFd r-        closeFd w-        try_ $ setNonBlockingMode True-        print ('-',r,w))--#else--  s <- fdToHandle fdIn-  t <- fdToHandle fdOut-  hSetBuffering s NoBuffering-  hSetBuffering t NoBuffering-  a <- mallocBytes len :: IO (Ptr Word8)--  print "PORTABLE-SPLICE"--  finally-    (forever $ do-       bytes <-   hGetBufSome s a len-       if bytes > 0-         then     hPutBuf     t a bytes-         else     throwRecv0)-    (do free a-        try_ $ hClose s-        try_ $ hClose t)--#endif----- | Similar to 'Control.Exception.Base.try' but used when an---   obvious exception is expected whose type can be safely---   ignored.-try_-  :: IO () -- ^ The action to run which can throw any exception.-  -> IO () -- ^ The new action where exceptions are silenced.-try_ a = (try a :: IO (Either SomeException ())) >> return ()--------------------------------------------------------------------------------------#ifdef LINUX_SPLICE--- SPLICE---- fcntl.h--- ssize_t splice(---   int          fd_in,---   loff_t*      off_in,---   int          fd_out,---   loff_t*      off_out,---   size_t       len,---   unsigned int flags--- );---- | Moves data between two file descriptors without---   copying between kernel address space and user ---   address space. It transfers up to 'len' bytes of---   data from the file descriptor 'fd_in' to the file---   file descriptor 'fd_out', where one of the---   descriptors must refer to a pipe.------   'c_splice' is NOT a loop and needs to called repeatedly.---   For an example, see the source code of 'splice'.-foreign import ccall "splice"-  c_splice-  :: Fd                  -- ^ fd_in-  -> Ptr (#type loff_t)  -- ^ off_in-  -> Fd                  -- ^ fd_out-  -> Ptr (#type loff_t)  -- ^ off_out-  -> (#type size_t)      -- ^ len-  -> Word                -- ^ flags-  -> IO (#type ssize_t)  -- ^ number of bytes moved or -1 on error----- | Attempt to move pages instead of copying. This is---   only a hint to the kernel: pages may stil be copied---   if the kernel cannot move the pages from the pipe,---   or if the pipe buffers don't refer to full pages.-sPLICE_F_MOVE :: Word-sPLICE_F_MOVE = (#const "SPLICE_F_MOVE")----- | More data will be coming in a subsequent splice.---   This is a helpful hint when 'fd_out' refers to a---   socket.-sPLICE_F_MORE :: Word-sPLICE_F_MORE = (#const "SPLICE_F_MORE")----- | Do not block on I/O. This makes the splice pipe---   operations nonblocking, but splice() may nevertheless---   block because the file descriptors that are spliced---   to/from may block (unless they have the O_NONBLOCK flag---   set).-sPLICE_F_NONBLOCK :: Word-sPLICE_F_NONBLOCK = (#const "SPLICE_F_NONBLOCK")--#endif
+ src/Network/Socket/Splice/Internal.hsc view
@@ -0,0 +1,149 @@+-- | Implementation.++#ifdef LINUX_SPLICE+#include <fcntl.h>+#endif+{-# LANGUAGE CPP #-}+{-# LANGUAGE ForeignFunctionInterface #-}+++module Network.Socket.Splice.Internal (++  -- * Cross-platform API for Socket to Socket Data Transfer Loops+  {- | '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':++       > void . forkIO . try_ $ splice 1024 sourceSocket targetSocket+       > void . forkIO . try_ $ splice 1024 targetSocket sourceSocket+  -}++    splice+  , ChunkSize+  , zeroCopy++  -- * Combinators for Exception Handling+  , try_++  ) where+++import Data.Word+import Foreign.Ptr++import Network.Socket+import Control.Monad+import Control.Exception++#ifdef LINUX_SPLICE+import Data.Int+import Data.Bits+import System.Posix.IO+import Unsafe.Coerce+import Foreign.C.Error+import System.Posix.Types+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+#endif+++--------------------------------------------------------------------------------+++-- | Indicates whether 'splice' uses zero-copy system calls or the portable user +--   space Haskell implementation.+zeroCopy :: Bool -- ^ @True@ if 'splice' uses zero-copy system calls;+                 --   otherwise, false.+zeroCopy =+#ifdef LINUX_SPLICE+  True+#else+  False+#endif+++-- | The numeric type to recommend chunk sizes for moving data between sockets+--   used by both zero-copy and portable implementations of 'splice'.+type ChunkSize =+#ifdef LINUX_SPLICE+  L.ChunkSize+#else+  Int+#endif+++-- | 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.+--+--   On other operating systems, a portable Haskell implementation utilizes a+--   user space buffer.+splice+  :: ChunkSize -- ^ chunk size.+  -> Socket    -- ^ source socket.+  -> Socket    -- ^ target socket.+  -> IO ()     -- ^ infinite loop.+splice len sIn sOut = do++  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+  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+       if bytes > 0+         then           L.c_splice r n t n (u bytes) flags+         else           throwRecv0)+    (do closeFd r+        closeFd w+        try_ $ setNonBlockingMode True)++#else++  s <- fdToHandle fdIn+  t <- fdToHandle fdOut+  hSetBuffering s NoBuffering+  hSetBuffering t NoBuffering+  a <- mallocBytes len :: IO (Ptr Word8)++  finally+    (forever $ do+       bytes <-   hGetBufSome s a len+       if bytes > 0+         then     hPutBuf     t a bytes+         else     throwRecv0)+    (do free a+        try_ $ hClose s+        try_ $ hClose t)++#endif+++-- | Similar to 'Control.Exception.Base.try' but used when an obvious exception+--   is expected which can be safely ignored.+try_+  :: IO () -- ^ action to run which can throw /any/ exception.+  -> IO () -- ^ new action where exceptions are silenced.+try_ a = (try a :: IO (Either SomeException ())) >> return ()
+ src/System/IO/Splice/Linux.hsc view
@@ -0,0 +1,76 @@+{- | Exposes the GNU\/Linux system call @splice()@.++     /This module is only available (compiled & exposed) on Linux./+-}+--  +-- Module      : Network.Socket.Splice+-- Copyright   : (c) Cetin Sert 2012+-- License     : BSD3+-- Maintainer  : fusion@corsis.eu+-- Stability   : stable+-- Portability : GNU\/Linux-only++#include <fcntl.h>+{-# LANGUAGE CPP #-}+{-# LANGUAGE ForeignFunctionInterface #-}++module System.IO.Splice.Linux (++    c_splice+  , ChunkSize+  , sPLICE_F_MOVE+  , sPLICE_F_MORE+  , sPLICE_F_NONBLOCK++  ) where+++import Data.Int+import Data.Word+import Foreign.Ptr+import Foreign.C.Types+import System.Posix.Types+++-- | The numeric type used by 'c_splice' for chunk size recommendations when+--   moving data.+type ChunkSize = (#type size_t)++-- | Moves data between two file descriptors without copying between kernel+--   address space and user address space. It transfers up to 'len' bytes of+--   data from the file descriptor 'fd_in' to the file descriptor 'fd_out',+--   where one of the descriptors must refer to a pipe.+--+--   'c_splice' is /NOT/ a loop and needs to be called repeatedly.+--+--   For an example, see 'Network.Socket.Splice.Internal.splice'.+foreign import ccall "splice"+  c_splice+  :: Fd                  -- ^ @fd_in@.+  -> Ptr (#type loff_t)  -- ^ @off_in@.+  -> Fd                  -- ^ @fd_out@.+  -> Ptr (#type loff_t)  -- ^ @off_out@.+  -> ChunkSize           -- ^ @len@.+  -> Word                -- ^ @flags@.+  -> IO (#type ssize_t)  -- ^ number of bytes moved if successful; otherwise -1.+++-- | Attempt to move pages instead of copying. This is only a hint to the+--   kernel: pages may stil be copied (/in kernel address space/) if the kernel+--   cannot move the pages from the pipe, or if the pipe buffers don't refer to+--   full pages.+sPLICE_F_MOVE :: Word+sPLICE_F_MOVE = (#const "SPLICE_F_MOVE")+++-- | More data will be coming in a subsequent splice. This is a helpful hint+--   when 'fd_out' refers to a socket.+sPLICE_F_MORE :: Word+sPLICE_F_MORE = (#const "SPLICE_F_MORE")+++-- | Do not block on I\/O. This makes the splice pipe operations nonblocking,+--   but splice() may nevertheless block because the file descriptors that are+--   spliced to\/from may block (unless they have the O_NONBLOCK flag set).+sPLICE_F_NONBLOCK :: Word+sPLICE_F_NONBLOCK = (#const "SPLICE_F_NONBLOCK")