{-# LINE 1 "src/Network/Socket/Splice/Internal.hsc" #-} -- | Implementation. {-# LINE 2 "src/Network/Socket/Splice/Internal.hsc" #-} {-# LINE 4 "src/Network/Socket/Splice/Internal.hsc" #-} {-# LINE 5 "src/Network/Socket/Splice/Internal.hsc" #-} {-# LINE 6 "src/Network/Socket/Splice/Internal.hsc" #-} {-# 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 {-# LINE 41 "src/Network/Socket/Splice/Internal.hsc" #-} 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 {-# LINE 54 "src/Network/Socket/Splice/Internal.hsc" #-} -------------------------------------------------------------------------------- -- | 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 = {-# LINE 65 "src/Network/Socket/Splice/Internal.hsc" #-} True {-# LINE 69 "src/Network/Socket/Splice/Internal.hsc" #-} -- | The numeric type to recommend chunk sizes for moving data between sockets -- used by both zero-copy and portable implementations of 'splice'. type ChunkSize = {-# LINE 75 "src/Network/Socket/Splice/Internal.hsc" #-} L.ChunkSize {-# LINE 79 "src/Network/Socket/Splice/Internal.hsc" #-} -- | 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 {-# LINE 101 "src/Network/Socket/Splice/Internal.hsc" #-} (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 :: (Int32) -> (Word32) {-# LINE 107 "src/Network/Socket/Splice/Internal.hsc" #-} 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) {-# LINE 142 "src/Network/Socket/Splice/Internal.hsc" #-} -- | 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 ()