105 lines · c
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-exception6//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// Expected18// 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 examples25// 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 the28// 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 results49// even after requesting cancellation.50// - it's Context-based, so async work must be dispatched to threads in51// 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 would55// help find where libraries' cancellation should be improved.56 57#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_SUPPORT_CANCELLATION_H58#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_SUPPORT_CANCELLATION_H59 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 is75/// 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(std::errc::operation_canceled);98 }99};100 101} // namespace clangd102} // namespace clang103 104#endif105