diff options
author | Zubin Duggal <zubin@cmi.ac.in> | 2021-02-17 22:14:02 +0530 |
---|---|---|
committer | Ben Gamari <ben@smart-cactus.org> | 2021-04-07 18:37:46 -0400 |
commit | 917a4595cb1e93c32baf5d4f5f893858fcf5c7ce (patch) | |
tree | 8c8ef9259abcbb39d3c174f14ac7a0ad958ce9eb | |
parent | 8d4fd516d4069e8c9fe500b04e76e3b21096b29b (diff) | |
download | haskell-917a4595cb1e93c32baf5d4f5f893858fcf5c7ce.tar.gz |
Don't catch async exceptions when evaluating Template Haskell
(cherry picked from commit df6d42d0c2534fe620798aab01a393dbd40573fb)
(cherry picked from commit 902ece87ffac545451c2a66d145e6c8653e12092)
(cherry picked from commit 629dd56deedcd1162ef5417a9e446f6f1c2b667a)
(cherry picked from commit c703cb398f980a1a799a5a7dbee763db12cc5f7f)
-rw-r--r-- | compiler/GHC/Data/IOEnv.hs | 23 |
1 files changed, 20 insertions, 3 deletions
diff --git a/compiler/GHC/Data/IOEnv.hs b/compiler/GHC/Data/IOEnv.hs index 5478af0eee..ac2d4f0998 100644 --- a/compiler/GHC/Data/IOEnv.hs +++ b/compiler/GHC/Data/IOEnv.hs @@ -1,6 +1,7 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE DerivingVia #-} + -- -- (c) The University of Glasgow 2002-2006 -- @@ -48,6 +49,8 @@ import Control.Monad.Trans.Reader import Control.Monad.Catch (MonadCatch, MonadMask, MonadThrow) import GHC.Utils.Monad import Control.Applicative (Alternative(..)) +import Control.Concurrent.MVar (newEmptyMVar, readMVar, putMVar) +import Control.Concurrent (forkIO, killThread) ---------------------------------------------------------------------- -- Defining the monad type @@ -140,12 +143,26 @@ tryM (IOEnv thing) = IOEnv (\ env -> tryIOEnvFailure (thing env)) tryIOEnvFailure :: IO a -> IO (Either IOEnvFailure a) tryIOEnvFailure = try --- XXX We shouldn't be catching everything, e.g. timeouts tryAllM :: IOEnv env r -> IOEnv env (Either SomeException r) --- Catch *all* exceptions +-- Catch *all* synchronous exceptions -- This is used when running a Template-Haskell splice, when -- even a pattern-match failure is a programmer error -tryAllM (IOEnv thing) = IOEnv (\ env -> try (thing env)) +tryAllM (IOEnv thing) = IOEnv (\ env -> safeTry (thing env)) + +-- | Like 'try', but doesn't catch asynchronous exceptions +safeTry :: IO a -> IO (Either SomeException a) +safeTry act = do + var <- newEmptyMVar + -- uninterruptible because we want to mask around 'killThread', which is interruptible. + uninterruptibleMask $ \restore -> do + -- Fork, so that 'act' is safe from all asynchronous exceptions other than the ones we send it + t <- forkIO $ try (restore act) >>= putMVar var + restore (readMVar var) + `catch` \(e :: SomeException) -> do + -- Control reaches this point only if the parent thread was sent an async exception + -- In that case, kill the 'act' thread and re-raise the exception + killThread t + throwIO e tryMostM :: IOEnv env r -> IOEnv env (Either SomeException r) tryMostM (IOEnv thing) = IOEnv (\ env -> tryMost (thing env)) |