#include "Threading.h" #include "Trace.h" #include "llvm/ADT/ScopeExit.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/Threading.h" #include #include #ifdef __USE_POSIX #include #endif 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) {} bool Semaphore::try_lock() { std::unique_lock Lock(Mutex); if (FreeSlots > 0) { --FreeSlots; return true; } return false; } 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 llvm::Twine &Name, llvm::unique_function Action) { { std::lock_guard Lock(Mutex); ++InFlightTasks; } auto CleanupTask = llvm::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)) { 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 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()); } static std::atomic AvoidThreadStarvation = {false}; void setCurrentThreadPriority(ThreadPriority Priority) { // Some *really* old glibcs are missing SCHED_IDLE. #if defined(__linux__) && defined(SCHED_IDLE) sched_param priority; priority.sched_priority = 0; pthread_setschedparam( pthread_self(), Priority == ThreadPriority::Low && !AvoidThreadStarvation ? SCHED_IDLE : SCHED_OTHER, &priority); #endif } void preventThreadStarvationInTests() { AvoidThreadStarvation = true; } } // namespace clangd } // namespace clang