Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rethrow async exception #1013

Merged
merged 6 commits into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions auto-update/Control/Reaper.hs
Original file line number Diff line number Diff line change
Expand Up @@ -207,18 +207,22 @@ reaper settings@ReaperSettings{..} stateRef tidRef = do
!merge <- reaperAction wl
-- Merging the left jobs and new jobs.
-- If there is no jobs, this thread finishes.
next <- atomicModifyIORef' stateRef (check merge)
next
cont <- atomicModifyIORef' stateRef (check merge)
if cont
then
reaper settings stateRef tidRef
else
writeIORef tidRef Nothing
where
swapWithEmpty NoReaper = error "Control.Reaper.reaper: unexpected NoReaper (1)"
swapWithEmpty (Workload wl) = (Workload reaperEmpty, wl)

check _ NoReaper = error "Control.Reaper.reaper: unexpected NoReaper (2)"
check merge (Workload wl)
-- If there is no job, reaper is terminated.
| reaperNull wl' = (NoReaper, writeIORef tidRef Nothing)
| reaperNull wl' = (NoReaper, False)
-- If there are jobs, carry them out.
| otherwise = (Workload wl', reaper settings stateRef tidRef)
| otherwise = (Workload wl', True)
where
wl' = merge wl

Expand Down
15 changes: 8 additions & 7 deletions time-manager/System/TimeManager.hs
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,13 @@ module System.TimeManager (
TimeoutThread (..),
) where

import Control.Concurrent (myThreadId)
import Control.Concurrent (myThreadId, mkWeakThreadId)
import qualified Control.Exception as E
import Control.Reaper
import Data.IORef (IORef)
import qualified Data.IORef as I
import Data.Typeable (Typeable)
import GHC.Weak (deRefWeak)

----------------------------------------------------------------

Expand Down Expand Up @@ -107,14 +108,14 @@ register mgr !onTimeout = do
-- | Registering a timeout action of killing this thread.
registerKillThread :: Manager -> TimeoutAction -> IO Handle
registerKillThread m onTimeout = do
-- If we hold ThreadId, the stack and data of the thread is leaked.
-- If we hold Weak ThreadId, the stack is released. However, its
-- data is still leaked probably because of a bug of GHC.
-- So, let's just use ThreadId and release ThreadId by
-- overriding the timeout action by "cancel".
tid <- myThreadId
wtid <- mkWeakThreadId tid
-- First run the timeout action in case the child thread is masked.
register m $ onTimeout `E.finally` E.throwTo tid TimeoutThread
register m $ onTimeout `E.finally` do
mtid <- deRefWeak wtid
case mtid of
Nothing -> return ()
Just tid' -> E.throwTo tid' TimeoutThread

data TimeoutThread = TimeoutThread
deriving (Typeable)
Expand Down
15 changes: 13 additions & 2 deletions warp-tls/Network/Wai/Handler/WarpTLS.hs
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,17 @@ mkConn tlsset set s params = do

----------------------------------------------------------------

isAsyncException :: Exception e => e -> Bool
isAsyncException e =
case E.fromException (E.toException e) of
Just (E.SomeAsyncException _) -> True
Nothing -> False

throughAsync :: IO a -> SomeException -> IO a
throughAsync action (SomeException e)
| isAsyncException e = E.throwIO e
| otherwise = action

httpOverTls
:: TLS.TLSParams params
=> TLSSettings
Expand All @@ -360,13 +371,13 @@ httpOverTls TLSSettings{..} set s bs0 params =
case mconn of
Nothing -> throwIO IncompleteHeaders
Just conn -> return conn
wrappedRecvN recvN n = handle (\(SomeException _) -> mempty) $ recvN n
wrappedRecvN recvN n = handle (throughAsync (return "")) $ recvN n
backend recvN =
TLS.Backend
{ TLS.backendFlush = return ()
#if MIN_VERSION_network(3,1,1)
, TLS.backendClose =
gracefulClose s 5000 `E.catch` \(SomeException _) -> return ()
gracefulClose s 5000 `E.catch` throughAsync (return ())
#else
, TLS.backendClose = close s
#endif
Expand Down
2 changes: 2 additions & 0 deletions warp/Network/Wai/Handler/Warp/HTTP1.hs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ http1server settings ii conn transport app addr th istatus src =
| Just NoKeepAliveRequest <- fromException e = return ()
-- No valid request
| Just (BadFirstLine _) <- fromException e = return ()
| isAsyncException e = throwIO e
| otherwise = do
_ <-
sendErrorResponse
Expand Down Expand Up @@ -204,6 +205,7 @@ processRequest settings ii conn app th istatus src req mremainingRef idxhdr next
Right ResponseReceived -> return ()
Left (e :: SomeException)
| Just (ExceptionInsideResponseBody e') <- fromException e -> throwIO e'
| isAsyncException e -> throwIO e
| otherwise -> do
keepAlive <- sendErrorResponse settings ii conn th istatus req e
settingsOnException settings (Just req) e
Expand Down
3 changes: 2 additions & 1 deletion warp/Network/Wai/Handler/Warp/HTTP2.hs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ http2server label settings ii transport addr app h2req0 aux0 response = do
| Just E.ThreadKilled <- E.fromException e -> return ()
-- killed by the local timeout manager
| Just T.TimeoutThread <- E.fromException e -> return ()
| isAsyncException e -> E.throwIO e
| otherwise -> do
S.settingsOnException settings (Just req) e
let ersp = S.settingsOnExceptionResponse settings e
Expand Down Expand Up @@ -152,7 +153,7 @@ wrappedRecvN th slowlorisSize readN bufsize = do
return bs
where
handler :: E.SomeException -> IO ByteString
handler _ = return ""
handler = throughAsync (return "")

-- connClose must not be called here since Run:fork calls it
goaway :: Connection -> H2.ErrorCodeId -> ByteString -> IO ()
Expand Down
14 changes: 14 additions & 0 deletions warp/Network/Wai/Handler/Warp/Imports.hs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@ module Network.Wai.Handler.Warp.Imports (
module Data.Word,
module Data.Maybe,
module Numeric,
throughAsync,
isAsyncException,
) where

import Control.Applicative
import Control.Exception
import Control.Monad
import Data.Bits
import Data.ByteString.Internal (ByteString (..))
Expand All @@ -23,3 +26,14 @@ import Data.Monoid
import Data.Ord
import Data.Word
import Numeric

isAsyncException :: Exception e => e -> Bool
isAsyncException e =
case fromException (toException e) of
Just (SomeAsyncException _) -> True
Nothing -> False

throughAsync :: IO a -> SomeException -> IO a
throughAsync action (SomeException e)
| isAsyncException e = throwIO e
| otherwise = action
6 changes: 3 additions & 3 deletions warp/Network/Wai/Handler/Warp/Run.hs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ socketConnection _ s = do
else settingsGracefulCloseTimeout1 set
if tm == 0
then close s
else gracefulClose s tm `E.catch` \(E.SomeException _) -> return ()
else gracefulClose s tm `E.catch` throughAsync (return ())
#else
, connClose = close s
#endif
Expand Down Expand Up @@ -179,7 +179,7 @@ runSettingsSocket set@Settings{settingsAccept = accept'} socket app = do
(s, sa) <- accept' socket
setSocketCloseOnExec s
-- NoDelay causes an error for AF_UNIX.
setSocketOption s NoDelay 1 `E.catch` \(E.SomeException _) -> return ()
setSocketOption s NoDelay 1 `E.catch` throughAsync (return ())
conn <- socketConnection set s
return (conn, sa)

Expand Down Expand Up @@ -369,7 +369,7 @@ fork set mkConn addr app counter ii = settingsFork set $ \unmask -> do
-- above ensures the connection is closed.
when goingon $ serveConnection conn ii th addr transport set app
where
register = T.registerKillThread (timeoutManager ii) (connClose conn)
register = T.registerKillThread (timeoutManager ii) (return ())
cancel = T.cancel

onOpen adr = increase counter >> settingsOnOpen set adr
Expand Down
8 changes: 4 additions & 4 deletions warp/Network/Wai/Handler/Warp/Settings.hs
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@

module Network.Wai.Handler.Warp.Settings where

import Control.Exception (SomeException, fromException)
import Control.Exception (SomeException(..), fromException, throw)
import qualified Data.ByteString.Builder as Builder
import qualified Data.ByteString.Char8 as C8
import Data.Streaming.Network (HostPreference)
import qualified Data.Text as T
import qualified Data.Text.IO as TIO
import Data.Version (showVersion)
import GHC.IO (IO (IO), unsafeUnmask)
import GHC.IO.Exception (AsyncException (ThreadKilled), IOErrorType (..))
import GHC.IO.Exception (IOErrorType (..))
import GHC.Prim (fork#)
import qualified Network.HTTP.Types as H
import Network.Socket (SockAddr, Socket, accept)
Expand Down Expand Up @@ -228,12 +228,11 @@ defaultSettings =
-- Since 2.1.3
defaultShouldDisplayException :: SomeException -> Bool
defaultShouldDisplayException se
| Just ThreadKilled <- fromException se = False
| Just (_ :: InvalidRequest) <- fromException se = False
| Just (ioeGetErrorType -> et) <- fromException se
, et == ResourceVanished || et == InvalidArgument =
False
| Just TimeoutThread <- fromException se = False
| isAsyncException se = False
| otherwise = True

-- | Printing an exception to standard error
Expand All @@ -255,6 +254,7 @@ defaultOnException _ e =
-- Since 3.2.27
defaultOnExceptionResponse :: SomeException -> Response
defaultOnExceptionResponse e
| isAsyncException e = throw e
| Just PayloadTooLarge <- fromException e =
responseLBS
H.status413
Expand Down
Loading