{-# language BangPatterns #-} {-# language LambdaCase #-} {-# language MultiWayIf #-} module Stream.Send.Two.Indefinite ( sendBoth ) where import Control.Concurrent.STM (TVar) import Foreign.C.Error (Errno(..), eAGAIN, eWOULDBLOCK, ePIPE, eCONNRESET) import Foreign.C.Types (CSize) import Socket.Error (die) import Socket.EventManager (Token) import Socket.Stream (SendException(..),Connection(..)) import Socket.Interrupt (Interrupt,Intr,wait,tokenToStreamSendException) import System.Posix.Types (Fd) import qualified Foreign.C.Error.Describe as D import qualified Socket.EventManager as EM import qualified Stream.Send.Two as SendBoth import qualified Stream.Send.Buffer.A as A import qualified Stream.Send.Buffer.B as B import qualified Stream.Send.Indefinite as SendIndef sendBoth :: Interrupt -> Connection -> A.Buffer -> B.Buffer -> IO (Either (SendException Intr) ()) sendBoth !intr (Connection !conn) !bufA !bufB = do let !mngr = EM.manager tv <- EM.writer mngr conn token0 <- wait intr tv case tokenToStreamSendException token0 0 of Left err -> pure (Left err) Right _ -> sendLoop intr conn tv token0 bufA bufB (A.length bufA) 0 sendLoop :: Interrupt -> Fd -> TVar Token -> Token -> A.Buffer -> B.Buffer -> Int -> Int -> IO (Either (SendException Intr) ()) sendLoop !intr !conn !tv !old !bufA !bufB !origLenA !sent = if lenA > 0 then SendBoth.sendOnce conn bufA bufB >>= \case Left e -> if | e == eAGAIN || e == eWOULDBLOCK -> do EM.unready old tv new <- wait intr tv case tokenToStreamSendException new sent of Left err -> pure (Left err) Right _ -> sendLoop intr conn tv new bufA bufB origLenA sent | e == ePIPE -> pure (Left SendShutdown) | e == eCONNRESET -> pure (Left SendReset) | otherwise -> die ("Socket.Stream.send: " ++ describeErrorCode e) Right sz' -> do let sz = csizeToInt sz' sendLoop intr conn tv old (A.advance bufA sz) bufB origLenA (sent + sz) else do let !sentB = sent - origLenA SendIndef.sendLoop intr conn tv old (B.advance bufB sentB) sentB origLenA where !lenA = A.length bufA csizeToInt :: CSize -> Int csizeToInt = fromIntegral describeErrorCode :: Errno -> String describeErrorCode err@(Errno e) = "error code " ++ D.string err ++ " (" ++ show e ++ ")"