@@ -4,7 +4,8 @@
44module Network.Wai.Handler.Warp.Run where
55
66import Control.Concurrent (threadDelay, forkIOWithUnmask)
7−import Control.Exception
7+import qualified Control.Concurrent as Conc (yield)
8+import Control.Exception as E
89import Control.Monad (forever, when, unless, void)
910import Control.Monad.IO.Class (MonadIO, liftIO)
1011import Data.ByteString (ByteString)
@@ -23,11 +24,7 @@ import Network.Wai.Handler.Warp.Response
2324import Network.Wai.Handler.Warp.Settings
2425import qualified Network.Wai.Handler.Warp.Timeout as T
2526import Network.Wai.Handler.Warp.Types
26−import Prelude hiding (catch)
27−
28−-- Sock.recv first tries to call recvfrom() optimistically.
29−-- If EAGAIN returns, it polls incoming data with epoll/kqueue.
30−-- This code first polls incoming data with epoll/kqueue.
27+import Network.Wai.Handler.Warp.Recv
3128
3229#if WINDOWS
3330import qualified Control.Concurrent.MVar as MV
@@ -48,14 +45,16 @@ bytesPerRead :: Int
4845bytesPerRead = 4096
4946
5047-- | Default action value for 'Connection'
51−socketConnection :: Socket -> Connection
52−socketConnection s = Connection
53− { connSendMany = Sock.sendMany s
54− , connSendAll = Sock.sendAll s
55− , connSendFile = sendFile s
56− , connClose = sClose s
57− , connRecv = Sock.recv s bytesPerRead
58− }
48+socketConnection :: Socket -> IO Connection
49+socketConnection s = do
50+ buf <- allocateRecvBuffer bytesPerRead
51+ return Connection {
52+ connSendMany = Sock.sendMany s
53+ , connSendAll = Sock.sendAll s
54+ , connSendFile = sendFile s
55+ , connClose = sClose s >> freeRecvBuffer buf
56+ , connRecv = receive s buf bytesPerRead
57+ }
5958
6059sendFile :: Socket -> FilePath -> Integer -> Integer -> IO () -> [ByteString] -> Cleaner -> IO ()
6160#if SENDFILEFD
@@ -110,12 +109,13 @@ runSettings set app =
110109-- 'serverPort' record.
111110runSettingsSocket :: Settings -> Socket -> Application -> IO ()
112111runSettingsSocket set socket app =
113− runSettingsConnection set getter app
112+ runSettingsConnection set getConn app
114113 where
115− getter = do
116− (conn, sa) <- accept socket
114+ getConn = do
115+ (s, sa) <- accept socket
117116 setSocketCloseOnExec socket
118− return (socketConnection conn, sa)
117+ conn <- socketConnection s
118+ return (conn, sa)
119119
120120runSettingsConnection :: Settings -> IO (Connection, SockAddr) -> Application -> IO ()
121121runSettingsConnection set getConn app = runSettingsConnectionMaker set getConnMaker app
@@ -131,7 +131,7 @@ runSettingsConnection set getConn app = runSettingsConnectionMaker set getConnMa
131131--
132132-- Since 1.3.5
133133runSettingsConnectionMaker :: Settings -> IO (IO Connection, SockAddr) -> Application -> IO ()
134−runSettingsConnectionMaker set getConn app = do
134+runSettingsConnectionMaker set getConnMaker app = do
135135#if SENDFILEFD
136136 let duration = settingsFdCacheDuration set
137137 fc <- case duration of
@@ -199,7 +199,7 @@ runSettingsConnectionMaker set getConn app = do
199199 in unmask .
200200 -- Call the user-supplied on exception code if any
201201 -- exceptions are thrown.
202− handle onE .
202+ handle (onE Nothing) .
203203
204204 -- Call the user-supplied code for connection open and close events
205205 bracket_ onOpen onClose $
@@ -208,8 +208,8 @@ runSettingsConnectionMaker set getConn app = do
208208 serveConnection th set cleaner app conn addr
209209 where
210210 -- FIXME: only IOEception is caught. What about other exceptions?
211− getConnLoop = getConn `catch` \(e :: IOException) -> do
212− onE (toException e)
211+ getConnLoop = getConnMaker `E.catch` \(e :: IOException) -> do
212+ onE Nothing (toException e)
213213 -- "resource exhausted (Too many open files)" may happen by accept().
214214 -- Wait a second hoping that resource will be available.
215215 threadDelay 1000000
@@ -250,6 +250,15 @@ serveConnection timeoutHandle settings cleaner app conn remoteHost' =
250250 liftIO $ T.resume th
251251 sendResponse settings cleaner env conn restore res
252252
253+ -- | We just send a Response and it takes a time to
254+ -- receive a Request again. If we immediately call recv,
255+ -- it is likely to fail and the IO manager works.
256+ -- It is very costy. So, we yield to another Haskell
257+ -- thread hoping that the next Request will arraive
258+ -- when this Haskell thread will be re-scheduled.
259+ -- This improves performance at least when
260+ -- the number of cores is small.
261+ Conc.yield
253262 -- flush the rest of the request body
254263 requestBody env $$ CL.sinkNull
255264 ResumableSource fromClient' _ <- liftIO getSource