network-unexceptional 0.2.0.0 → 0.2.1.0
raw patch · 4 files changed
+84/−7 lines, 4 filesdep ~primitivePVP ok
version bump matches the API change (PVP)
Dependency ranges changed: primitive
API changes (from Hackage documentation)
+ Network.Unexceptional.ByteArray: receiveFromInterruptible :: TVar Bool -> Socket -> Int -> IO (Either Errno (ByteArray, SockAddr))
+ Network.Unexceptional.MutableBytes: receiveFromInterruptible :: TVar Bool -> Socket -> MutableBytes RealWorld -> IO (Either Errno (Int, SockAddr))
Files
- CHANGELOG.md +4/−0
- network-unexceptional.cabal +2/−2
- src/Network/Unexceptional/ByteArray.hs +16/−0
- src/Network/Unexceptional/MutableBytes.hs +62/−5
CHANGELOG.md view
@@ -1,5 +1,9 @@ # Revision history for network-unexceptional +## 0.2.1.0 -- 2024-01-17++* Add `receiveFromInterruptible"+ ## 0.2.0.0 -- 2023-09-18 * Receive functions now fail with `EEOI` when the peer shuts down.
network-unexceptional.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: network-unexceptional-version: 0.2.0.0+version: 0.2.1.0 synopsis: Network functions that do not throw exceptions description: Functions compatible with the Socket type from the network library that@@ -31,7 +31,7 @@ , error-codes >=0.1.3 , byteslice >=0.2.8 , network >=3.1- , primitive >=0.8+ , primitive >=0.9 , primitive-addr >=0.1.0.2 , bytestring >=0.11.4 , stm >=2.5.1
src/Network/Unexceptional/ByteArray.hs view
@@ -6,6 +6,7 @@ module Network.Unexceptional.ByteArray ( receiveExactly+ , receiveFromInterruptible ) where import Control.Exception (throwIO)@@ -17,6 +18,7 @@ import GHC.Conc (threadWaitWrite) import Network.Socket (Socket) import System.Posix.Types (Fd(Fd))+import Control.Concurrent.STM (TVar) import qualified Network.Unexceptional.Types as Types import qualified Posix.Socket as X@@ -38,3 +40,17 @@ Right _ -> do dst' <- PM.unsafeFreezeByteArray dst pure (Right dst')++receiveFromInterruptible ::+ TVar Bool+ -> Socket+ -> Int -- ^ Maximum number of bytes to receive.+ -> IO (Either Errno (ByteArray, S.SockAddr))+receiveFromInterruptible !intr s !n = do+ dst <- PM.newByteArray n+ MB.receiveFromInterruptible intr s (MutableBytes dst 0 n) >>= \case+ Left err -> pure (Left err)+ Right (sz,sockAddr) -> do+ PM.shrinkMutableByteArray dst sz+ dst' <- PM.unsafeFreezeByteArray dst+ pure (Right (dst', sockAddr))
src/Network/Unexceptional/MutableBytes.hs view
@@ -1,14 +1,21 @@ {-# language BangPatterns #-} {-# language DuplicateRecordFields #-}-{-# language PatternSynonyms #-} {-# language LambdaCase #-} {-# language NamedFieldPuns #-}+{-# language PatternSynonyms #-}+{-# language ScopedTypeVariables #-} +-- | Note: The functions that are designated as being intended for stream+-- sockets convert a reception length of zero to an non-standard @EOI@ error+-- code. Datagram reception functions do not do this. module Network.Unexceptional.MutableBytes- ( receive+ ( -- * Stream Sockets+ receive , receiveInterruptible , receiveExactly , receiveExactlyInterruptible+ -- * Datagram Sockets+ , receiveFromInterruptible ) where import Control.Applicative ((<|>))@@ -18,14 +25,22 @@ import Data.Bytes.Types (MutableBytes(MutableBytes)) import Data.Functor (($>)) import Data.Primitive (MutableByteArray)+import Foreign.C.Types (CSize,CInt) import Foreign.C.Error (Errno)-import Foreign.C.Error.Pattern (pattern EWOULDBLOCK,pattern EAGAIN) import Foreign.C.Error.Pattern (pattern EEOI)+import Foreign.C.Error.Pattern (pattern EWOULDBLOCK,pattern EAGAIN)+import Foreign.Storable (poke)+import Foreign.Ptr (castPtr)+import Foreign.Marshal.Alloc (allocaBytes,alloca) import GHC.Conc (threadWaitRead,threadWaitReadSTM)-import GHC.Exts (RealWorld)-import Network.Socket (Socket)+import GHC.Exts (RealWorld,Ptr)+import Network.Socket (Socket,SockAddr)+import Network.Socket.Address (peekSocketAddress) import System.Posix.Types (Fd(Fd))+import Data.Word (Word8) +import qualified Data.Primitive as PM+import qualified Data.Primitive.Ptr as PM import qualified Control.Concurrent.STM as STM import qualified Data.Bytes.Types import qualified Linux.Socket as X@@ -47,6 +62,48 @@ -- ready for reads. receiveLoop (Fd fd) array offset len else throwIO Types.NonpositiveReceptionSize++-- | Receive bytes from a socket. Receives at most N bytes, where N+-- is the size of the buffer. Returns the number of bytes that were+-- actually received.+receiveFromInterruptible ::+ TVar Bool+ -> Socket+ -> MutableBytes RealWorld -- ^ Slice of a buffer+ -> IO (Either Errno (Int, SockAddr))+receiveFromInterruptible !interrupt s MutableBytes{array,offset,length=len} =+ if len > 0+ then S.withFdSocket s $ \fd -> do+ -- We attempt the first receive without testing if the socket is+ -- ready for reads.+ receiveFromInterruptibleLoop interrupt (Fd fd) array offset len+ else throwIO Types.NonpositiveReceptionSize++receiveFromInterruptibleLoop ::+ TVar Bool+ -> Fd+ -> MutableByteArray RealWorld+ -> Int+ -> Int+ -> IO (Either Errno (Int, SockAddr))+receiveFromInterruptibleLoop !intr !fd !dst !doff !dlen = + X.uninterruptibleReceiveFromMutableByteArray fd dst doff (fromIntegral dlen :: CSize) mempty 128 >>= \case+ Left e -> if e == EAGAIN || e == EWOULDBLOCK+ then waitUntilReadable intr fd >>= \case+ Ready -> receiveFromInterruptibleLoop intr fd dst doff dlen+ Interrupted -> pure (Left EAGAIN)+ else pure (Left e)+ Right (sockAddrSz,X.SocketAddress sockAddr,recvSzC) -> do+ let sockAddrSzI = fromIntegral sockAddrSz :: Int+ pinned <- PM.newPinnedByteArray sockAddrSzI+ PM.copyByteArray pinned 0 sockAddr 0 sockAddrSzI+ pinned' <- PM.unsafeFreezeByteArray pinned+ sockAddrNetwork <- PM.withByteArrayContents pinned' $ \ptr -> do+ peekSocketAddress (castPtr ptr :: Ptr sa)+ let recvSz = fromIntegral recvSzC :: Int+ in case compare recvSz dlen of+ GT -> throwIO Types.ReceivedTooManyBytes+ _ -> pure (Right (recvSz, sockAddrNetwork)) receiveInterruptible :: TVar Bool -- ^ Interrupt