[clang-tools-extra] r340607 - [clangd] Initial cancellation mechanism for LSP requests.

Kadir Cetinkaya via cfe-commits cfe-commits at lists.llvm.org
Fri Aug 24 06:09:41 PDT 2018


Author: kadircet
Date: Fri Aug 24 06:09:41 2018
New Revision: 340607

URL: http://llvm.org/viewvc/llvm-project?rev=340607&view=rev
Log:
[clangd] Initial cancellation mechanism for LSP requests.

Reviewers: ilya-biryukov, ioeric, hokein

Reviewed By: ilya-biryukov

Subscribers: mgorny, ioeric, MaskRay, jkorous, arphaman, jfb, cfe-commits

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

Added:
    clang-tools-extra/trunk/clangd/Cancellation.cpp
    clang-tools-extra/trunk/clangd/Cancellation.h
    clang-tools-extra/trunk/unittests/clangd/CancellationTests.cpp
Modified:
    clang-tools-extra/trunk/clangd/CMakeLists.txt
    clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp
    clang-tools-extra/trunk/clangd/ClangdLSPServer.h
    clang-tools-extra/trunk/clangd/ClangdServer.cpp
    clang-tools-extra/trunk/clangd/ClangdServer.h
    clang-tools-extra/trunk/clangd/JSONRPCDispatcher.cpp
    clang-tools-extra/trunk/clangd/JSONRPCDispatcher.h
    clang-tools-extra/trunk/clangd/Protocol.cpp
    clang-tools-extra/trunk/clangd/Protocol.h
    clang-tools-extra/trunk/clangd/ProtocolHandlers.cpp
    clang-tools-extra/trunk/clangd/ProtocolHandlers.h
    clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt

