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

Stop doing action for handles and ignore responses after shutdown #567

Merged
Show file tree
Hide file tree
Changes from 15 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
4 changes: 4 additions & 0 deletions lsp/src/Language/LSP/Server/Core.hs
Original file line number Diff line number Diff line change
Expand Up @@ -1024,4 +1024,8 @@ be cancelled when we receive `shutdown`.
Shutdown is a request, and the client won't send `exit` until a server responds, so if you
want to be sure that some cleanup happens, you need to ensure we don't respond to `shutdown`
until it's done. The best way to do this is just to install a specific `shutdown` handler.

After the `shutdown` request, we don't handle any more requests and notifications other than
`exit`. We also don't handle any more responses to requests we have sent but just throw the
responses away.
-}
25 changes: 16 additions & 9 deletions lsp/src/Language/LSP/Server/Processing.hs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ instance Pretty LspProcessingLog where
processMessage :: (m ~ LspM config) => LogAction m (WithSeverity LspProcessingLog) -> BSL.ByteString -> m ()
processMessage logger jsonStr = do
pendingResponsesVar <- LspT $ asks $ resPendingResponses . resState
shutdown <- isShuttingDown
join $ liftIO $ atomically $ fmap handleErrors $ runExceptT $ do
val <- except $ eitherDecode jsonStr
pending <- lift $ readTVar pendingResponsesVar
Expand All @@ -100,8 +101,10 @@ processMessage logger jsonStr = do
FromClientMess m mess ->
pure $ handle logger m mess
FromClientRsp (P.Pair (ServerResponseCallback f) (Const !newMap)) res -> do
-- see Note [Shutdown]
writeTVar pendingResponsesVar newMap
pure $ liftIO $ f (res ^. L.result)
unless shutdown <$> do
soulomoon marked this conversation as resolved.
Show resolved Hide resolved
pure $ liftIO $ f (res ^. L.result)
where
parser :: ResponseMap -> Value -> Parser (FromClientMessage' (P.Product ServerResponseCallback (Const ResponseMap)))
parser rm = parseClientMessage $ \i ->
Expand Down Expand Up @@ -449,31 +452,35 @@ handle' ::
TClientMessage meth ->
m ()
handle' logger mAction m msg = do
maybe (return ()) (\f -> f msg) mAction
shutdown <- isShuttingDown
-- These are the methods that we are allowed to process during shutdown.
-- The reason that we include 'shutdown' itself here is because we change
-- state to "shutting down" just _before_ we process the message itself!
let allowedMethod m = case (splitClientMethod m, m) of
soulomoon marked this conversation as resolved.
Show resolved Hide resolved
(IsClientNot, SMethod_Exit) -> True
-- we do not allow SMethod_Shutdown to be called during shutdown
soulomoon marked this conversation as resolved.
Show resolved Hide resolved
_ -> False

case mAction of
Just f | not shutdown || allowedMethod m -> f msg
_ -> pure ()

dynReqHandlers <- getsState resRegistrationsReq
dynNotHandlers <- getsState resRegistrationsNot

env <- getLspEnv
let Handlers{reqHandlers, notHandlers} = resHandlers env
shutdown <- isShuttingDown

case splitClientMethod m of
-- See Note [Shutdown]
IsClientNot | shutdown, not (allowedMethod m) -> notificationDuringShutdown
where
allowedMethod SMethod_Exit = True
allowedMethod _ = False
IsClientNot -> case pickHandler dynNotHandlers notHandlers of
Just h -> liftIO $ h msg
Nothing
| SMethod_Exit <- m -> exitNotificationHandler logger msg
| otherwise -> missingNotificationHandler
-- See Note [Shutdown]
IsClientReq | shutdown, not (allowedMethod m) -> requestDuringShutdown msg
where
allowedMethod SMethod_Shutdown = True
allowedMethod _ = False
IsClientReq -> case pickHandler dynReqHandlers reqHandlers of
Just h -> liftIO $ h msg (runLspT env . sendResponse msg)
Nothing
Expand Down
Loading