Skip to content

Commit

Permalink
[clangd] Handle exit notification (proper shutdown)
Browse files Browse the repository at this point in the history
Summary:
This changes the onShutdown handler to do essentially nothing (for now), and
instead exits the runloop when we receive the exit notification from the client.

Some clients may wait on the reply from the shutdown request before sending an
exit notification. If we exit the runloop already in the shutdown request, a
client might block forever.

This also gives us the opportunity to do any global cleanups and/or
serializations of PCH preambles to disk, but I've left that out for now.

See the LSP protocol documentation for details.

Reviewers: malaperle, krasimir, bkramer, sammccall, ilya-biryukov

Reviewed By: malaperle, sammccall, ilya-biryukov

Subscribers: cfe-commits

Differential Revision: https://reviews.llvm.org/D38939

llvm-svn: 316564
  • Loading branch information
ilya-biryukov committed Oct 25, 2017
1 parent 1f74211 commit 0d9b8a3
Show file tree
Hide file tree
Showing 26 changed files with 108 additions and 6 deletions.
10 changes: 8 additions & 2 deletions clang-tools-extra/clangd/ClangdLSPServer.cpp
Expand Up @@ -56,9 +56,13 @@ void ClangdLSPServer::onInitialize(Ctx C, InitializeParams &Params) {
}

void ClangdLSPServer::onShutdown(Ctx C, ShutdownParams &Params) {
IsDone = true;
// Do essentially nothing, just say we're ready to exit.
ShutdownRequestReceived = true;
C.reply("null");
}

void ClangdLSPServer::onExit(Ctx C, ExitParams &Params) { IsDone = true; }

void ClangdLSPServer::onDocumentDidOpen(Ctx C,
DidOpenTextDocumentParams &Params) {
if (Params.metadata && !Params.metadata->extraFlags.empty())
Expand Down Expand Up @@ -197,7 +201,7 @@ ClangdLSPServer::ClangdLSPServer(JSONOutput &Out, unsigned AsyncThreadsCount,
/*EnableSnippetsAndCodePatterns=*/SnippetCompletions),
/*Logger=*/Out, ResourceDir) {}

