[PATCH] D65387: [clangd] Add a callback mechanism for handling responses from client.
Sam McCall via Phabricator via cfe-commits
cfe-commits at lists.llvm.org
Fri Aug 2 14:29:25 PDT 2019
sammccall added a comment.
Thanks, this looks a lot better. There are a bunch of details that can be simplified and names that could be clearer, but I think this is the right shape.
================
Comment at: clang-tools-extra/clangd/ClangdLSPServer.cpp:161
+
+ Callback<llvm::json::Value> FoundCB = nullptr;
+ if (auto IntID = ID.getAsInteger()) {
----------------
nit: maybe call this ReplyHandler?
================
Comment at: clang-tools-extra/clangd/ClangdLSPServer.cpp:165
+ // Find a corresponding callback for the request ID;
+ // We can't use binary search here as ReplyCallbacks is not guaranteed to
+ // be sorted in the multi-thread environment.
----------------
this got me thinking - we're using an `atomic<int>` to assign sequential IDs, but then locking to put them into a map. The reward for using two types of concurrency primitives is our array is almost-but-not-quite sorted.
I think we should demote `NextCallID` to a regular int, and guard it with the same mutex as ReplyCallbacks. (Just call it `CallMutex`?) Then bindReply can lock the mutex, pick a call id, add the callback to the map, and return the ID.
I still don't think we need to binary search though. The list is 100 at most and ~1 in practice.
================
Comment at: clang-tools-extra/clangd/ClangdLSPServer.cpp:167
+ // be sorted in the multi-thread environment.
+ auto Found =
+ llvm::find_if(ReplyCallbacks, [&IntID](const ReplyCallback &RCB) {
----------------
nit: a normal loop with if/break seems clearer than find_if here
================
Comment at: clang-tools-extra/clangd/ClangdLSPServer.cpp:177
+
+ if (FoundCB) {
+ // Found a corresponding callback from the queue.
----------------
Might just be me, but I think I'd find it slightly to avoid the LogAndRun lambda by overwriting the callback:
```
// If there was no callback in the table, the client is sending us an unsolicited reply!
if (FoundCB == nullptr)
Found = [&ID](...) { elog and consume error; }
// inline LogAndRun here
```
================
Comment at: clang-tools-extra/clangd/ClangdLSPServer.cpp:184
+ // No callback being found, run a default log callback.
+ LogAndRun([&ID](decltype(Result) Result) {
+ elog("received a reply with ID {0}, but there was no such call", ID);
----------------
nit: we tend to use `decltype(Result)` to specify the type of `Result` when we're pseudo-capturing it in a lambda using `Bind()`. That's not the case here, so please spell out the type.
================
Comment at: clang-tools-extra/clangd/ClangdLSPServer.cpp:218
+ // take out and run the oldest pending callback if we overflow.
+ if (ReplyCallbacks.size() > MaxCallbacks) {
+ OldestCB = std::move(ReplyCallbacks.front());
----------------
elog("more than {0} outstanding LSP calls, forgetting about {1}")
================
Comment at: clang-tools-extra/clangd/ClangdLSPServer.cpp:224
+ if (OldestCB)
+ OldestCB->CB(
+ llvm::formatv("failed to receive a client reply for request ({0})",
----------------
you're returning a string to the callback, I think you meant to return an error?
================
Comment at: clang-tools-extra/clangd/ClangdLSPServer.cpp:278
~ReplyOnce() {
- if (Server && !Replied) {
+ if (Server && !Server->isDestructing() && !Replied) {
elog("No reply to message {0}({1})", Method, ID);
----------------
Need a comment about the server being destroyed/unreplied calls case.
================
Comment at: clang-tools-extra/clangd/ClangdLSPServer.cpp:315
+ //
+ // We bound the maximum size to the pending queue to prevent memory leakage
+ // for cases where LSP clients don't reply for the request.
----------------
nit: physically this is a deque, but it functions more like a map and I think it might be easier to refer to that way.
================
Comment at: clang-tools-extra/clangd/ClangdLSPServer.cpp:318
+ //
+ // If the queue overflows, we assume that the client didn't reply the
+ // oldest request, and run the corresponding callback which replies an Error
----------------
nit: I think this part of the comment belongs in the implementation rather than on the variable
================
Comment at: clang-tools-extra/clangd/ClangdLSPServer.cpp:325
+ // a corresponding request.
+ struct ReplyCallback {
+ int RequestID;
----------------
hmm, maybe just use `pair<int, Callback<Value>>` here? That way it looks even more like a map
================
Comment at: clang-tools-extra/clangd/ClangdLSPServer.cpp:580
};
+ auto ReplyApplyEdit =
+ [](decltype(Reply) Reply,
----------------
I can't parse this name.
Looking at the code... it's doing something like `ReplyAfterApplyingEdits`
It should take the success message as a parameter, I think - at the moment you're replying "fix applied" even in the tweak case
================
Comment at: clang-tools-extra/clangd/ClangdLSPServer.cpp:580
};
+ auto ReplyApplyEdit =
+ [](decltype(Reply) Reply,
----------------
sammccall wrote:
> I can't parse this name.
>
> Looking at the code... it's doing something like `ReplyAfterApplyingEdits`
>
> It should take the success message as a parameter, I think - at the moment you're replying "fix applied" even in the tweak case
So this method is already a tangled mess, and this patch makes it worse. However, it's *conceptually* exactly the right API, just callbacks and Expected and LSP conspire to be awkward here.
We should refactor this, but this patch isn't the right time. Can you add a `//FIXME: this method is tangled and confusing, refactor it` or so?
================
Comment at: clang-tools-extra/clangd/ClangdLSPServer.cpp:591
+ llvm::inconvertibleErrorCode(),
+ ("edits fail to applied: " + Reason).c_str()));
+ }
----------------
nit: edits were not applied (or edits failed to apply)
================
Comment at: clang-tools-extra/clangd/ClangdLSPServer.h:54
+ bool isDestructing() const { return Destructing; }
+
----------------
MessageHandler is a nested class and so has access to Destructing already, no need for this function
================
Comment at: clang-tools-extra/clangd/ClangdLSPServer.h:138
+ std::atomic<bool> Destructing = {false};
+
----------------
nit: Destroying
but I think `IsBeingDestroyed` would be clearer, as destroy is transitive
this needs a comment
================
Comment at: clang-tools-extra/clangd/ClangdLSPServer.h:159
+ // Wrap the callback with LSP conversion and error-handling.
+ auto Reply = [](decltype(CB) CB,
+ llvm::Expected<llvm::json::Value> RawResponse) {
----------------
nit: HandleReply?
lambdas often have names like functions, so this reads like it's a function that replies
================
Comment at: clang-tools-extra/clangd/ClangdLSPServer.h:174
+ }
+ void callImpl(StringRef Method, llvm::json::Value Params,
+ Callback<llvm::json::Value> CB);
----------------
nit: I know I suggested this name but I think `callRaw` would be better
================
Comment at: clang-tools-extra/clangd/Protocol.h:877
+struct ApplyWorkspaceEditResponse {
+ bool applied;
+ llvm::Optional<std::string> failureReason;
----------------
needs a default value
`= true`, I guess
================
Comment at: clang-tools-extra/clangd/test/request-reply.test:5
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///main.cpp","languageId":"cpp","version":1,"text":"auto i = 0;"}}}
+---
+---
----------------
nit, extra line
================
Comment at: clang-tools-extra/clangd/test/request-reply.test:22
+---
+{"jsonrpc":"2.0","id":0,"result":{"applied":false}}
+# CHECK: "code": -32001,
----------------
please use increasing IDs and don't reuse them. (In general, but particularly in this test)
================
Comment at: clang-tools-extra/clangd/test/request-reply.test:23
+{"jsonrpc":"2.0","id":0,"result":{"applied":false}}
+# CHECK: "code": -32001,
+# CHECK-NEXT: "message": "edits fail to applied:
----------------
can you verify that the error comes with the right ID? (and similar below)
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D65387/new/
https://reviews.llvm.org/D65387
More information about the cfe-commits
mailing list