blob: 6872c448de86b830f0acbbad94628966eb52957a [file] [log] [blame]
Kadir Cetinkayab910a102018-08-24 13:09:41 +00001//===--- Cancellation.h -------------------------------------------*-C++-*-===//
2//
Chandler Carruthb1ace232019-01-19 08:50:56 +00003// 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
Kadir Cetinkayab910a102018-08-24 13:09:41 +00006//
7//===----------------------------------------------------------------------===//
Sam McCallf0874bf2018-09-13 11:47:48 +00008// Cancellation mechanism for long-running tasks.
Kadir Cetinkayab910a102018-08-24 13:09:41 +00009//
Sam McCallf0874bf2018-09-13 11:47:48 +000010// This manages interactions between:
Kadir Cetinkayab910a102018-08-24 13:09:41 +000011//
Sam McCallf0874bf2018-09-13 11:47:48 +000012// 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; });
Kadir Cetinkayab910a102018-08-24 13:09:41 +000019// }
Sam McCallf0874bf2018-09-13 11:47:48 +000020// // ...some time later...
21// if (User.fellAsleep())
22// Task.second();
Kadir Cetinkayab910a102018-08-24 13:09:41 +000023//
Sam McCallf0874bf2018-09-13 11:47:48 +000024// (This example has an asynchronous computation, but synchronous examples
25// work similarly - the Canceler should be invoked from another thread).
Kadir Cetinkayab910a102018-08-24 13:09:41 +000026//
Sam McCallf0874bf2018-09-13 11:47:48 +000027// 2. Library code that executes long-running work, and can exit early if the
28// result is not needed.
Kadir Cetinkayab910a102018-08-24 13:09:41 +000029//
Sam McCallf0874bf2018-09-13 11:47:48 +000030// 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// }
Kadir Cetinkayab910a102018-08-24 13:09:41 +000041//
Sam McCallf0874bf2018-09-13 11:47:48 +000042// (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.
Kadir Cetinkayab910a102018-08-24 13:09:41 +000056
57#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CANCELLATION_H
58#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CANCELLATION_H
59
60#include "Context.h"
61#include "llvm/Support/Error.h"
Sam McCallf0874bf2018-09-13 11:47:48 +000062#include <functional>
Kadir Cetinkayab910a102018-08-24 13:09:41 +000063#include <system_error>
64
65namespace clang {
66namespace clangd {
67
Sam McCallf0874bf2018-09-13 11:47:48 +000068/// A canceller requests cancellation of a task, when called.
69/// Calling it again has no effect.
70using Canceler = std::function<void()>;
Kadir Cetinkayab910a102018-08-24 13:09:41 +000071
Sam McCallf0874bf2018-09-13 11:47:48 +000072/// 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 false until the Canceler is
75/// invoked, and true afterwards.
76std::pair<Context, Canceler> cancelableTask();
Kadir Cetinkayab910a102018-08-24 13:09:41 +000077
Sam McCallf0874bf2018-09-13 11:47:48 +000078/// True if the current context is within a cancelable task which was cancelled.
79/// Always false if there is no active cancelable task.
80/// This isn't free (context lookup) - don't call it in a tight loop.
Sam McCall227e72b2018-11-22 10:22:16 +000081bool isCancelled(const Context &Ctx = Context::current());
Kadir Cetinkayab910a102018-08-24 13:09:41 +000082
Sam McCallf0874bf2018-09-13 11:47:48 +000083/// Conventional error when no result is returned due to cancellation.
Kadir Cetinkayab910a102018-08-24 13:09:41 +000084class CancelledError : public llvm::ErrorInfo<CancelledError> {
85public:
86 static char ID;
87
88 void log(llvm::raw_ostream &OS) const override {
89 OS << "Task was cancelled.";
90 }
91 std::error_code convertToErrorCode() const override {
92 return std::make_error_code(std::errc::operation_canceled);
93 }
94};
95
96} // namespace clangd
97} // namespace clang
98
99#endif