Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[clangd] Initial cancellation mechanism for LSP requests.
Reviewers: ilya-biryukov, ioeric, hokein Reviewed By: ilya-biryukov Subscribers: mgorny, ioeric, MaskRay, jkorous, arphaman, jfb, cfe-commits Differential Revision: https://reviews.llvm.org/D50502 llvm-svn: 340607
- Loading branch information
Showing
15 changed files
with
431 additions
and
29 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
//===--- Cancellation.cpp -----------------------------------------*-C++-*-===// | ||
// | ||
// The LLVM Compiler Infrastructure | ||
// | ||
// This file is distributed under the University of Illinois Open Source | ||
// License. See LICENSE.TXT for details. | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#include "Cancellation.h" | ||
#include <atomic> | ||
|
||
namespace clang { | ||
namespace clangd { | ||
|
||
namespace { | ||
static Key<ConstTaskHandle> TaskKey; | ||
} // namespace | ||
|
||
char CancelledError::ID = 0; | ||
|
||
const Task &getCurrentTask() { | ||
const auto TH = Context::current().getExisting(TaskKey); | ||
assert(TH && "Fetched a nullptr for TaskHandle from context."); | ||
return *TH; | ||
} | ||
|
||
Context setCurrentTask(ConstTaskHandle TH) { | ||
assert(TH && "Trying to stash a nullptr as TaskHandle into context."); | ||
return Context::current().derive(TaskKey, std::move(TH)); | ||
} | ||
|
||
} // namespace clangd | ||
} // namespace clang |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
//===--- Cancellation.h -------------------------------------------*-C++-*-===// | ||
// | ||
// The LLVM Compiler Infrastructure | ||
// | ||
// This file is distributed under the University of Illinois Open Source | ||
// License. See LICENSE.TXT for details. | ||
// | ||
//===----------------------------------------------------------------------===// | ||
// Cancellation mechanism for async tasks. Roughly all the clients of this code | ||
// can be classified into three categories: | ||
// 1. The code that creates and schedules async tasks, e.g. TUScheduler. | ||
// 2. The callers of the async method that can cancel some of the running tasks, | ||
// e.g. `ClangdLSPServer` | ||
// 3. The code running inside the async task itself, i.e. code completion or | ||
// find definition implementation that run clang, etc. | ||
// | ||
// For (1), the guideline is to accept a callback for the result of async | ||
// operation and return a `TaskHandle` to allow cancelling the request. | ||
// | ||
// TaskHandle someAsyncMethod(Runnable T, | ||
// function<void(llvm::Expected<ResultType>)> Callback) { | ||
// auto TH = Task::createHandle(); | ||
// WithContext ContextWithCancellationToken(TH); | ||
// auto run = [](){ | ||
// Callback(T()); | ||
// } | ||
// // Start run() in a new async thread, and make sure to propagate Context. | ||
// return TH; | ||
// } | ||
// | ||
// The callers of async methods (2) can issue cancellations and should be | ||
// prepared to handle `TaskCancelledError` result: | ||
// | ||
// void Caller() { | ||
// // You should store this handle if you wanna cancel the task later on. | ||
// TaskHandle TH = someAsyncMethod(Task, [](llvm::Expected<ResultType> R) { | ||
// if(/*check for task cancellation error*/) | ||
// // Handle the error | ||
// // Do other things on R. | ||
// }); | ||
// // To cancel the task: | ||
// sleep(5); | ||
// TH->cancel(); | ||
// } | ||
// | ||
// The worker code itself (3) should check for cancellations using | ||
// `Task::isCancelled` that can be retrieved via `getCurrentTask()`. | ||
// | ||
// llvm::Expected<ResultType> AsyncTask() { | ||
// // You can either store the read only TaskHandle by calling getCurrentTask | ||
// // once and just use the variable everytime you want to check for | ||
// // cancellation, or call isCancelled everytime. The former is more | ||
// // efficient if you are going to have multiple checks. | ||
// const auto T = getCurrentTask(); | ||
// // DO SMTHNG... | ||
// if(T.isCancelled()) { | ||
// // Task has been cancelled, lets get out. | ||
// return llvm::makeError<CancelledError>(); | ||
// } | ||
// // DO SOME MORE THING... | ||
// if(T.isCancelled()) { | ||
// // Task has been cancelled, lets get out. | ||
// return llvm::makeError<CancelledError>(); | ||
// } | ||
// return ResultType(...); | ||
// } | ||
// If the operation was cancelled before task could run to completion, it should | ||
// propagate the TaskCancelledError as a result. | ||
|
||
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CANCELLATION_H | ||
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CANCELLATION_H | ||
|
||
#include "Context.h" | ||
#include "llvm/Support/Error.h" | ||
#include <atomic> | ||
#include <memory> | ||
#include <system_error> | ||
|
||
namespace clang { | ||
namespace clangd { | ||
|
||
/// Enables signalling a cancellation on an async task or checking for | ||
/// cancellation. It is thread-safe to trigger cancellation from multiple | ||
/// threads or check for cancellation. Task object for the currently running | ||
/// task can be obtained via clangd::getCurrentTask(). | ||
class Task { | ||
public: | ||
void cancel() { CT = true; } | ||
/// If cancellation checks are rare, one could use the isCancelled() helper in | ||
/// the namespace to simplify the code. However, if cancellation checks are | ||
/// frequent, the guideline is first obtain the Task object for the currently | ||
/// running task with getCurrentTask() and do cancel checks using it to avoid | ||
/// extra lookups in the Context. | ||
bool isCancelled() const { return CT; } | ||
|
||
/// Creates a task handle that can be used by an asyn task to check for | ||
/// information that can change during it's runtime, like Cancellation. | ||
static std::shared_ptr<Task> createHandle() { | ||
return std::shared_ptr<Task>(new Task()); | ||
} | ||
|
||
Task(const Task &) = delete; | ||
Task &operator=(const Task &) = delete; | ||
Task(Task &&) = delete; | ||
Task &operator=(Task &&) = delete; | ||
|
||
private: | ||
Task() : CT(false) {} | ||
std::atomic<bool> CT; | ||
}; | ||
using ConstTaskHandle = std::shared_ptr<const Task>; | ||
using TaskHandle = std::shared_ptr<Task>; | ||
|
||
/// Fetches current task information from Context. TaskHandle must have been | ||
/// stashed into context beforehand. | ||
const Task &getCurrentTask(); | ||
|
||
/// Stashes current task information within the context. | ||
LLVM_NODISCARD Context setCurrentTask(ConstTaskHandle TH); | ||
|
||
/// Checks whether the current task has been cancelled or not. | ||
/// Consider storing the task handler returned by getCurrentTask and then | ||
/// calling isCancelled through it. getCurrentTask is expensive since it does a | ||
/// lookup in the context. | ||
inline bool isCancelled() { return getCurrentTask().isCancelled(); } | ||
|
||
class CancelledError : public llvm::ErrorInfo<CancelledError> { | ||
public: | ||
static char ID; | ||
|
||
void log(llvm::raw_ostream &OS) const override { | ||
OS << "Task was cancelled."; | ||
} | ||
std::error_code convertToErrorCode() const override { | ||
return std::make_error_code(std::errc::operation_canceled); | ||
} | ||
}; | ||
|
||
} // namespace clangd | ||
} // namespace clang | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.