#include "Threading.h" #include "Trace.h" #include "llvm/ADT/ScopeExit.h" #include "llvm/Config/config.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/Threading.h" #include #ifdef HAVE_PTHREAD_H #include #endif using namespace llvm; namespace clang { namespace clangd { void Notification::notify() { { std::lock_guard Lock(Mu); Notified = true; } CV.notify_all(); } void Notification::wait() const { std::unique_lock 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 Lock(Mutex); SlotsChanged.wait(Lock, [&]() { return FreeSlots > 0; }); --FreeSlots; } } void Semaphore::unlock() { std::unique_lock Lock(Mutex); ++FreeSlots; Lock.unlock(); SlotsChanged.notify_one(); } AsyncTaskRunner::~AsyncTaskRunner() { wait(); } bool AsyncTaskRunner::wait(Deadline D) const { std::unique_lock Lock(Mutex); return clangd::wait(Lock, TasksReachedZero, D, [&] { return InFlightTasks == 0; }); } void AsyncTaskRunner::runAsync(const Twine &Name, unique_function Action) { { std::lock_guard Lock(Mutex); ++InFlightTasks; } auto CleanupTask = make_scope_exit([this]() { std::lock_guard 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)) { 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(Optional Seconds) { using namespace std::chrono; if (!Seconds) return Deadline::infinity(); return steady_clock::now() + duration_cast(duration(*Seconds)); } void wait(std::unique_lock &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()); } void setThreadPriority(std::thread &T, ThreadPriority Priority) { #ifdef HAVE_PTHREAD_H sched_param priority; priority.sched_priority = 0; pthread_setschedparam( T.native_handle(), Priority == ThreadPriority::Low ? SCHED_IDLE : SCHED_OTHER, &priority); #endif } } // namespace clangd } // namespace clang