Modified: clang-tools-extra/trunk/clangd/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/CMakeLists.txt?rev=340607&r1=340606&r2=340607&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/CMakeLists.txt (original)
+++ clang-tools-extra/trunk/clangd/CMakeLists.txt Fri Aug 24 06:09:41 2018
@@ -9,6 +9,7 @@ endif()
 
 add_clang_library(clangDaemon
   AST.cpp
+  Cancellation.cpp
   ClangdLSPServer.cpp
   ClangdServer.cpp
   ClangdUnit.cpp

Added: clang-tools-extra/trunk/clangd/Cancellation.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Cancellation.cpp?rev=340607&view=auto
==============================================================================
--- clang-tools-extra/trunk/clangd/Cancellation.cpp (added)
+++ clang-tools-extra/trunk/clangd/Cancellation.cpp Fri Aug 24 06:09:41 2018
@@ -0,0 +1,34 @@
+//===--- Cancellation.cpp -----------------------------------------*-C++-*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Cancellation.h"
+#include <atomic>
+
+namespace clang {
+namespace clangd {
+
+namespace {
+static Key<ConstTaskHandle> TaskKey;
+} // namespace
+
+char CancelledError::ID = 0;
+
+const Task &getCurrentTask() {
+  const auto TH = Context::current().getExisting(TaskKey);
+  assert(TH && "Fetched a nullptr for TaskHandle from context.");
+  return *TH;
+}
+
+Context setCurrentTask(ConstTaskHandle TH) {
+  assert(TH && "Trying to stash a nullptr as TaskHandle into context.");
+  return Context::current().derive(TaskKey, std::move(TH));
+}
+
+} // namespace clangd
+} // namespace clang

Added: clang-tools-extra/trunk/clangd/Cancellation.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Cancellation.h?rev=340607&view=auto
==============================================================================
--- clang-tools-extra/trunk/clangd/Cancellation.h (added)
+++ clang-tools-extra/trunk/clangd/Cancellation.h Fri Aug 24 06:09:41 2018
@@ -0,0 +1,142 @@
+//===--- Cancellation.h -------------------------------------------*-C++-*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// Cancellation mechanism for async tasks. Roughly all the clients of this code
+// can be classified into three categories:
+// 1. The code that creates and schedules async tasks, e.g. TUScheduler.
+// 2. The callers of the async method that can cancel some of the running tasks,
+// e.g. `ClangdLSPServer`
+// 3. The code running inside the async task itself, i.e. code completion or
+// find definition implementation that run clang, etc.
+//
+// For (1), the guideline is to accept a callback for the result of async
+// operation and return a `TaskHandle` to allow cancelling the request.
+//
+//  TaskHandle someAsyncMethod(Runnable T,
+//  function<void(llvm::Expected<ResultType>)> Callback) {
+//   auto TH = Task::createHandle();
+//   WithContext ContextWithCancellationToken(TH);
+//   auto run = [](){
+//     Callback(T());
+//   }
+//   // Start run() in a new async thread, and make sure to propagate Context.
+//   return TH;
+// }
+//
+// The callers of async methods (2) can issue cancellations and should be
+// prepared to handle `TaskCancelledError` result:
+//
+// void Caller() {
+//   // You should store this handle if you wanna cancel the task later on.
+//   TaskHandle TH = someAsyncMethod(Task, [](llvm::Expected<ResultType> R) {
+//     if(/*check for task cancellation error*/)
+//       // Handle the error
+//     // Do other things on R.
+//   });
+//   // To cancel the task:
+//   sleep(5);
+//   TH->cancel();
+// }
+//
+// The worker code itself (3) should check for cancellations using
+// `Task::isCancelled` that can be retrieved via `getCurrentTask()`.
+//
+// llvm::Expected<ResultType> AsyncTask() {
+//    // You can either store the read only TaskHandle by calling getCurrentTask
+//    // once and just use the variable everytime you want to check for
+//    // cancellation, or call isCancelled everytime. The former is more
+//    // efficient if you are going to have multiple checks.
+//    const auto T = getCurrentTask();
+//    // DO SMTHNG...
+//    if(T.isCancelled()) {
+//      // Task has been cancelled, lets get out.
+//      return llvm::makeError<CancelledError>();
+//    }
+//    // DO SOME MORE THING...
+//    if(T.isCancelled()) {
+//      // Task has been cancelled, lets get out.
+//      return llvm::makeError<CancelledError>();
+//    }
+//    return ResultType(...);
+// }
+// If the operation was cancelled before task could run to completion, it should
+// propagate the TaskCancelledError as a result.
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CANCELLATION_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CANCELLATION_H
+
+#include "Context.h"
+#include "llvm/Support/Error.h"
+#include <atomic>
+#include <memory>
+#include <system_error>
+
+namespace clang {
+namespace clangd {
+
+/// Enables signalling a cancellation on an async task or checking for
+/// cancellation. It is thread-safe to trigger cancellation from multiple
+/// threads or check for cancellation. Task object for the currently running
+/// task can be obtained via clangd::getCurrentTask().
+class Task {
+public:
+  void cancel() { CT = true; }
+  /// If cancellation checks are rare, one could use the isCancelled() helper in
+  /// the namespace to simplify the code. However, if cancellation checks are
+  /// frequent, the guideline is first obtain the Task object for the currently
+  /// running task with getCurrentTask() and do cancel checks using it to avoid
+  /// extra lookups in the Context.
+  bool isCancelled() const { return CT; }
+
+  /// Creates a task handle that can be used by an asyn task to check for
+  /// information that can change during it's runtime, like Cancellation.
+  static std::shared_ptr<Task> createHandle() {
+    return std::shared_ptr<Task>(new Task());
+  }
+
+  Task(const Task &) = delete;
+  Task &operator=(const Task &) = delete;
+  Task(Task &&) = delete;
+  Task &operator=(Task &&) = delete;
+
+private:
+  Task() : CT(false) {}
+  std::atomic<bool> CT;
+};
+using ConstTaskHandle = std::shared_ptr<const Task>;
+using TaskHandle = std::shared_ptr<Task>;
+
+/// Fetches current task information from Context. TaskHandle must have been
+/// stashed into context beforehand.
+const Task &getCurrentTask();
+
+/// Stashes current task information within the context.
+LLVM_NODISCARD Context setCurrentTask(ConstTaskHandle TH);
+
+/// Checks whether the current task has been cancelled or not.
+/// Consider storing the task handler returned by getCurrentTask and then
+/// calling isCancelled through it. getCurrentTask is expensive since it does a
+/// lookup in the context.
+inline bool isCancelled() { return getCurrentTask().isCancelled(); }
+
+class CancelledError : public llvm::ErrorInfo<CancelledError> {
+public:
+  static char ID;
+
+  void log(llvm::raw_ostream &OS) const override {
+    OS << "Task was cancelled.";
+  }
+  std::error_code convertToErrorCode() const override {
+    return std::make_error_code(std::errc::operation_canceled);
+  }
+};
+
+} // namespace clangd
+} // namespace clang
+
+#endif

Modified: clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp?rev=340607&r1=340606&r2=340607&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp (original)
+++ clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp Fri Aug 24 06:09:41 2018
@@ -8,10 +8,12 @@
 //===----------------------------------------------------------------------===//
 
 #include "ClangdLSPServer.h"
+#include "Cancellation.h"
 #include "Diagnostics.h"
 #include "JSONRPCDispatcher.h"
 #include "SourceCode.h"
 #include "URI.h"
+#include "llvm/ADT/ScopeExit.h"
 #include "llvm/Support/Errc.h"
 #include "llvm/Support/FormatVariadic.h"
 #include "llvm/Support/Path.h"
@@ -69,6 +71,11 @@ SymbolKindBitset defaultSymbolKinds() {
   return Defaults;
 }
 
+std::string NormalizeRequestID(const json::Value &ID) {
+  auto NormalizedID = parseNumberOrString(&ID);
+  assert(NormalizedID && "Was not able to parse request id.");
+  return std::move(*NormalizedID);
+}
 } // namespace
 
 void ClangdLSPServer::onInitialize(InitializeParams &Params) {
@@ -339,17 +346,21 @@ void ClangdLSPServer::onCodeAction(CodeA
 }
 
 void ClangdLSPServer::onCompletion(TextDocumentPositionParams &Params) {
-  Server.codeComplete(Params.textDocument.uri.file(), Params.position, CCOpts,
-                      [this](llvm::Expected<CodeCompleteResult> List) {
-                        if (!List)
-                          return replyError(ErrorCode::InvalidParams,
-                                            llvm::toString(List.takeError()));
-                        CompletionList LSPList;
-                        LSPList.isIncomplete = List->HasMore;
-                        for (const auto &R : List->Completions)
-                          LSPList.items.push_back(R.render(CCOpts));
-                        reply(std::move(LSPList));
-                      });
+  CreateSpaceForTaskHandle();
+  TaskHandle TH = Server.codeComplete(
+      Params.textDocument.uri.file(), Params.position, CCOpts,
+      [this](llvm::Expected<CodeCompleteResult> List) {
+        auto _ = llvm::make_scope_exit([this]() { CleanupTaskHandle(); });
+
+        if (!List)
+          return replyError(List.takeError());
+        CompletionList LSPList;
+        LSPList.isIncomplete = List->HasMore;
+        for (const auto &R : List->Completions)
+          LSPList.items.push_back(R.render(CCOpts));
+        return reply(std::move(LSPList));
+      });
+  StoreTaskHandle(std::move(TH));
 }
 
 void ClangdLSPServer::onSignatureHelp(TextDocumentPositionParams &Params) {
@@ -364,14 +375,14 @@ void ClangdLSPServer::onSignatureHelp(Te
 }
 
 void ClangdLSPServer::onGoToDefinition(TextDocumentPositionParams &Params) {
-  Server.findDefinitions(
-      Params.textDocument.uri.file(), Params.position,
-      [](llvm::Expected<std::vector<Location>> Items) {
-        if (!Items)
-          return replyError(ErrorCode::InvalidParams,
-                            llvm::toString(Items.takeError()));
-        reply(json::Array(*Items));
-      });
+  Server.findDefinitions(Params.textDocument.uri.file(), Params.position,
+                         [](llvm::Expected<std::vector<Location>> Items) {
+                           if (!Items)
+                             return replyError(
+                                 ErrorCode::InvalidParams,
+                                 llvm::toString(Items.takeError()));
+                           reply(json::Array(*Items));
+                         });
 }
 
 void ClangdLSPServer::onSwitchSourceHeader(TextDocumentIdentifier &Params) {
@@ -606,3 +617,49 @@ GlobalCompilationDatabase &ClangdLSPServ
     return *CachingCDB;
   return *CDB;
 }
+
+void ClangdLSPServer::onCancelRequest(CancelParams &Params) {
+  std::lock_guard<std::mutex> Lock(TaskHandlesMutex);
+  const auto &It = TaskHandles.find(Params.ID);
+  if (It == TaskHandles.end())
+    return;
+  if (It->second)
+    It->second->cancel();
+  TaskHandles.erase(It);
+}
+
+void ClangdLSPServer::CleanupTaskHandle() {
+  const json::Value *ID = getRequestId();
+  if (!ID)
+    return;
+  std::string NormalizedID = NormalizeRequestID(*ID);
+  std::lock_guard<std::mutex> Lock(TaskHandlesMutex);
+  TaskHandles.erase(NormalizedID);
+}
+
+void ClangdLSPServer::CreateSpaceForTaskHandle() {
+  const json::Value *ID = getRequestId();
+  if (!ID)
+    return;
+  std::string NormalizedID = NormalizeRequestID(*ID);
+  std::lock_guard<std::mutex> Lock(TaskHandlesMutex);
+  if (!TaskHandles.insert({NormalizedID, nullptr}).second)
+    elog("Creation of space for task handle: {0} failed.", NormalizedID);
+}
+
+void ClangdLSPServer::StoreTaskHandle(TaskHandle TH) {
+  const json::Value *ID = getRequestId();
+  if (!ID)
+    return;
+  std::string NormalizedID = NormalizeRequestID(*ID);
+  std::lock_guard<std::mutex> Lock(TaskHandlesMutex);
+  auto It = TaskHandles.find(NormalizedID);
+  if (It == TaskHandles.end()) {
+    elog("CleanupTaskHandle called before store can happen for request:{0}.",
+         NormalizedID);
+    return;
+  }
+  if (It->second != nullptr)
+    elog("TaskHandle didn't get cleared for: {0}.", NormalizedID);
+  It->second = std::move(TH);
+}

Modified: clang-tools-extra/trunk/clangd/ClangdLSPServer.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdLSPServer.h?rev=340607&r1=340606&r2=340607&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ClangdLSPServer.h (original)
+++ clang-tools-extra/trunk/clangd/ClangdLSPServer.h Fri Aug 24 06:09:41 2018
@@ -75,6 +75,7 @@ private:
   void onRename(RenameParams &Parames) override;
   void onHover(TextDocumentPositionParams &Params) override;
   void onChangeConfiguration(DidChangeConfigurationParams &Params) override;
+  void onCancelRequest(CancelParams &Params) override;
 
   std::vector<Fix> getFixes(StringRef File, const clangd::Diagnostic &D);
 
@@ -167,8 +168,22 @@ private:
   // the worker thread that may otherwise run an async callback on partially
   // destructed instance of ClangdLSPServer.
   ClangdServer Server;
-};
 
+  // Holds task handles for running requets. Key of the map is a serialized
+  // request id.
+  llvm::StringMap<TaskHandle> TaskHandles;
+  std::mutex TaskHandlesMutex;
+
+  // Following three functions are for managing TaskHandles map. They store or
+  // remove a task handle for the request-id stored in current Context.
+  // FIXME(kadircet): Wrap the following three functions in a RAII object to
+  // make sure these do not get misused. The object might be stored in the
+  // Context of the thread or moved around until a reply is generated for the
+  // request.
+  void CleanupTaskHandle();
+  void CreateSpaceForTaskHandle();
+  void StoreTaskHandle(TaskHandle TH);
+};
 } // namespace clangd
 } // namespace clang
 

