[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

git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@340607 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/clangd/Cancellation.h b/clangd/Cancellation.h
new file mode 100644
index 0000000..d7868e7
--- /dev/null
+++ b/clangd/Cancellation.h
@@ -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