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
65namespace clang {
66namespace clangd {
67
68/// A canceller requests cancellation of a task, when called.
69/// Calling it again has no effect.
70using 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.
77std::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.
83int isCancelled(const Context &Ctx = Context::current());
84
85/// Conventional error when no result is returned due to cancellation.
86class CancelledError : public llvm::ErrorInfo<CancelledError> {
87public:
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(e: std::errc::operation_canceled);
98 }
99};
100
101} // namespace clangd
102} // namespace clang
103
104#endif
105

source code of clang-tools-extra/clangd/support/Cancellation.h