{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Network.HTTP2.H2.Manager (
Manager,
start,
stopAfter,
forkManaged,
forkManagedUnmask,
forkManagedTimeout,
KilledByHttp2ThreadManager (..),
waitCounter0,
) where
import Control.Concurrent
import Control.Concurrent.STM
import Control.Exception
import qualified Control.Exception as E
import Data.Foldable
import Data.IORef
import Data.IntMap (IntMap)
import qualified Data.IntMap.Strict as Map
import System.Mem.Weak (Weak, deRefWeak)
import qualified System.TimeManager as T
import Imports
data Manager = Manager T.Manager (TVar ManagedThreads)
type ManagedThreads = IntMap ManagedThread
data ManagedThread = ManagedThread (Weak ThreadId) (IORef Bool)
start :: T.Manager -> IO Manager
start :: Manager -> IO Manager
start Manager
timmgr = Manager -> TVar ManagedThreads -> Manager
Manager Manager
timmgr (TVar ManagedThreads -> Manager)
-> IO (TVar ManagedThreads) -> IO Manager
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ManagedThreads -> IO (TVar ManagedThreads)
forall a. a -> IO (TVar a)
newTVarIO ManagedThreads
forall a. IntMap a
Map.empty
data KilledByHttp2ThreadManager = KilledByHttp2ThreadManager (Maybe SomeException)
deriving (Int -> KilledByHttp2ThreadManager -> ShowS
[KilledByHttp2ThreadManager] -> ShowS
KilledByHttp2ThreadManager -> String
(Int -> KilledByHttp2ThreadManager -> ShowS)
-> (KilledByHttp2ThreadManager -> String)
-> ([KilledByHttp2ThreadManager] -> ShowS)
-> Show KilledByHttp2ThreadManager
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> KilledByHttp2ThreadManager -> ShowS
showsPrec :: Int -> KilledByHttp2ThreadManager -> ShowS
$cshow :: KilledByHttp2ThreadManager -> String
show :: KilledByHttp2ThreadManager -> String
$cshowList :: [KilledByHttp2ThreadManager] -> ShowS
showList :: [KilledByHttp2ThreadManager] -> ShowS
Show)
instance Exception KilledByHttp2ThreadManager where
toException :: KilledByHttp2ThreadManager -> SomeException
toException = KilledByHttp2ThreadManager -> SomeException
forall e. Exception e => e -> SomeException
asyncExceptionToException
fromException :: SomeException -> Maybe KilledByHttp2ThreadManager
fromException = SomeException -> Maybe KilledByHttp2ThreadManager
forall e. Exception e => SomeException -> Maybe e
asyncExceptionFromException
stopAfter :: Manager -> IO a -> (Maybe SomeException -> IO ()) -> IO a
stopAfter :: forall a. Manager -> IO a -> (Maybe SomeException -> IO ()) -> IO a
stopAfter (Manager Manager
_timmgr TVar ManagedThreads
var) IO a
action Maybe SomeException -> IO ()
cleanup = do
((forall a. IO a -> IO a) -> IO a) -> IO a
forall b. ((forall a. IO a -> IO a) -> IO b) -> IO b
mask (((forall a. IO a -> IO a) -> IO a) -> IO a)
-> ((forall a. IO a -> IO a) -> IO a) -> IO a
forall a b. (a -> b) -> a -> b
$ \forall a. IO a -> IO a
unmask -> do
Either SomeException a
ma <- IO a -> IO (Either SomeException a)
forall e a. Exception e => IO a -> IO (Either e a)
try (IO a -> IO (Either SomeException a))
-> IO a -> IO (Either SomeException a)
forall a b. (a -> b) -> a -> b
$ IO a -> IO a
forall a. IO a -> IO a
unmask IO a
action
ManagedThreads
m <- STM ManagedThreads -> IO ManagedThreads
forall a. STM a -> IO a
atomically (STM ManagedThreads -> IO ManagedThreads)
-> STM ManagedThreads -> IO ManagedThreads
forall a b. (a -> b) -> a -> b
$ do
ManagedThreads
m0 <- TVar ManagedThreads -> STM ManagedThreads
forall a. TVar a -> STM a
readTVar TVar ManagedThreads
var
TVar ManagedThreads -> ManagedThreads -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar ManagedThreads
var ManagedThreads
forall a. IntMap a
Map.empty
ManagedThreads -> STM ManagedThreads
forall a. a -> STM a
forall (m :: * -> *) a. Monad m => a -> m a
return ManagedThreads
m0
let ths :: [ManagedThread]
ths = ManagedThreads -> [ManagedThread]
forall a. IntMap a -> [a]
Map.elems ManagedThreads
m
er :: Maybe SomeException
er = (SomeException -> Maybe SomeException)
-> (a -> Maybe SomeException)
-> Either SomeException a
-> Maybe SomeException
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either SomeException -> Maybe SomeException
forall a. a -> Maybe a
Just (Maybe SomeException -> a -> Maybe SomeException
forall a b. a -> b -> a
const Maybe SomeException
forall a. Maybe a
Nothing) Either SomeException a
ma
ex :: KilledByHttp2ThreadManager
ex = Maybe SomeException -> KilledByHttp2ThreadManager
KilledByHttp2ThreadManager Maybe SomeException
er
[ManagedThread] -> (ManagedThread -> IO ()) -> IO ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
t a -> (a -> m b) -> m ()
forM_ [ManagedThread]
ths ((ManagedThread -> IO ()) -> IO ())
-> (ManagedThread -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \(ManagedThread Weak ThreadId
wtid IORef Bool
ref) -> Weak ThreadId -> IORef Bool -> KilledByHttp2ThreadManager -> IO ()
forall e. Exception e => Weak ThreadId -> IORef Bool -> e -> IO ()
lockAndKill Weak ThreadId
wtid IORef Bool
ref KilledByHttp2ThreadManager
ex
case Either SomeException a
ma of
Left SomeException
err -> Maybe SomeException -> IO ()
cleanup (SomeException -> Maybe SomeException
forall a. a -> Maybe a
Just SomeException
err) IO () -> IO a -> IO a
forall a b. IO a -> IO b -> IO b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> SomeException -> IO a
forall e a. Exception e => e -> IO a
throwIO SomeException
err
Right a
a -> Maybe SomeException -> IO ()
cleanup Maybe SomeException
forall a. Maybe a
Nothing IO () -> IO a -> IO a
forall a b. IO a -> IO b -> IO b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> a -> IO a
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return a
a
forkManaged :: Manager -> String -> IO () -> IO ()
forkManaged :: Manager -> String -> IO () -> IO ()
forkManaged Manager
mgr String
label IO ()
io =
Manager -> String -> ((forall a. IO a -> IO a) -> IO ()) -> IO ()
forkManagedUnmask Manager
mgr String
label (((forall a. IO a -> IO a) -> IO ()) -> IO ())
-> ((forall a. IO a -> IO a) -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \forall a. IO a -> IO a
unmask -> IO () -> IO ()
forall a. IO a -> IO a
unmask IO ()
io
forkManagedUnmask
:: Manager -> String -> ((forall x. IO x -> IO x) -> IO ()) -> IO ()
forkManagedUnmask :: Manager -> String -> ((forall a. IO a -> IO a) -> IO ()) -> IO ()
forkManagedUnmask (Manager Manager
_timmgr TVar ManagedThreads
var) String
label (forall a. IO a -> IO a) -> IO ()
io =
IO ThreadId -> IO ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (IO ThreadId -> IO ()) -> IO ThreadId -> IO ()
forall a b. (a -> b) -> a -> b
$ IO ThreadId -> IO ThreadId
forall a. IO a -> IO a
mask_ (IO ThreadId -> IO ThreadId) -> IO ThreadId -> IO ThreadId
forall a b. (a -> b) -> a -> b
$ ((forall a. IO a -> IO a) -> IO ()) -> IO ThreadId
forkIOWithUnmask (((forall a. IO a -> IO a) -> IO ()) -> IO ThreadId)
-> ((forall a. IO a -> IO a) -> IO ()) -> IO ThreadId
forall a b. (a -> b) -> a -> b
$ \forall a. IO a -> IO a
unmask -> (KilledByHttp2ThreadManager -> IO ()) -> IO () -> IO ()
forall e a. Exception e => (e -> IO a) -> IO a -> IO a
E.handle KilledByHttp2ThreadManager -> IO ()
ignore (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
String -> IO ()
labelMe String
label
IO (Int, Weak ThreadId, IORef Bool)
-> ((Int, Weak ThreadId, IORef Bool) -> IO ())
-> ((Int, Weak ThreadId, IORef Bool) -> IO ())
-> IO ()
forall a b c. IO a -> (a -> IO b) -> (a -> IO c) -> IO c
E.bracket (TVar ManagedThreads -> IO (Int, Weak ThreadId, IORef Bool)
setup TVar ManagedThreads
var) (TVar ManagedThreads -> (Int, Weak ThreadId, IORef Bool) -> IO ()
clear TVar ManagedThreads
var) (((Int, Weak ThreadId, IORef Bool) -> IO ()) -> IO ())
-> ((Int, Weak ThreadId, IORef Bool) -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \(Int, Weak ThreadId, IORef Bool)
_ -> (forall a. IO a -> IO a) -> IO ()
io IO x -> IO x
forall a. IO a -> IO a
unmask
forkManagedTimeout :: Manager -> String -> (T.Handle -> IO ()) -> IO ()
forkManagedTimeout :: Manager -> String -> (Handle -> IO ()) -> IO ()
forkManagedTimeout (Manager Manager
timmgr TVar ManagedThreads
var) String
label Handle -> IO ()
io =
IO ThreadId -> IO ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (IO ThreadId -> IO ()) -> IO ThreadId -> IO ()
forall a b. (a -> b) -> a -> b
$ IO () -> IO ThreadId
forkIO (IO () -> IO ThreadId) -> IO () -> IO ThreadId
forall a b. (a -> b) -> a -> b
$ (KilledByHttp2ThreadManager -> IO ()) -> IO () -> IO ()
forall e a. Exception e => (e -> IO a) -> IO a -> IO a
E.handle KilledByHttp2ThreadManager -> IO ()
ignore (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
String -> IO ()
labelMe String
label
IO (Int, Weak ThreadId, IORef Bool)
-> ((Int, Weak ThreadId, IORef Bool) -> IO ())
-> ((Int, Weak ThreadId, IORef Bool) -> IO ())
-> IO ()
forall a b c. IO a -> (a -> IO b) -> (a -> IO c) -> IO c
E.bracket (TVar ManagedThreads -> IO (Int, Weak ThreadId, IORef Bool)
setup TVar ManagedThreads
var) (TVar ManagedThreads -> (Int, Weak ThreadId, IORef Bool) -> IO ()
clear TVar ManagedThreads
var) (((Int, Weak ThreadId, IORef Bool) -> IO ()) -> IO ())
-> ((Int, Weak ThreadId, IORef Bool) -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \(Int
_n, Weak ThreadId
wtid, IORef Bool
ref) ->
Manager -> IO () -> (Handle -> IO ()) -> IO ()
forall a. Manager -> IO () -> (Handle -> IO a) -> IO a
T.withHandle Manager
timmgr (Weak ThreadId -> IORef Bool -> TimeoutThread -> IO ()
forall e. Exception e => Weak ThreadId -> IORef Bool -> e -> IO ()
lockAndKill Weak ThreadId
wtid IORef Bool
ref TimeoutThread
T.TimeoutThread) Handle -> IO ()
io
setup :: TVar (IntMap ManagedThread) -> IO (Int, Weak ThreadId, IORef Bool)
setup :: TVar ManagedThreads -> IO (Int, Weak ThreadId, IORef Bool)
setup TVar ManagedThreads
var = do
(Weak ThreadId
wtid, Int
n) <- IO (Weak ThreadId, Int)
myWeakThradId
IORef Bool
ref <- Bool -> IO (IORef Bool)
forall a. a -> IO (IORef a)
newIORef Bool
False
let ent :: ManagedThread
ent = Weak ThreadId -> IORef Bool -> ManagedThread
ManagedThread Weak ThreadId
wtid IORef Bool
ref
STM () -> IO ()
forall a. STM a -> IO a
atomically (STM () -> IO ()) -> STM () -> IO ()
forall a b. (a -> b) -> a -> b
$ TVar ManagedThreads -> (ManagedThreads -> ManagedThreads) -> STM ()
forall a. TVar a -> (a -> a) -> STM ()
modifyTVar' TVar ManagedThreads
var ((ManagedThreads -> ManagedThreads) -> STM ())
-> (ManagedThreads -> ManagedThreads) -> STM ()
forall a b. (a -> b) -> a -> b
$ Int -> ManagedThread -> ManagedThreads -> ManagedThreads
forall a. Int -> a -> IntMap a -> IntMap a
Map.insert Int
n ManagedThread
ent
(Int, Weak ThreadId, IORef Bool)
-> IO (Int, Weak ThreadId, IORef Bool)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Int
n, Weak ThreadId
wtid, IORef Bool
ref)
lockAndKill :: Exception e => Weak ThreadId -> IORef Bool -> e -> IO ()
lockAndKill :: forall e. Exception e => Weak ThreadId -> IORef Bool -> e -> IO ()
lockAndKill Weak ThreadId
wtid IORef Bool
ref e
e = do
Bool
alreadyLocked <- IORef Bool -> (Bool -> (Bool, Bool)) -> IO Bool
forall a b. IORef a -> (a -> (a, b)) -> IO b
atomicModifyIORef' IORef Bool
ref (\Bool
b -> (Bool
True, Bool
b))
Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless Bool
alreadyLocked (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
Maybe ThreadId
mtid <- Weak ThreadId -> IO (Maybe ThreadId)
forall v. Weak v -> IO (Maybe v)
deRefWeak Weak ThreadId
wtid
case Maybe ThreadId
mtid of
Maybe ThreadId
Nothing -> () -> IO ()
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ()
Just ThreadId
tid -> ThreadId -> e -> IO ()
forall e. Exception e => ThreadId -> e -> IO ()
E.throwTo ThreadId
tid e
e
clear
:: TVar (IntMap ManagedThread)
-> (Map.Key, Weak ThreadId, IORef Bool)
-> IO ()
clear :: TVar ManagedThreads -> (Int, Weak ThreadId, IORef Bool) -> IO ()
clear TVar ManagedThreads
var (Int
n, Weak ThreadId
_, IORef Bool
_) = STM () -> IO ()
forall a. STM a -> IO a
atomically (STM () -> IO ()) -> STM () -> IO ()
forall a b. (a -> b) -> a -> b
$ TVar ManagedThreads -> (ManagedThreads -> ManagedThreads) -> STM ()
forall a. TVar a -> (a -> a) -> STM ()
modifyTVar' TVar ManagedThreads
var ((ManagedThreads -> ManagedThreads) -> STM ())
-> (ManagedThreads -> ManagedThreads) -> STM ()
forall a b. (a -> b) -> a -> b
$ Int -> ManagedThreads -> ManagedThreads
forall a. Int -> IntMap a -> IntMap a
Map.delete Int
n
ignore :: KilledByHttp2ThreadManager -> IO ()
ignore :: KilledByHttp2ThreadManager -> IO ()
ignore (KilledByHttp2ThreadManager Maybe SomeException
_) = () -> IO ()
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ()
waitCounter0 :: Manager -> IO ()
waitCounter0 :: Manager -> IO ()
waitCounter0 (Manager Manager
_timmgr TVar ManagedThreads
var) = STM () -> IO ()
forall a. STM a -> IO a
atomically (STM () -> IO ()) -> STM () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
ManagedThreads
m <- TVar ManagedThreads -> STM ManagedThreads
forall a. TVar a -> STM a
readTVar TVar ManagedThreads
var
Bool -> STM ()
check (ManagedThreads -> Int
forall a. IntMap a -> Int
Map.size ManagedThreads
m Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0)
myWeakThradId :: IO (Weak ThreadId, Int)
myWeakThradId :: IO (Weak ThreadId, Int)
myWeakThradId = do
ThreadId
tid <- IO ThreadId
myThreadId
Weak ThreadId
wtid <- ThreadId -> IO (Weak ThreadId)
mkWeakThreadId ThreadId
tid
let n :: Int
n = String -> Int
forall a. Read a => String -> a
read (Int -> ShowS
forall a. Int -> [a] -> [a]
drop Int
9 ShowS -> ShowS
forall a b. (a -> b) -> a -> b
$ ThreadId -> String
forall a. Show a => a -> String
show ThreadId
tid)
(Weak ThreadId, Int) -> IO (Weak ThreadId, Int)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Weak ThreadId
wtid, Int
n)