14d006520SSam McCall //===--- Threading.cpp - Abstractions for multithreading ------------------===//
24d006520SSam McCall //
34d006520SSam McCall // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
44d006520SSam McCall // See https://llvm.org/LICENSE.txt for license information.
54d006520SSam McCall // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
64d006520SSam McCall //
74d006520SSam McCall //===----------------------------------------------------------------------===//
84d006520SSam McCall
9ad97ccf6SSam McCall #include "support/Threading.h"
10ad97ccf6SSam McCall #include "support/Trace.h"
11ad97ccf6SSam McCall #include "llvm/ADT/ScopeExit.h"
12ad97ccf6SSam McCall #include "llvm/Support/Threading.h"
1348c68a63STim Northover #include "llvm/Support/thread.h"
14ad97ccf6SSam McCall #include <atomic>
15*2c675be9SKazu Hirata #include <optional>
16ad97ccf6SSam McCall #include <thread>
17ad97ccf6SSam McCall #ifdef __USE_POSIX
18ad97ccf6SSam McCall #include <pthread.h>
19ad97ccf6SSam McCall #elif defined(__APPLE__)
20ad97ccf6SSam McCall #include <sys/resource.h>
21ad97ccf6SSam McCall #elif defined(_WIN32)
22ad97ccf6SSam McCall #include <windows.h>
23ad97ccf6SSam McCall #endif
24ad97ccf6SSam McCall
25ad97ccf6SSam McCall namespace clang {
26ad97ccf6SSam McCall namespace clangd {
27ad97ccf6SSam McCall
notify()28ad97ccf6SSam McCall void Notification::notify() {
29ad97ccf6SSam McCall {
30ad97ccf6SSam McCall std::lock_guard<std::mutex> Lock(Mu);
31ad97ccf6SSam McCall Notified = true;
32ad97ccf6SSam McCall // Broadcast with the lock held. This ensures that it's safe to destroy
33ad97ccf6SSam McCall // a Notification after wait() returns, even from another thread.
34ad97ccf6SSam McCall CV.notify_all();
35ad97ccf6SSam McCall }
36ad97ccf6SSam McCall }
37ad97ccf6SSam McCall
wait(Deadline D) const38c468635bSSam McCall bool Notification::wait(Deadline D) const {
39ad97ccf6SSam McCall std::unique_lock<std::mutex> Lock(Mu);
40c468635bSSam McCall return clangd::wait(Lock, CV, D, [&] { return Notified; });
41ad97ccf6SSam McCall }
42ad97ccf6SSam McCall
Semaphore(std::size_t MaxLocks)43ad97ccf6SSam McCall Semaphore::Semaphore(std::size_t MaxLocks) : FreeSlots(MaxLocks) {}
44ad97ccf6SSam McCall
try_lock()45ad97ccf6SSam McCall bool Semaphore::try_lock() {
46ad97ccf6SSam McCall std::unique_lock<std::mutex> Lock(Mutex);
47ad97ccf6SSam McCall if (FreeSlots > 0) {
48ad97ccf6SSam McCall --FreeSlots;
49ad97ccf6SSam McCall return true;
50ad97ccf6SSam McCall }
51ad97ccf6SSam McCall return false;
52ad97ccf6SSam McCall }
53ad97ccf6SSam McCall
lock()54ad97ccf6SSam McCall void Semaphore::lock() {
55ad97ccf6SSam McCall trace::Span Span("WaitForFreeSemaphoreSlot");
56ad97ccf6SSam McCall // trace::Span can also acquire locks in ctor and dtor, we make sure it
57ad97ccf6SSam McCall // happens when Semaphore's own lock is not held.
58ad97ccf6SSam McCall {
59ad97ccf6SSam McCall std::unique_lock<std::mutex> Lock(Mutex);
60ad97ccf6SSam McCall SlotsChanged.wait(Lock, [&]() { return FreeSlots > 0; });
61ad97ccf6SSam McCall --FreeSlots;
62ad97ccf6SSam McCall }
63ad97ccf6SSam McCall }
64ad97ccf6SSam McCall
unlock()65ad97ccf6SSam McCall void Semaphore::unlock() {
66ad97ccf6SSam McCall std::unique_lock<std::mutex> Lock(Mutex);
67ad97ccf6SSam McCall ++FreeSlots;
68ad97ccf6SSam McCall Lock.unlock();
69ad97ccf6SSam McCall
70ad97ccf6SSam McCall SlotsChanged.notify_one();
71ad97ccf6SSam McCall }
72ad97ccf6SSam McCall
~AsyncTaskRunner()73ad97ccf6SSam McCall AsyncTaskRunner::~AsyncTaskRunner() { wait(); }
74ad97ccf6SSam McCall
wait(Deadline D) const75ad97ccf6SSam McCall bool AsyncTaskRunner::wait(Deadline D) const {
76ad97ccf6SSam McCall std::unique_lock<std::mutex> Lock(Mutex);
77ad97ccf6SSam McCall return clangd::wait(Lock, TasksReachedZero, D,
78ad97ccf6SSam McCall [&] { return InFlightTasks == 0; });
79ad97ccf6SSam McCall }
80ad97ccf6SSam McCall
runAsync(const llvm::Twine & Name,llvm::unique_function<void ()> Action)81ad97ccf6SSam McCall void AsyncTaskRunner::runAsync(const llvm::Twine &Name,
82ad97ccf6SSam McCall llvm::unique_function<void()> Action) {
83ad97ccf6SSam McCall {
84ad97ccf6SSam McCall std::lock_guard<std::mutex> Lock(Mutex);
85ad97ccf6SSam McCall ++InFlightTasks;
86ad97ccf6SSam McCall }
87ad97ccf6SSam McCall
88ad97ccf6SSam McCall auto CleanupTask = llvm::make_scope_exit([this]() {
89ad97ccf6SSam McCall std::lock_guard<std::mutex> Lock(Mutex);
90ad97ccf6SSam McCall int NewTasksCnt = --InFlightTasks;
91ad97ccf6SSam McCall if (NewTasksCnt == 0) {
92ad97ccf6SSam McCall // Note: we can't unlock here because we don't want the object to be
93ad97ccf6SSam McCall // destroyed before we notify.
94ad97ccf6SSam McCall TasksReachedZero.notify_one();
95ad97ccf6SSam McCall }
96ad97ccf6SSam McCall });
97ad97ccf6SSam McCall
98ad97ccf6SSam McCall auto Task = [Name = Name.str(), Action = std::move(Action),
99ad97ccf6SSam McCall Cleanup = std::move(CleanupTask)]() mutable {
100ad97ccf6SSam McCall llvm::set_thread_name(Name);
101ad97ccf6SSam McCall Action();
102ad97ccf6SSam McCall // Make sure function stored by ThreadFunc is destroyed before Cleanup runs.
103ad97ccf6SSam McCall Action = nullptr;
104ad97ccf6SSam McCall };
105ad97ccf6SSam McCall
106ad97ccf6SSam McCall // Ensure our worker threads have big enough stacks to run clang.
10748c68a63STim Northover llvm::thread Thread(
108b1df3a2cSFangrui Song /*clang::DesiredStackSize*/ std::optional<unsigned>(8 << 20),
10948c68a63STim Northover std::move(Task));
11048c68a63STim Northover Thread.detach();
111ad97ccf6SSam McCall }
112ad97ccf6SSam McCall
timeoutSeconds(std::optional<double> Seconds)113*2c675be9SKazu Hirata Deadline timeoutSeconds(std::optional<double> Seconds) {
114ad97ccf6SSam McCall using namespace std::chrono;
115ad97ccf6SSam McCall if (!Seconds)
116ad97ccf6SSam McCall return Deadline::infinity();
117ad97ccf6SSam McCall return steady_clock::now() +
118ad97ccf6SSam McCall duration_cast<steady_clock::duration>(duration<double>(*Seconds));
119ad97ccf6SSam McCall }
120ad97ccf6SSam McCall
wait(std::unique_lock<std::mutex> & Lock,std::condition_variable & CV,Deadline D)121ad97ccf6SSam McCall void wait(std::unique_lock<std::mutex> &Lock, std::condition_variable &CV,
122ad97ccf6SSam McCall Deadline D) {
123ad97ccf6SSam McCall if (D == Deadline::zero())
124ad97ccf6SSam McCall return;
125ad97ccf6SSam McCall if (D == Deadline::infinity())
126ad97ccf6SSam McCall return CV.wait(Lock);
127ad97ccf6SSam McCall CV.wait_until(Lock, D.time());
128ad97ccf6SSam McCall }
129ad97ccf6SSam McCall
operator ()()1303dbe471aSSam McCall bool PeriodicThrottler::operator()() {
1313dbe471aSSam McCall Rep Now = Stopwatch::now().time_since_epoch().count();
1323dbe471aSSam McCall Rep OldNext = Next.load(std::memory_order_acquire);
1333dbe471aSSam McCall if (Now < OldNext)
1343dbe471aSSam McCall return false;
1353dbe471aSSam McCall // We're ready to run (but may be racing other threads).
1363dbe471aSSam McCall // Work out the updated target time, and run if we successfully bump it.
1373dbe471aSSam McCall Rep NewNext = Now + Period;
1383dbe471aSSam McCall return Next.compare_exchange_strong(OldNext, NewNext,
1393dbe471aSSam McCall std::memory_order_acq_rel);
1403dbe471aSSam McCall }
1413dbe471aSSam McCall
142ad97ccf6SSam McCall } // namespace clangd
143ad97ccf6SSam McCall } // namespace clang
144