1 //===--- Cancellation.h -------------------------------------------*-C++-*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 // Cancellation mechanism for long-running tasks.
9 //
10 // This manages interactions between:
11 //
12 // 1. Client code that starts some long-running work, and maybe cancels later.
13 //
14 //   std::pair<Context, Canceler> Task = cancelableTask();
15 //   {
16 //     WithContext Cancelable(std::move(Task.first));
17 //     Expected
18 //     deepThoughtAsync([](int answer){ errs() << answer; });
19 //   }
20 //   // ...some time later...
21 //   if (User.fellAsleep())
22 //     Task.second();
23 //
24 //  (This example has an asynchronous computation, but synchronous examples
25 //  work similarly - the Canceler should be invoked from another thread).
26 //
27 // 2. Library code that executes long-running work, and can exit early if the
28 //   result is not needed.
29 //
30 //   void deepThoughtAsync(std::function<void(int)> Callback) {
31 //     runAsync([Callback]{
32 //       int A = ponder(6);
33 //       if (isCancelled())
34 //         return;
35 //       int B = ponder(9);
36 //       if (isCancelled())
37 //         return;
38 //       Callback(A * B);
39 //     });
40 //   }
41 //
42 //   (A real example may invoke the callback with an error on cancellation,
43 //   the CancelledError is provided for this purpose).
44 //
45 // Cancellation has some caveats:
46 //   - the work will only stop when/if the library code next checks for it.
47 //     Code outside clangd such as Sema will not do this.
48 //   - it's inherently racy: client code must be prepared to accept results
49 //     even after requesting cancellation.
50 //   - it's Context-based, so async work must be dispatched to threads in
51 //     ways that preserve the context. (Like runAsync() or TUScheduler).
52 //
53 // FIXME: We could add timestamps to isCancelled() and CancelledError.
54 //        Measuring the start -> cancel -> acknowledge -> finish timeline would
55 //        help find where libraries' cancellation should be improved.
56 
57 #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_SUPPORT_CANCELLATION_H
58 #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_SUPPORT_CANCELLATION_H
59 
60 #include "support/Context.h"
61 #include "llvm/Support/Error.h"
62 #include <functional>
63 #include <system_error>
64 
65 namespace clang {
66 namespace clangd {
67 
68 /// A canceller requests cancellation of a task, when called.
69 /// Calling it again has no effect.
70 using Canceler = std::function<void()>;
71 
72 /// Defines a new task whose cancellation may be requested.
73 /// The returned Context defines the scope of the task.
74 /// When the context is active, isCancelled() is 0 until the Canceler is
75 /// invoked, and equal to Reason afterwards.
76 /// Conventionally, Reason may be the LSP error code to return.
77 std::pair<Context, Canceler> cancelableTask(int Reason = 1);
78 
79 /// If the current context is within a cancelled task, returns the reason.
80 /// (If the context is within multiple nested tasks, true if any are cancelled).
81 /// Always zero if there is no active cancelable task.
82 /// This isn't free (context lookup) - don't call it in a tight loop.
83 int isCancelled(const Context &Ctx = Context::current());
84 
85 /// Conventional error when no result is returned due to cancellation.
86 class CancelledError : public llvm::ErrorInfo<CancelledError> {
87 public:
88   static char ID;
89   const int Reason;
90 
CancelledError(int Reason)91   CancelledError(int Reason) : Reason(Reason) {}
92 
log(llvm::raw_ostream & OS)93   void log(llvm::raw_ostream &OS) const override {
94     OS << "Task was cancelled.";
95   }
convertToErrorCode()96   std::error_code convertToErrorCode() const override {
97     return std::make_error_code(std::errc::operation_canceled);
98   }
99 };
100 
101 } // namespace clangd
102 } // namespace clang
103 
104 #endif
105