Modified: clang-tools-extra/trunk/clangd/ClangdServer.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdServer.cpp?rev=340607&r1=340606&r2=340607&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ClangdServer.cpp (original)
+++ clang-tools-extra/trunk/clangd/ClangdServer.cpp Fri Aug 24 06:09:41 2018
@@ -8,6 +8,7 @@
 //===-------------------------------------------------------------------===//
 
 #include "ClangdServer.h"
+#include "Cancellation.h"
 #include "CodeComplete.h"
 #include "FindSymbols.h"
 #include "Headers.h"
@@ -173,14 +174,16 @@ void ClangdServer::removeDocument(PathRe
   WorkScheduler.remove(File);
 }
 
-void ClangdServer::codeComplete(PathRef File, Position Pos,
-                                const clangd::CodeCompleteOptions &Opts,
-                                Callback<CodeCompleteResult> CB) {
+TaskHandle ClangdServer::codeComplete(PathRef File, Position Pos,
+                                      const clangd::CodeCompleteOptions &Opts,
+                                      Callback<CodeCompleteResult> CB) {
   // Copy completion options for passing them to async task handler.
   auto CodeCompleteOpts = Opts;
   if (!CodeCompleteOpts.Index) // Respect overridden index.
     CodeCompleteOpts.Index = Index;
 
+  TaskHandle TH = Task::createHandle();
+  WithContext ContextWithCancellation(setCurrentTask(TH));
   // Copy PCHs to avoid accessing this->PCHs concurrently
   std::shared_ptr<PCHContainerOperations> PCHs = this->PCHs;
   auto FS = FSProvider.getFileSystem();
@@ -188,6 +191,9 @@ void ClangdServer::codeComplete(PathRef
   auto Task = [PCHs, Pos, FS, CodeCompleteOpts,
                this](Path File, Callback<CodeCompleteResult> CB,
                      llvm::Expected<InputsAndPreamble> IP) {
+    if (isCancelled())
+      return CB(llvm::make_error<CancelledError>());
+
     if (!IP)
       return CB(IP.takeError());
 
@@ -226,6 +232,7 @@ void ClangdServer::codeComplete(PathRef
 
   WorkScheduler.runWithPreamble("CodeComplete", File,
                                 Bind(Task, File.str(), std::move(CB)));
+  return TH;
 }
 
 void ClangdServer::signatureHelp(PathRef File, Position Pos,

Modified: clang-tools-extra/trunk/clangd/ClangdServer.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdServer.h?rev=340607&r1=340606&r2=340607&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ClangdServer.h (original)
+++ clang-tools-extra/trunk/clangd/ClangdServer.h Fri Aug 24 06:09:41 2018
@@ -10,6 +10,7 @@
 #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDSERVER_H
 #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDSERVER_H
 
+#include "Cancellation.h"
 #include "ClangdUnit.h"
 #include "CodeComplete.h"
 #include "FSProvider.h"
@@ -124,9 +125,9 @@ public:
   /// while returned future is not yet ready.
   /// A version of `codeComplete` that runs \p Callback on the processing thread
   /// when codeComplete results become available.
-  void codeComplete(PathRef File, Position Pos,
-                    const clangd::CodeCompleteOptions &Opts,
-                    Callback<CodeCompleteResult> CB);
+  TaskHandle codeComplete(PathRef File, Position Pos,
+                          const clangd::CodeCompleteOptions &Opts,
+                          Callback<CodeCompleteResult> CB);
 
   /// Provide signature help for \p File at \p Pos.  This method should only be
   /// called for tracked files.

Modified: clang-tools-extra/trunk/clangd/JSONRPCDispatcher.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/JSONRPCDispatcher.cpp?rev=340607&r1=340606&r2=340607&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/JSONRPCDispatcher.cpp (original)
+++ clang-tools-extra/trunk/clangd/JSONRPCDispatcher.cpp Fri Aug 24 06:09:41 2018
@@ -8,6 +8,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "JSONRPCDispatcher.h"
+#include "Cancellation.h"
 #include "ProtocolHandlers.h"
 #include "Trace.h"
 #include "llvm/ADT/SmallString.h"
@@ -93,7 +94,7 @@ void JSONOutput::mirrorInput(const Twine
 }
 
 void clangd::reply(json::Value &&Result) {
-  auto ID = Context::current().get(RequestID);
+  auto ID = getRequestId();
   if (!ID) {
     elog("Attempted to reply to a notification!");
     return;
@@ -116,7 +117,7 @@ void clangd::replyError(ErrorCode Code,
                                  {"message", Message.str()}};
   });
 
-  if (auto ID = Context::current().get(RequestID)) {
+  if (auto ID = getRequestId()) {
     log("--> reply({0}) error: {1}", *ID, Message);
     Context::current()
         .getExisting(RequestOut)
@@ -129,6 +130,16 @@ void clangd::replyError(ErrorCode Code,
   }
 }
 
+void clangd::replyError(Error E) {
+  handleAllErrors(std::move(E),
+                  [](const CancelledError &TCE) {
+                    replyError(ErrorCode::RequestCancelled, TCE.message());
+                  },
+                  [](const ErrorInfoBase &EIB) {
+                    replyError(ErrorCode::InvalidParams, EIB.message());
+                  });
+}
+
 void clangd::call(StringRef Method, json::Value &&Params) {
   RequestSpan::attach([&](json::Object &Args) {
     Args["Call"] = json::Object{{"method", Method.str()}, {"params", Params}};
@@ -366,3 +377,7 @@ void clangd::runLanguageServerLoop(std::
     }
   }
 }
+
+const json::Value *clangd::getRequestId() {
+  return Context::current().get(RequestID);
+}

Modified: clang-tools-extra/trunk/clangd/JSONRPCDispatcher.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/JSONRPCDispatcher.h?rev=340607&r1=340606&r2=340607&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/JSONRPCDispatcher.h (original)
+++ clang-tools-extra/trunk/clangd/JSONRPCDispatcher.h Fri Aug 24 06:09:41 2018
@@ -64,6 +64,13 @@ void reply(llvm::json::Value &&Result);
 /// Sends an error response to the client, and logs it.
 /// Current context must derive from JSONRPCDispatcher::Handler.
 void replyError(ErrorCode Code, const llvm::StringRef &Message);
+/// Implements ErrorCode and message extraction from a given llvm::Error. It
+/// fetches the related message from error's message method. If error doesn't
+/// match any known errors, uses ErrorCode::InvalidParams for the error.
+void replyError(llvm::Error E);
+/// Returns the request-id of the current request. Should not be used directly
+/// for replying to requests, use the above mentioned methods for that case.
+const llvm::json::Value *getRequestId();
 /// Sends a request to the client.
 /// Current context must derive from JSONRPCDispatcher::Handler.
 void call(llvm::StringRef Method, llvm::json::Value &&Params);
@@ -110,7 +117,6 @@ enum JSONStreamStyle {
 void runLanguageServerLoop(std::FILE *In, JSONOutput &Out,
                            JSONStreamStyle InputStyle,
                            JSONRPCDispatcher &Dispatcher, bool &IsDone);
-
 } // namespace clangd
 } // namespace clang
 

Modified: clang-tools-extra/trunk/clangd/Protocol.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Protocol.cpp?rev=340607&r1=340606&r2=340607&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/Protocol.cpp (original)
+++ clang-tools-extra/trunk/clangd/Protocol.cpp Fri Aug 24 06:09:41 2018
@@ -616,5 +616,38 @@ bool fromJSON(const json::Value &Params,
          O.map("compilationDatabaseChanges", CCPC.compilationDatabaseChanges);
 }
 
+json::Value toJSON(const CancelParams &CP) {
+  return json::Object{{"id", CP.ID}};
+}
+
+llvm::raw_ostream &operator<<(llvm::raw_ostream &O, const CancelParams &CP) {
+  O << toJSON(CP);
+  return O;
+}
+
+llvm::Optional<std::string> parseNumberOrString(const json::Value *Params) {
+  if (!Params)
+    return llvm::None;
+  // ID is either a number or a string, check for both.
+  if(const auto AsString = Params->getAsString())
+    return AsString->str();
+
+  if(const auto AsNumber = Params->getAsInteger())
+    return itostr(AsNumber.getValue());
+
+  return llvm::None;
+}
+
+bool fromJSON(const json::Value &Params, CancelParams &CP) {
+  const auto ParamsAsObject = Params.getAsObject();
+  if (!ParamsAsObject)
+    return false;
+  if (auto Parsed = parseNumberOrString(ParamsAsObject->get("id"))) {
+    CP.ID = std::move(*Parsed);
+    return true;
+  }
+  return false;
+}
+
 } // namespace clangd
 } // namespace clang

Modified: clang-tools-extra/trunk/clangd/Protocol.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Protocol.h?rev=340607&r1=340606&r2=340607&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/Protocol.h (original)
+++ clang-tools-extra/trunk/clangd/Protocol.h Fri Aug 24 06:09:41 2018
@@ -871,6 +871,20 @@ struct DocumentHighlight {
 llvm::json::Value toJSON(const DocumentHighlight &DH);
 llvm::raw_ostream &operator<<(llvm::raw_ostream &, const DocumentHighlight &);
 
+struct CancelParams {
+  /// The request id to cancel.
+  /// This can be either a number or string, if it is a number simply print it
+  /// out and always use a string.
+  std::string ID;
+};
+llvm::json::Value toJSON(const CancelParams &);
+llvm::raw_ostream &operator<<(llvm::raw_ostream &, const CancelParams &);
+bool fromJSON(const llvm::json::Value &, CancelParams &);
+
+/// Param can be either of type string or number. Returns the result as a
+/// string.
+llvm::Optional<std::string> parseNumberOrString(const llvm::json::Value *Param);
+
 } // namespace clangd
 } // namespace clang
 

Modified: clang-tools-extra/trunk/clangd/ProtocolHandlers.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ProtocolHandlers.cpp?rev=340607&r1=340606&r2=340607&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ProtocolHandlers.cpp (original)
+++ clang-tools-extra/trunk/clangd/ProtocolHandlers.cpp Fri Aug 24 06:09:41 2018
@@ -75,4 +75,5 @@ void clangd::registerCallbackHandlers(JS
   Register("workspace/didChangeConfiguration",
            &ProtocolCallbacks::onChangeConfiguration);
   Register("workspace/symbol", &ProtocolCallbacks::onWorkspaceSymbol);
+  Register("$/cancelRequest", &ProtocolCallbacks::onCancelRequest);
 }

Modified: clang-tools-extra/trunk/clangd/ProtocolHandlers.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ProtocolHandlers.h?rev=340607&r1=340606&r2=340607&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/ProtocolHandlers.h (original)
+++ clang-tools-extra/trunk/clangd/ProtocolHandlers.h Fri Aug 24 06:09:41 2018
@@ -55,6 +55,7 @@ public:
   virtual void onDocumentHighlight(TextDocumentPositionParams &Params) = 0;
   virtual void onHover(TextDocumentPositionParams &Params) = 0;
   virtual void onChangeConfiguration(DidChangeConfigurationParams &Params) = 0;
+  virtual void onCancelRequest(CancelParams &Params) = 0;
 };
 
 void registerCallbackHandlers(JSONRPCDispatcher &Dispatcher,

Modified: clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt?rev=340607&r1=340606&r2=340607&view=diff
==============================================================================
--- clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt (original)
+++ clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt Fri Aug 24 06:09:41 2018
@@ -10,6 +10,7 @@ include_directories(
 
 add_extra_unittest(ClangdTests
   Annotations.cpp
+  CancellationTests.cpp
   ClangdTests.cpp
   ClangdUnitTests.cpp
   CodeCompleteTests.cpp

Added: clang-tools-extra/trunk/unittests/clangd/CancellationTests.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/CancellationTests.cpp?rev=340607&view=auto
==============================================================================
--- clang-tools-extra/trunk/unittests/clangd/CancellationTests.cpp (added)
+++ clang-tools-extra/trunk/unittests/clangd/CancellationTests.cpp Fri Aug 24 06:09:41 2018
@@ -0,0 +1,74 @@
+#include "Cancellation.h"
+#include "Context.h"
+#include "Threading.h"
+#include "llvm/Support/Error.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include <atomic>
+#include <memory>
+#include <thread>
+
+namespace clang {
+namespace clangd {
+namespace {
+
+TEST(CancellationTest, CancellationTest) {
+  TaskHandle TH = Task::createHandle();
+  WithContext ContextWithCancellation(setCurrentTask(TH));
+  EXPECT_FALSE(isCancelled());
+  TH->cancel();
+  EXPECT_TRUE(isCancelled());
+}
+
+TEST(CancellationTest, TaskTestHandleDiesContextLives) {
+  llvm::Optional<WithContext> ContextWithCancellation;
+  {
+    TaskHandle TH = Task::createHandle();
+    ContextWithCancellation.emplace(setCurrentTask(TH));
+    EXPECT_FALSE(isCancelled());
+    TH->cancel();
+    EXPECT_TRUE(isCancelled());
+  }
+  EXPECT_TRUE(isCancelled());
+}
+
+TEST(CancellationTest, TaskContextDiesHandleLives) {
+  TaskHandle TH = Task::createHandle();
+  {
+    WithContext ContextWithCancellation(setCurrentTask(TH));
+    EXPECT_FALSE(isCancelled());
+    TH->cancel();
+    EXPECT_TRUE(isCancelled());
+  }
+  // Still should be able to cancel without any problems.
+  TH->cancel();
+}
+
+TEST(CancellationTest, CancellationToken) {
+  TaskHandle TH = Task::createHandle();
+  WithContext ContextWithCancellation(setCurrentTask(TH));
+  const auto &CT = getCurrentTask();
+  EXPECT_FALSE(CT.isCancelled());
+  TH->cancel();
+  EXPECT_TRUE(CT.isCancelled());
+}
+
+TEST(CancellationTest, AsynCancellationTest) {
+  std::atomic<bool> HasCancelled(false);
+  Notification Cancelled;
+  auto TaskToBeCancelled = [&](ConstTaskHandle CT) {
+    WithContext ContextGuard(setCurrentTask(std::move(CT)));
+    Cancelled.wait();
+    HasCancelled = isCancelled();
+  };
+  TaskHandle TH = Task::createHandle();
+  std::thread AsyncTask(TaskToBeCancelled, TH);
+  TH->cancel();
+  Cancelled.notify();
+  AsyncTask.join();
+
+  EXPECT_TRUE(HasCancelled);
+}
+} // namespace
+} // namespace clangd
+} // namespace clang




More information about the cfe-commits mailing list