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