teak-llvm/clang-tools-extra/clangd/Threading.cpp
Sam McCall 0bb24cd4fa [clangd] Stop exposing Futures from ClangdServer operations.
Summary:
LSP has asynchronous semantics, being able to block on an async operation
completing is unneccesary and leads to tighter coupling with the threading.

In practice only tests depend on this, so we add a general-purpose "block until
idle" function to the scheduler which will work for all operations.

To get this working, fix a latent condition-variable bug in ASTWorker, and make
AsyncTaskRunner const-correct.

Reviewers: ilya-biryukov

Subscribers: klimek, jkorous-apple, ioeric, cfe-commits

Differential Revision: https://reviews.llvm.org/D43127

llvm-svn: 324990
2018-02-13 08:59:23 +00:00

74 lines
1.9 KiB
C++

#include "Threading.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/Threading.h"
#include <thread>
namespace clang {
namespace clangd {
CancellationFlag::CancellationFlag()
: WasCancelled(std::make_shared<std::atomic<bool>>(false)) {}
Semaphore::Semaphore(std::size_t MaxLocks) : FreeSlots(MaxLocks) {}
void Semaphore::lock() {
std::unique_lock<std::mutex> Lock(Mutex);
SlotsChanged.wait(Lock, [&]() { return FreeSlots > 0; });
--FreeSlots;
}
void Semaphore::unlock() {
std::unique_lock<std::mutex> Lock(Mutex);
++FreeSlots;
Lock.unlock();
SlotsChanged.notify_one();
}
AsyncTaskRunner::~AsyncTaskRunner() { wait(); }
bool AsyncTaskRunner::wait(Deadline D) const {
std::unique_lock<std::mutex> Lock(Mutex);
return clangd::wait(Lock, TasksReachedZero, D,
[&] { return InFlightTasks == 0; });
}
void AsyncTaskRunner::runAsync(UniqueFunction<void()> Action) {
{
std::lock_guard<std::mutex> Lock(Mutex);
++InFlightTasks;
}
auto CleanupTask = llvm::make_scope_exit([this]() {
std::lock_guard<std::mutex> Lock(Mutex);
int NewTasksCnt = --InFlightTasks;
if (NewTasksCnt == 0) {
// Note: we can't unlock here because we don't want the object to be
// destroyed before we notify.
TasksReachedZero.notify_one();
}
});
std::thread(
[](decltype(Action) Action, decltype(CleanupTask)) {
Action();
// Make sure function stored by Action is destroyed before CleanupTask
// is run.
Action = nullptr;
},
std::move(Action), std::move(CleanupTask))
.detach();
}
Deadline timeoutSeconds(llvm::Optional<double> Seconds) {
using namespace std::chrono;
if (!Seconds)
return llvm::None;
return steady_clock::now() +
duration_cast<steady_clock::duration>(duration<double>(*Seconds));
}
} // namespace clangd
} // namespace clang