on master help sign in
introducing a manager of HTTP/2 workers.
commitfedab88cf1d84456a2cf19ce453b7571fc24c68b authorKazu Yamamoto <kazu@iij.ad.jp> dateFri 17 Jul 2015, 15:12 (11 years ago) parent905456d
5 files changed+152−34
warp/Network/Wai/Handler/Warp/HTTP2.hs+14−9 warp/Network/Wai/Handler/Warp/HTTP2/Manager.hs+74 warp/Network/Wai/Handler/Warp/HTTP2/Types.hs+1−5 warp/Network/Wai/Handler/Warp/HTTP2/Worker.hs+62−20 warp/warp.cabal+1
warp/Network/Wai/Handler/Warp/HTTP2.hs+14−9
@@ -5,12 +5,13 @@ module Network.Wai.Handler.Warp.HTTP2 (isHTTP2, http2) where
55
66import Control.Concurrent (forkIO, killThread)
77import qualified Control.Exception as E
8−import Control.Monad (when, unless, replicateM)
8+import Control.Monad (when, unless, replicateM_)
99import Data.ByteString (ByteString)
1010import Network.HTTP2
1111import Network.Socket (SockAddr)
1212import Network.Wai
1313import Network.Wai.Handler.Warp.HTTP2.EncodeFrame
14+import Network.Wai.Handler.Warp.HTTP2.Manager
1415import Network.Wai.Handler.Warp.HTTP2.Receiver
1516import Network.Wai.Handler.Warp.HTTP2.Request
1617import Network.Wai.Handler.Warp.HTTP2.Sender
@@ -27,19 +28,23 @@ http2 conn ii addr transport settings readN app = do
2728 ok <- checkPreface
2829 when ok $ do
2930 ctx <- newContext
30− let responder = response ctx
31− mkreq = mkRequest settings addr
31+ -- Workers & Manager
32+ mgr <- start
33+ let responder = response ctx mgr
34+ action = worker ctx settings tm app responder
35+ setAction mgr action
36+ -- fixme: hard coding: 10
37+ replicateM_ 10 $ spawn mgr
38+ -- Receiver
39+ let mkreq = mkRequest settings addr
3240 tid <- forkIO $ frameReceiver ctx mkreq readN
33− -- fixme: 10 is hard-coded
34− -- To prevent thread-leak, we executed the fixed number of threads
35− -- statically at this moment. But ResponseStream occupies one
36− -- worker thread. So, this should be dynamic.
37− tids <- replicateM 10 $ forkIO $ worker ctx settings tm app responder
41+ -- Sender
3842 -- frameSender is the main thread because it ensures to send
3943 -- a goway frame.
4044 frameSender ctx conn ii settings `E.finally` do
4145 clearContext ctx
42− mapM_ killThread (tid:tids)
46+ stop mgr
47+ killThread tid
4348 where
4449 tm = timeoutManager ii
4550 checkTLS = case transport of
warp/Network/Wai/Handler/Warp/HTTP2/Manager.hsnew file+74
@@ -0,0 +1,74 @@
1+module Network.Wai.Handler.Warp.HTTP2.Manager (
2+ start
3+ , stop
4+ , spawn
5+ , replace
6+ , setAction
7+ , Manager
8+ ) where
9+
10+import Control.Concurrent
11+import Control.Concurrent.STM
12+import Control.Monad (void)
13+import Data.Set (Set)
14+import qualified Data.Set as Set
15+import Network.Wai.Handler.Warp.IORef
16+
17+----------------------------------------------------------------
18+
19+data Command = Stop | Spawn | Replace ThreadId
20+
21+data Manager = Manager (TQueue Command) (IORef (IO ()))
22+
23+start :: IO Manager
24+start = do
25+ tset <- newThreadSet
26+ q <- newTQueueIO
27+ ref <- newIORef (return ())
28+ void $ forkIO $ go q tset ref
29+ return $ Manager q ref
30+ where
31+ go q tset ref = do
32+ x <- atomically $ readTQueue q
33+ case x of
34+ Stop -> kill tset
35+ Spawn -> next
36+ Replace oldtid -> do
37+ del tset oldtid
38+ next
39+ where
40+ next = do
41+ action <- readIORef ref
42+ newtid <- forkIO $ action
43+ add tset newtid
44+ go q tset ref
45+
46+setAction :: Manager -> IO () -> IO ()
47+setAction (Manager _ ref) action = writeIORef ref action
48+
49+stop :: Manager -> IO ()
50+stop (Manager q _) = atomically $ writeTQueue q Stop
51+
52+spawn :: Manager -> IO ()
53+spawn (Manager q _) = atomically $ writeTQueue q Spawn
54+
55+replace :: Manager -> ThreadId -> IO ()
56+replace (Manager q _) tid = atomically $ writeTQueue q $ Replace tid
57+
58+----------------------------------------------------------------
59+
60+newtype ThreadSet = ThreadSet (IORef (Set ThreadId))
61+
62+newThreadSet :: IO ThreadSet
63+newThreadSet = ThreadSet <$> newIORef Set.empty
64+
65+add :: ThreadSet -> ThreadId -> IO ()
66+add (ThreadSet ref) tid =
67+ atomicModifyIORef' ref (\set -> (Set.insert tid set, ()))
68+
69+del :: ThreadSet -> ThreadId -> IO ()
70+del (ThreadSet ref) tid =
71+ atomicModifyIORef' ref (\set -> (Set.delete tid set, ()))
72+
73+kill :: ThreadSet -> IO ()
74+kill (ThreadSet ref) = Set.toList <$> readIORef ref >>= mapM_ killThread
warp/Network/Wai/Handler/Warp/HTTP2/Types.hs+1−5
@@ -117,7 +117,7 @@ newContext = Context <$> newIORef defaultSettings
117117 <*> newTVarIO defaultInitialWindowSize
118118
119119clearContext :: Context -> IO ()
120−clearContext ctx = stop $ streamTable ctx
120+clearContext ctx = void $ reaperStop $ streamTable ctx
121121
122122----------------------------------------------------------------
123123
@@ -232,7 +232,3 @@ insert strmtbl k v = reaperAdd strmtbl (k,v)
232232
233233search :: StreamTable -> M.Key -> IO (Maybe Stream)
234234search strmtbl k = M.lookup k <$> reaperRead strmtbl
235−
236−stop :: StreamTable -> IO ()
237−stop strmtbl = void $ reaperStop strmtbl
238−
warp/Network/Wai/Handler/Warp/HTTP2/Worker.hs+62−20
@@ -1,8 +1,12 @@
11{-# LANGUAGE DeriveDataTypeable #-}
22{-# LANGUAGE RecordWildCards, NamedFieldPuns #-}
3−{-# LANGUAGE PatternGuards #-}
3+{-# LANGUAGE PatternGuards, BangPatterns #-}
44
5−module Network.Wai.Handler.Warp.HTTP2.Worker where
5+module Network.Wai.Handler.Warp.HTTP2.Worker (
6+ Responder
7+ , response
8+ , worker
9+ ) where
610
711import Control.Concurrent
812import Control.Concurrent.STM
@@ -15,6 +19,7 @@ import Network.HTTP2
1519import Network.HTTP2.Priority
1620import Network.Wai
1721import Network.Wai.Handler.Warp.HTTP2.EncodeFrame
22+import Network.Wai.Handler.Warp.HTTP2.Manager
1823import Network.Wai.Handler.Warp.HTTP2.Types
1924import Network.Wai.Handler.Warp.IORef
2025import qualified Network.Wai.Handler.Warp.Response as R
@@ -24,16 +29,21 @@ import Network.Wai.Internal (Response(..), ResponseReceived(..), ResponseReceive
2429
2530----------------------------------------------------------------
2631
27−type Responder = T.Handle -> Stream -> Priority -> Request -> Response -> IO ResponseReceived
32+type Responder = ThreadContinue -> T.Handle -> Stream -> Priority -> Request -> Response -> IO ResponseReceived
2833
29−response :: Context -> Responder
30−response Context{outputQ} th strm pri req rsp = do
34+response :: Context -> Manager -> Responder
35+response Context{outputQ} mgr tconf th strm pri req rsp = do
3136 case rsp of
3237 ResponseStream _ _ strmbdy -> do
33− -- fixme: spawn a new worker thread.
34− --
3538 -- We must not exit this WAI application.
3639 -- If the application exits, streaming would be also closed.
40+ -- So, this work occupies this thread.
41+ --
42+ -- We need to increase the number of workers.
43+ myThreadId >>= replace mgr
44+ -- After this work, this thread stops to decease
45+ -- the number of workers.
46+ setThreadContinue tconf False
3747 -- Since 'StreamingBody' is loop, we cannot control it.
3848 -- So, let's serialize 'Builder' with a designated queue.
3949 sq <- newTBQueueIO 10 -- fixme: hard coding: 10
@@ -50,6 +60,7 @@ response Context{outputQ} th strm pri req rsp = do
5060 strmbdy push flush
5161 atomically $ writeTBQueue sq SFinish
5262 _ -> do
63+ setThreadContinue tconf True
5364 let hasBody = requestMethod req /= H.methodHead
5465 || R.hasBody (responseStatus rsp)
5566 enqueue outputQ (OResponse strm rsp (Oneshot hasBody)) pri
@@ -62,34 +73,37 @@ instance Exception Break
6273worker :: Context -> S.Settings -> T.Manager -> Application -> Responder -> IO ()
6374worker ctx@Context{inputQ,outputQ} set tm app responder = do
6475 tid <- myThreadId
65− ref <- newIORef Nothing
76+ sinfo <- newStreamInfo
77+ tcont <- newThreadContinue
6678 let setup = T.register tm $ E.throwTo tid Break
67− E.bracket setup T.cancel $ go ref
79+ E.bracket setup T.cancel $ go sinfo tcont
6880 where
69− go ref th = do
81+ go sinfo tcont th = do
82+ setThreadContinue tcont True
7083 ex <- E.try $ do
7184 T.pause th
7285 Input strm req pri <- atomically $ readTQueue inputQ
73− writeIORef ref $ Just (strm,req)
86+ setStreamInfo sinfo strm req
7487 T.resume th
7588 T.tickle th
76− app req $ responder th strm pri req
77− cont <- case ex of
89+ app req $ responder tcont th strm pri req
90+ cont1 <- case ex of
7891 Right ResponseReceived -> return True
7992 Left e@(SomeException _)
8093 | Just Break <- E.fromException e -> do
81− cleanup ref Nothing
94+ cleanup sinfo Nothing
8295 return True
8396 -- killed by the sender
8497 | Just ThreadKilled <- E.fromException e -> do
85− cleanup ref Nothing
98+ cleanup sinfo Nothing
8699 return False
87100 | otherwise -> do
88− cleanup ref (Just e)
101+ cleanup sinfo (Just e)
89102 return True
90− when cont $ go ref th
91− cleanup ref me = do
92− m <- readIORef ref
103+ cont2 <- getThreadContinue tcont
104+ when (cont1 && cont2) $ go sinfo tcont th
105+ cleanup sinfo me = do
106+ m <- getStreamInfo sinfo
93107 case m of
94108 Nothing -> return ()
95109 Just (strm,req) -> do
@@ -99,7 +113,7 @@ worker ctx@Context{inputQ,outputQ} set tm app responder = do
99113 case me of
100114 Nothing -> return ()
101115 Just e -> S.settingsOnException set (Just req) e
102− writeIORef ref Nothing
116+ clearStreamInfo sinfo
103117
104118waiter :: TVar Sync -> TBQueue Sequence
105119 -> (Output -> Priority -> IO ()) -> Stream -> Priority
@@ -122,3 +136,31 @@ waiter tvar sq enq strm pri = do
122136 enq (ONext strm next) pri
123137 waiter tvar sq enq strm pri
124138
139+----------------------------------------------------------------
140+
141+newtype ThreadContinue = ThreadContinue (IORef Bool)
142+
143+newThreadContinue :: IO ThreadContinue
144+newThreadContinue = ThreadContinue <$> newIORef True
145+
146+setThreadContinue :: ThreadContinue -> Bool -> IO ()
147+setThreadContinue (ThreadContinue ref) x = writeIORef ref x
148+
149+getThreadContinue :: ThreadContinue -> IO Bool
150+getThreadContinue (ThreadContinue ref) = readIORef ref
151+
152+----------------------------------------------------------------
153+
154+newtype StreamInfo = StreamInfo (IORef (Maybe (Stream,Request)))
155+
156+newStreamInfo :: IO StreamInfo
157+newStreamInfo = StreamInfo <$> newIORef Nothing
158+
159+clearStreamInfo :: StreamInfo -> IO ()
160+clearStreamInfo (StreamInfo ref) = writeIORef ref Nothing
161+
162+setStreamInfo :: StreamInfo -> Stream -> Request -> IO ()
163+setStreamInfo (StreamInfo ref) strm req = writeIORef ref $ Just (strm,req)
164+
165+getStreamInfo :: StreamInfo -> IO (Maybe (Stream, Request))
166+getStreamInfo (StreamInfo ref) = readIORef ref
warp/warp.cabal+1
@@ -74,6 +74,7 @@ Library
7474 Network.Wai.Handler.Warp.HTTP2
7575 Network.Wai.Handler.Warp.HTTP2.EncodeFrame
7676 Network.Wai.Handler.Warp.HTTP2.HPACK
77+ Network.Wai.Handler.Warp.HTTP2.Manager
7778 Network.Wai.Handler.Warp.HTTP2.Receiver
7879 Network.Wai.Handler.Warp.HTTP2.Request
7980 Network.Wai.Handler.Warp.HTTP2.Sender