void ClangdLSPServer::run(std::istream &In) {
bool ClangdLSPServer::run(std::istream &In) {
assert(!IsDone && "Run was called before");

// Set up JSONRPCDispatcher.
Expand All @@ -213,6 +217,8 @@ void ClangdLSPServer::run(std::istream &In) {
// Make sure IsDone is set to true after this method exits to ensure assertion
// at the start of the method fires if it's ever executed again.
IsDone = true;

return ShutdownRequestReceived;
}

std::vector<clang::tooling::Replacement>
Expand Down
9 changes: 8 additions & 1 deletion clang-tools-extra/clangd/ClangdLSPServer.h
Expand Up @@ -39,7 +39,9 @@ class ClangdLSPServer : private DiagnosticsConsumer, private ProtocolCallbacks {
/// opened in binary mode. Output will be written using Out variable passed to
/// class constructor. This method must not be executed more than once for
/// each instance of ClangdLSPServer.
void run(std::istream &In);
///
/// \return Wether we received a 'shutdown' request before an 'exit' request
bool run(std::istream &In);

private:
// Implement DiagnosticsConsumer.
Expand All @@ -50,6 +52,7 @@ class ClangdLSPServer : private DiagnosticsConsumer, private ProtocolCallbacks {
// Implement ProtocolCallbacks.
void onInitialize(Ctx C, InitializeParams &Params) override;
void onShutdown(Ctx C, ShutdownParams &Params) override;
void onExit(Ctx C, ExitParams &Params) override;
void onDocumentDidOpen(Ctx C, DidOpenTextDocumentParams &Params) override;
void onDocumentDidChange(Ctx C, DidChangeTextDocumentParams &Params) override;
void onDocumentDidClose(Ctx C, DidCloseTextDocumentParams &Params) override;
Expand Down Expand Up @@ -79,6 +82,10 @@ class ClangdLSPServer : private DiagnosticsConsumer, private ProtocolCallbacks {
JSONOutput &Out;
/// Used to indicate that the 'shutdown' request was received from the
/// Language Server client.
bool ShutdownRequestReceived = false;

/// Used to indicate that the 'exit' notification was received from the
/// Language Server client.
/// It's used to break out of the LSP parsing loop.
bool IsDone = false;

Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/clangd/Protocol.h
Expand Up @@ -173,6 +173,7 @@ struct NoParams {
}
};
using ShutdownParams = NoParams;
using ExitParams = NoParams;

struct InitializeParams {
/// The process Id of the parent process that started
Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/clangd/ProtocolHandlers.cpp
Expand Up @@ -52,6 +52,7 @@ void clangd::registerCallbackHandlers(JSONRPCDispatcher &Dispatcher,

Register("initialize", &ProtocolCallbacks::onInitialize);
Register("shutdown", &ProtocolCallbacks::onShutdown);
Register("exit", &ProtocolCallbacks::onExit);
Register("textDocument/didOpen", &ProtocolCallbacks::onDocumentDidOpen);
Register("textDocument/didClose", &ProtocolCallbacks::onDocumentDidClose);
Register("textDocument/didChange", &ProtocolCallbacks::onDocumentDidChange);
Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/clangd/ProtocolHandlers.h
Expand Up @@ -34,6 +34,7 @@ class ProtocolCallbacks {

virtual void onInitialize(Ctx C, InitializeParams &Params) = 0;
virtual void onShutdown(Ctx C, ShutdownParams &Params) = 0;
virtual void onExit(Ctx C, ExitParams &Params) = 0;
virtual void onDocumentDidOpen(Ctx C, DidOpenTextDocumentParams &Params) = 0;
virtual void onDocumentDidChange(Ctx C,
DidChangeTextDocumentParams &Params) = 0;
Expand Down
4 changes: 3 additions & 1 deletion clang-tools-extra/clangd/tool/ClangdMain.cpp
Expand Up @@ -114,5 +114,7 @@ int main(int argc, char *argv[]) {
/// Initialize and run ClangdLSPServer.
ClangdLSPServer LSPServer(Out, WorkerThreadsCount, EnableSnippets,
ResourceDirRef, CompileCommandsDirPath);
LSPServer.run(std::cin);

constexpr int NoShutdownRequestErrorCode = 1;
return LSPServer.run(std::cin) ? 0 : NoShutdownRequestErrorCode;
}
4 changes: 4 additions & 0 deletions clang-tools-extra/test/clangd/authority-less-uri.test
Expand Up @@ -30,3 +30,7 @@ Content-Length: 172
Content-Length: 44

{"jsonrpc":"2.0","id":3,"method":"shutdown"}
# CHECK: {"jsonrpc":"2.0","id":3,"result":null}
Content-Length: 33

{"jsonrpc":"2.0":"method":"exit"}
4 changes: 4 additions & 0 deletions clang-tools-extra/test/clangd/completion-priorities.test
Expand Up @@ -34,3 +34,7 @@ Content-Length: 151
Content-Length: 58

{"jsonrpc":"2.0","id":4,"method":"shutdown","params":null}
# CHECK: {"jsonrpc":"2.0","id":4,"result":null}
Content-Length: 33

{"jsonrpc":"2.0":"method":"exit"}
4 changes: 4 additions & 0 deletions clang-tools-extra/test/clangd/completion-qualifiers.test
Expand Up @@ -16,3 +16,7 @@ Content-Length: 151
Content-Length: 44

{"jsonrpc":"2.0","id":4,"method":"shutdown"}
# CHECK: {"jsonrpc":"2.0","id":4,"result":null}
Content-Length: 33

{"jsonrpc":"2.0":"method":"exit"}
4 changes: 4 additions & 0 deletions clang-tools-extra/test/clangd/completion-snippet.test
Expand Up @@ -52,3 +52,7 @@ Content-Length: 148
Content-Length: 44

{"jsonrpc":"2.0","id":4,"method":"shutdown"}
# CHECK: {"jsonrpc":"2.0","id":4,"result":null}
Content-Length: 33

{"jsonrpc":"2.0":"method":"exit"}
4 changes: 4 additions & 0 deletions clang-tools-extra/test/clangd/completion.test
Expand Up @@ -52,3 +52,7 @@ Content-Length: 148
Content-Length: 44

{"jsonrpc":"2.0","id":4,"method":"shutdown"}
# CHECK: {"jsonrpc":"2.0","id":4,"result":null}
Content-Length: 33

{"jsonrpc":"2.0":"method":"exit"}
4 changes: 4 additions & 0 deletions clang-tools-extra/test/clangd/definitions.test
Expand Up @@ -166,3 +166,7 @@ Content-Length: 148
Content-Length: 44

{"jsonrpc":"2.0","id":3,"method":"shutdown"}
# CHECK: {"jsonrpc":"2.0","id":3,"result":null}
Content-Length: 33

{"jsonrpc":"2.0":"method":"exit"}
4 changes: 4 additions & 0 deletions clang-tools-extra/test/clangd/diagnostics-preamble.test
Expand Up @@ -13,3 +13,7 @@ Content-Length: 206
Content-Length: 58

{"jsonrpc":"2.0","id":2,"method":"shutdown","params":null}
# CHECK: {"jsonrpc":"2.0","id":2,"result":null}
Content-Length: 33

{"jsonrpc":"2.0":"method":"exit"}
4 changes: 4 additions & 0 deletions clang-tools-extra/test/clangd/diagnostics.test
Expand Up @@ -15,3 +15,7 @@ Content-Length: 152
Content-Length: 44

{"jsonrpc":"2.0","id":5,"method":"shutdown"}
# CHECK: {"jsonrpc":"2.0","id":5,"result":null}
Content-Length: 33

{"jsonrpc":"2.0":"method":"exit"}
3 changes: 3 additions & 0 deletions clang-tools-extra/test/clangd/did-change-watch-files.test
Expand Up @@ -59,3 +59,6 @@ Content-Length: 86
Content-Length: 44

{"jsonrpc":"2.0","id":3,"method":"shutdown"}
Content-Length: 33

{"jsonrpc":"2.0":"method":"exit"}
4 changes: 4 additions & 0 deletions clang-tools-extra/test/clangd/extra-flags.test
Expand Up @@ -18,5 +18,9 @@ Content-Length: 175
Content-Length: 44

{"jsonrpc":"2.0","id":5,"method":"shutdown"}
# CHECK: {"jsonrpc":"2.0","id":5,"result":null}
Content-Length: 33

{"jsonrpc":"2.0":"method":"exit"}


4 changes: 4 additions & 0 deletions clang-tools-extra/test/clangd/fixits.test
Expand Up @@ -26,3 +26,7 @@ Content-Length: 771
Content-Length: 44

{"jsonrpc":"2.0","id":3,"method":"shutdown"}
# CHECK: {"jsonrpc":"2.0","id":3,"result":null}
Content-Length: 33

{"jsonrpc":"2.0":"method":"exit"}
4 changes: 4 additions & 0 deletions clang-tools-extra/test/clangd/formatting.test
Expand Up @@ -65,3 +65,7 @@ Content-Length: 204
Content-Length: 44

{"jsonrpc":"2.0","id":6,"method":"shutdown"}
# CHECK: {"jsonrpc":"2.0","id":6,"result":null}
Content-Length: 33

{"jsonrpc":"2.0":"method":"exit"}
4 changes: 4 additions & 0 deletions clang-tools-extra/test/clangd/initialize-params-invalid.test
Expand Up @@ -20,3 +20,7 @@ Content-Length: 142
Content-Length: 44

{"jsonrpc":"2.0","id":3,"method":"shutdown"}
# CHECK: {"jsonrpc":"2.0","id":3,"result":null}
Content-Length: 33

{"jsonrpc":"2.0":"method":"exit"}
4 changes: 4 additions & 0 deletions clang-tools-extra/test/clangd/initialize-params.test
Expand Up @@ -20,3 +20,7 @@ Content-Length: 143
Content-Length: 44

{"jsonrpc":"2.0","id":3,"method":"shutdown"}
# CHECK: {"jsonrpc":"2.0","id":3,"result":null}
Content-Length: 33

{"jsonrpc":"2.0":"method":"exit"}
4 changes: 4 additions & 0 deletions clang-tools-extra/test/clangd/input-mirror.test
Expand Up @@ -152,3 +152,7 @@ Content-Length: 148
Content-Length: 44

{"jsonrpc":"2.0","id":3,"method":"shutdown"}
# CHECK: {"jsonrpc":"2.0","id":3,"result":null}
Content-Length: 33

{"jsonrpc":"2.0":"method":"exit"}
6 changes: 4 additions & 2 deletions clang-tools-extra/test/clangd/protocol.test
@@ -1,8 +1,10 @@
# RUN: clangd -run-synchronously < %s | FileCheck %s
# RUN: clangd -run-synchronously < %s 2>&1 | FileCheck -check-prefix=STDERR %s
# RUN: not clangd -run-synchronously < %s | FileCheck %s
# RUN: not clangd -run-synchronously < %s 2>&1 | FileCheck -check-prefix=STDERR %s
# vim: fileformat=dos
# It is absolutely vital that this file has CRLF line endings.
#
# Note that we invert the test because we intent to let clangd exit prematurely.
#
# Test protocol parsing
Content-Length: 125
Content-Type: application/vscode-jsonrpc; charset-utf-8
Expand Down
9 changes: 9 additions & 0 deletions clang-tools-extra/test/clangd/shutdown-with-exit.test
@@ -0,0 +1,9 @@
# RUN: clangd -run-synchronously < %s
# vim: fileformat=dos
# It is absolutely vital that this file has CRLF line endings.
Content-Length: 44

{"jsonrpc":"2.0","id":3,"method":"shutdown"}
Content-Length: 33

{"jsonrpc":"2.0":"method":"exit"}
6 changes: 6 additions & 0 deletions clang-tools-extra/test/clangd/shutdown-without-exit.test
@@ -0,0 +1,6 @@
# RUN: not clangd -run-synchronously < %s
# vim: fileformat=dos
# It is absolutely vital that this file has CRLF line endings.
Content-Length: 33

{"jsonrpc":"2.0":"method":"exit"}
4 changes: 4 additions & 0 deletions clang-tools-extra/test/clangd/signature-help.test
Expand Up @@ -40,3 +40,7 @@ Content-Length: 151
Content-Length: 49

{"jsonrpc":"2.0","id":100000,"method":"shutdown"}
# CHECK: {"jsonrpc":"2.0","id":100000,"result":null}
Content-Length: 33

{"jsonrpc":"2.0":"method":"exit"}
4 changes: 4 additions & 0 deletions clang-tools-extra/test/clangd/unsupported-method.test
Expand Up @@ -17,3 +17,7 @@ Content-Length: 92
Content-Length: 44

{"jsonrpc":"2.0","id":2,"method":"shutdown"}
# CHECK: {"jsonrpc":"2.0","id":2,"result":null}
Content-Length: 33

{"jsonrpc":"2.0":"method":"exit"}

0 comments on commit 0d9b8a3

Please sign in to comment.