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