I have a Yesod app with a warp server, and some of its functionality depends on async exceptions. Recently there arose a need to migrate it to https, which I did with the package warp-tls
. However now I can't kill the warp thread by throwing ThreadKilled
exceptions at it, the throwTo
function just hangs and does nothing.
Consider the following example. Here we monitor the state of warp thread with MVar ()
, which is empty while the thread is running, and has a () value when the thread is killed.
import MyApplication (waiPage)
-- waiPage :: Application
runWai :: MVar () -> IO ()
runWai finishVar = bracket
(return ())
(const $ putMVar finishVar ())
(const runApp)
where
-- change this to normal or tls to check
runApp = runAppTls
runAppNormal = runSettings warpSettings waiPage
runAppTls = runTLS siteTlsSettings warpSettings waiPage
--
warpSettings = setPort 8080 defaultSettings
siteTlsSettings = tlsSettings "cert.pem" "key.pem"
main :: IO ()
main = do
finishVar <- newEmptyMVar
thread_id <- forkIO $ runWai finishVar
-- Try to kill warp thread. Fork because throw might hang
forkIO $ throwTo thread_id ThreadKilled
threadDelay (2 * 10^6) -- microseconds to seconds
isAlive <- isEmptyMVar finishVar
if isAlive then putStrLn "Couldn't kill warp thread"
else putStrLn "Succesfully killed warp thread"
-- Wait for forked warp thread to finish
readMVar finishVar
When you have runApp = runAppNormal
, you will get Succesfully killed warp thread
message, and the application will exit.
When you have runApp = runAppTls
, you will get the Couldn't kill warp thread
message, and the app will hang and keep serving.
So how do I get rid of this exception-intercepting behavior? Or at Least is there a way to kill the warpTls thread in any other way?
It turned out to be a Windows-only bug in the version I used which is warp-tls == 3.2.4
that got fixed in a later version. I've looked at the fix and it's a function with the name windowsThreadBlockHack
, so if anyone is stuck with a bit outdated warp, you can backport this fix for you too.