Skip to content

Commit ec2f815

Browse files
committed
Make sure sendError sends ErrorResponse when serviceClient
`sendError` should throw an error which interrupts the request/response cycle with an `ErrorResponse`. However, the deadlock fix in 813e665 introduced a bug where we no longer ever send an ErrorResponse when `sendError`, just crashing the client-connection thread with an `error` instead. There are two main situations in which we want to run an `Adaptor s r a`: - When `r == ()`, when unlifting `Adaptor` for the threads registered in `registerNewDebugSession` - When `r == Request`, which means we're responding to a `Request`. In the former case, there's no well-defined meaning for the `sendError` thrown error, since we're not responding to any `Request` -- so we just `error` on those uncaught errors. OK. In the latter case, there IS a well-defined meaning for the `sendError` errors: reply with an `ErrorResponse` to the client. Fixes #27
1 parent bdf4c85 commit ec2f815

2 files changed

Lines changed: 30 additions & 21 deletions

File tree

src/DAP/Adaptor.hs

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ module DAP.Adaptor
5555
, sendRaw
5656
-- * Internal function used to execute actions on behalf of the DAP server
5757
-- from child threads (useful for handling asynchronous debugger events).
58-
, runAdaptorWith
59-
, runAdaptor
58+
, runAdaptorPoly
59+
, runAdaptorRequest
6060
, withRequest
6161
, getHandle
6262
) where
@@ -178,7 +178,7 @@ registerNewDebugSession k v debuggerConcurrentActions = do
178178
let emptyState = AdaptorState MessageTypeEvent []
179179
debuggerThreadState <- liftIO $
180180
DebuggerThreadState
181-
<$> sequence [fork $ action (runAdaptorWith lcl' emptyState) | action <- debuggerConcurrentActions]
181+
<$> sequence [fork $ action (runAdaptorPoly lcl' emptyState) | action <- debuggerConcurrentActions]
182182
liftIO . atomically $ modifyTVar' store (H.insert k (debuggerThreadState, v))
183183
logInfo $ T.pack $ "Registered new debug session: " <> unpack k
184184
setDebugSessionId k
@@ -463,24 +463,29 @@ getReverseRequestResponseBody resp = do
463463
logError (T.pack reason)
464464
liftIO $ throwIO (ParseException reason)
465465
----------------------------------------------------------------------------
466-
-- | Evaluates Adaptor action by using and updating the state in the MVar
467-
runAdaptorWith :: AdaptorLocal app request -> AdaptorState -> Adaptor app request () -> IO ()
468-
runAdaptorWith lcl st (Adaptor action) = do
466+
-- | Run an Adaptor for any parametric 'request' (i.e. this function can be
467+
-- used regardless in a non-Request scenario).
468+
runAdaptorPoly :: AdaptorLocal app request -> AdaptorState -> Adaptor app request a -> IO a
469+
runAdaptorPoly lcl st (Adaptor action) = do
469470
(es,final_st) <- runStateT (runReaderT (runExceptT action) lcl) st
470471
case es of
471472
Left err -> error ("runAdaptorWith, unhandled exception:" <> show err)
472-
Right () -> case final_st of
473+
Right x -> case final_st of
473474
AdaptorState _ p ->
474475
if null p
475-
then return ()
476+
then return x
476477
else error $ "runAdaptorWith, unexpected payload:" <> show p
477478
----------------------------------------------------------------------------
478-
-- | Utility for evaluating a monad transformer stack
479-
runAdaptor :: AdaptorLocal app Request -> AdaptorState -> Adaptor app Request () -> IO ()
480-
runAdaptor lcl s (Adaptor client) =
479+
-- | Run an Adaptor in the context of replying to a 'Request' (notably, this
480+
-- should be used to run the Adaptor servicing the client ('serviceClient')).
481+
--
482+
-- When 'sendError' is used to throw an error in the Adaptor, we cancel the
483+
-- current pending request with an 'ErrorResponse'.
484+
runAdaptorRequest :: AdaptorLocal app Request -> AdaptorState -> Adaptor app Request () -> IO ()
485+
runAdaptorRequest lcl s (Adaptor client) =
481486
runStateT (runReaderT (runExceptT client) lcl) s >>= \case
482487
(Left (errorMessage, maybeMessage), s') ->
483-
runAdaptor lcl s' (sendErrorResponse errorMessage maybeMessage)
488+
runAdaptorRequest lcl s' (sendErrorResponse errorMessage maybeMessage)
484489
(Right (), _) -> pure ()
485490
----------------------------------------------------------------------------
486491

src/DAP/Server.hs

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ module DAP.Server
2424
, TerminateServer(..)
2525
) where
2626
----------------------------------------------------------------------------
27-
import Control.Monad ( when, forever )
27+
import Control.Monad ( when )
2828
import Control.Concurrent ( ThreadId, myThreadId, throwTo )
2929
import Control.Concurrent.MVar ( newMVar )
3030
import Control.Concurrent.STM ( newTVarIO )
@@ -137,15 +137,19 @@ initAdaptorState logAction handle address appStore serverConfig = do
137137
-- because there's no 'Request' to reply to)
138138
serviceClient
139139
:: (Command -> Adaptor app Request ())
140-
-> (ReverseRequestResponse -> Adaptor app r ())
141-
-> AdaptorLocal app r
140+
-> (ReverseRequestResponse -> Adaptor app () ())
141+
-> AdaptorLocal app ()
142142
-> IO ()
143-
serviceClient communicate ackResp lcl = forever $ runAdaptorWith lcl st $ do
144-
either_nextRequest <- getRequest
145-
case either_nextRequest of
146-
Right nextRequest ->
147-
withRequest nextRequest (communicate (command nextRequest))
148-
Left rrr -> ackResp rrr
143+
serviceClient communicate ackResp lcl = do
144+
rrr_or_nextRequest <- runAdaptorPoly lcl st getRequest
145+
case rrr_or_nextRequest of
146+
Right nextRequest -> do
147+
let lcl' = lcl{ request = nextRequest }
148+
runAdaptorRequest lcl' st $
149+
communicate (command nextRequest)
150+
Left rrr ->
151+
runAdaptorPoly lcl st $ ackResp rrr
152+
serviceClient communicate ackResp lcl
149153
where
150154
st = AdaptorState MessageTypeResponse []
151155
----------------------------------------------------------------------------

0 commit comments

Comments
 (0)