|  | #include "Threading.h" | 
|  | #include "Trace.h" | 
|  | #include "llvm/ADT/ScopeExit.h" | 
|  | #include "llvm/Support/FormatVariadic.h" | 
|  | #include "llvm/Support/Threading.h" | 
|  | #include <thread> | 
|  |  | 
|  | namespace clang { | 
|  | namespace clangd { | 
|  |  | 
|  | void Notification::notify() { | 
|  | { | 
|  | std::lock_guard<std::mutex> Lock(Mu); | 
|  | Notified = true; | 
|  | } | 
|  | CV.notify_all(); | 
|  | } | 
|  |  | 
|  | void Notification::wait() const { | 
|  | std::unique_lock<std::mutex> Lock(Mu); | 
|  | CV.wait(Lock, [this] { return Notified; }); | 
|  | } | 
|  |  | 
|  | Semaphore::Semaphore(std::size_t MaxLocks) : FreeSlots(MaxLocks) {} | 
|  |  | 
|  | void Semaphore::lock() { | 
|  | trace::Span Span("WaitForFreeSemaphoreSlot"); | 
|  | // trace::Span can also acquire locks in ctor and dtor, we make sure it | 
|  | // happens when Semaphore's own lock is not held. | 
|  | { | 
|  | 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(const llvm::Twine &Name, | 
|  | llvm::unique_function<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( | 
|  | [](std::string Name, decltype(Action) Action, decltype(CleanupTask)) { | 
|  | llvm::set_thread_name(Name); | 
|  | Action(); | 
|  | // Make sure function stored by Action is destroyed before CleanupTask | 
|  | // is run. | 
|  | Action = nullptr; | 
|  | }, | 
|  | Name.str(), std::move(Action), std::move(CleanupTask)) | 
|  | .detach(); | 
|  | } | 
|  |  | 
|  | Deadline timeoutSeconds(llvm::Optional<double> Seconds) { | 
|  | using namespace std::chrono; | 
|  | if (!Seconds) | 
|  | return Deadline::infinity(); | 
|  | return steady_clock::now() + | 
|  | duration_cast<steady_clock::duration>(duration<double>(*Seconds)); | 
|  | } | 
|  |  | 
|  | void wait(std::unique_lock<std::mutex> &Lock, std::condition_variable &CV, | 
|  | Deadline D) { | 
|  | if (D == Deadline::zero()) | 
|  | return; | 
|  | if (D == Deadline::infinity()) | 
|  | return CV.wait(Lock); | 
|  | CV.wait_until(Lock, D.time()); | 
|  | } | 
|  |  | 
|  | } // namespace clangd | 
|  | } // namespace clang |