173
|
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
|
|
91 CancelledError(int Reason) : Reason(Reason) {}
|
|
92
|
|
93 void log(llvm::raw_ostream &OS) const override {
|
|
94 OS << "Task was cancelled.";
|
|
95 }
|
|
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
|