@@ -9,7 +9,7 @@ module Network.HTTP2.H2.Manager (
9
9
stopAfter ,
10
10
forkManaged ,
11
11
forkManagedUnmask ,
12
- withTimeout ,
12
+ forkManagedTimeout ,
13
13
KilledByHttp2ThreadManager (.. ),
14
14
waitCounter0 ,
15
15
) where
@@ -19,8 +19,10 @@ import Control.Concurrent.STM
19
19
import Control.Exception
20
20
import qualified Control.Exception as E
21
21
import Data.Foldable
22
- import Data.Map (Map )
23
- import qualified Data.Map.Strict as Map
22
+ import Data.IORef
23
+ import Data.IntMap (IntMap )
24
+ import qualified Data.IntMap.Strict as Map
25
+ import System.Mem.Weak (Weak , deRefWeak )
24
26
import qualified System.TimeManager as T
25
27
26
28
import Imports
@@ -30,17 +32,15 @@ import Imports
30
32
-- | Manager to manage the thread and the timer.
31
33
data Manager = Manager T. Manager (TVar ManagedThreads )
32
34
33
- type ManagedThreads = Map ThreadId TimeoutHandle
35
+ type ManagedThreads = IntMap ManagedThread
34
36
35
37
----------------------------------------------------------------
36
38
37
- data TimeoutHandle
38
- = ThreadWithTimeout T. Handle
39
- | ThreadWithoutTimeout
40
-
41
- cancelTimeout :: TimeoutHandle -> IO ()
42
- cancelTimeout (ThreadWithTimeout th) = T. cancel th
43
- cancelTimeout ThreadWithoutTimeout = return ()
39
+ -- 'IORef' prevents race between WAI TimeManager (TimeoutThread)
40
+ -- and stopAfter (KilledByHttp2ThreadManager).
41
+ -- It is initialized with 'False' and turned into 'True' when locked.
42
+ -- The winner can throw an asynchronous exception.
43
+ data ManagedThread = ManagedThread (Weak ThreadId ) (IORef Bool )
44
44
45
45
----------------------------------------------------------------
46
46
@@ -74,10 +74,10 @@ stopAfter (Manager _timmgr var) action cleanup = do
74
74
m0 <- readTVar var
75
75
writeTVar var Map. empty
76
76
return m0
77
- forM_ ( Map. elems m) cancelTimeout
78
- let er = either Just (const Nothing ) ma
79
- forM_ ( Map. keys m) $ \ tid ->
80
- E. throwTo tid $ KilledByHttp2ThreadManager er
77
+ let ths = Map. elems m
78
+ er = either Just (const Nothing ) ma
79
+ ex = KilledByHttp2ThreadManager er
80
+ forM_ ths $ \ ( ManagedThread wtid ref) -> lockAndKill wtid ref ex
81
81
case ma of
82
82
Left err -> cleanup (Just err) >> throwIO err
83
83
Right a -> cleanup Nothing >> return a
@@ -97,18 +97,44 @@ forkManaged mgr label io =
97
97
forkManagedUnmask
98
98
:: Manager -> String -> ((forall x . IO x -> IO x ) -> IO () ) -> IO ()
99
99
forkManagedUnmask (Manager _timmgr var) label io =
100
- -- This is the top level of thread.
101
- -- So, SomeException should be reasonable.
102
100
void $ mask_ $ forkIOWithUnmask $ \ unmask -> E. handle ignore $ do
103
101
labelMe label
104
- tid <- myThreadId
105
- atomically $ modifyTVar var $ Map. insert tid ThreadWithoutTimeout
106
- -- We catch the exception and do not rethrow it: we don't want the
107
- -- exception printed to stderr.
108
- io unmask `catch` ignore
109
- atomically $ modifyTVar var $ Map. delete tid
110
- where
111
- ignore (E. SomeException _) = return ()
102
+ E. bracket (setup var) (clear var) $ \ _ -> io unmask
103
+
104
+ forkManagedTimeout :: Manager -> String -> (T. Handle -> IO () ) -> IO ()
105
+ forkManagedTimeout (Manager timmgr var) label io =
106
+ void $ forkIO $ E. handle ignore $ do
107
+ labelMe label
108
+ E. bracket (setup var) (clear var) $ \ (_n, wtid, ref) ->
109
+ -- 'TimeoutThread' is ignored by 'withHandle'.
110
+ T. withHandle timmgr (lockAndKill wtid ref T. TimeoutThread ) io
111
+
112
+ setup :: TVar (IntMap ManagedThread ) -> IO (Int , Weak ThreadId , IORef Bool )
113
+ setup var = do
114
+ (wtid, n) <- myWeakThradId
115
+ ref <- newIORef False
116
+ let ent = ManagedThread wtid ref
117
+ -- asking to throw KilledByHttp2ThreadManager to me
118
+ atomically $ modifyTVar' var $ Map. insert n ent
119
+ return (n, wtid, ref)
120
+
121
+ lockAndKill :: Exception e => Weak ThreadId -> IORef Bool -> e -> IO ()
122
+ lockAndKill wtid ref e = do
123
+ alreadyLocked <- atomicModifyIORef' ref (\ b -> (True , b)) -- try to lock
124
+ unless alreadyLocked $ do
125
+ mtid <- deRefWeak wtid
126
+ case mtid of
127
+ Nothing -> return ()
128
+ Just tid -> E. throwTo tid e
129
+
130
+ clear
131
+ :: TVar (IntMap ManagedThread )
132
+ -> (Map. Key , Weak ThreadId , IORef Bool )
133
+ -> IO ()
134
+ clear var (n, _, _) = atomically $ modifyTVar' var $ Map. delete n
135
+
136
+ ignore :: KilledByHttp2ThreadManager -> IO ()
137
+ ignore (KilledByHttp2ThreadManager _) = return ()
112
138
113
139
waitCounter0 :: Manager -> IO ()
114
140
waitCounter0 (Manager _timmgr var) = atomically $ do
@@ -117,10 +143,9 @@ waitCounter0 (Manager _timmgr var) = atomically $ do
117
143
118
144
----------------------------------------------------------------
119
145
120
- withTimeout :: Manager -> (T. Handle -> IO () ) -> IO ()
121
- withTimeout (Manager timmgr var) action =
122
- T. withHandleKillThread timmgr (return () ) $ \ th -> do
123
- tid <- myThreadId
124
- -- overriding ThreadWithoutTimeout
125
- atomically $ modifyTVar var $ Map. insert tid $ ThreadWithTimeout th
126
- action th
146
+ myWeakThradId :: IO (Weak ThreadId , Int )
147
+ myWeakThradId = do
148
+ tid <- myThreadId
149
+ wtid <- mkWeakThreadId tid
150
+ let n = read (drop 9 $ show tid) -- drop "ThreadId "
151
+ return (wtid, n)
0 commit comments