packages feed

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 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