clang-tools 19.0.0git
Threading.cpp
Go to the documentation of this file.
1//===--- Threading.cpp - Abstractions for multithreading ------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "support/Threading.h"
10#include "support/Trace.h"
11#include "llvm/ADT/ScopeExit.h"
12#include "llvm/Support/Threading.h"
13#include "llvm/Support/thread.h"
14#include <atomic>
15#include <optional>
16#include <thread>
17#ifdef __USE_POSIX
18#include <pthread.h>
19#elif defined(__APPLE__)
20#include <sys/resource.h>
21#elif defined(_WIN32)
22#include <windows.h>
23#endif
24
25namespace clang {
26namespace clangd {
27
29 {
30 std::lock_guard<std::mutex> Lock(Mu);
31 Notified = true;
32 // Broadcast with the lock held. This ensures that it's safe to destroy
33 // a Notification after wait() returns, even from another thread.
34 CV.notify_all();
35 }
36}
37
39 std::unique_lock<std::mutex> Lock(Mu);
40 return clangd::wait(Lock, CV, D, [&] { return Notified; });
41}
42
43Semaphore::Semaphore(std::size_t MaxLocks) : FreeSlots(MaxLocks) {}
44
46 std::unique_lock<std::mutex> Lock(Mutex);
47 if (FreeSlots > 0) {
48 --FreeSlots;
49 return true;
50 }
51 return false;
52}
53
55 trace::Span Span("WaitForFreeSemaphoreSlot");
56 // trace::Span can also acquire locks in ctor and dtor, we make sure it
57 // happens when Semaphore's own lock is not held.
58 {
59 std::unique_lock<std::mutex> Lock(Mutex);
60 SlotsChanged.wait(Lock, [&]() { return FreeSlots > 0; });
61 --FreeSlots;
62 }
63}
64
66 std::unique_lock<std::mutex> Lock(Mutex);
67 ++FreeSlots;
68 Lock.unlock();
69
70 SlotsChanged.notify_one();
71}
72
74
76 std::unique_lock<std::mutex> Lock(Mutex);
77 return clangd::wait(Lock, TasksReachedZero, D,
78 [&] { return InFlightTasks == 0; });
79}
80
81void AsyncTaskRunner::runAsync(const llvm::Twine &Name,
82 llvm::unique_function<void()> Action) {
83 {
84 std::lock_guard<std::mutex> Lock(Mutex);
85 ++InFlightTasks;
86 }
87
88 auto CleanupTask = llvm::make_scope_exit([this]() {
89 std::lock_guard<std::mutex> Lock(Mutex);
90 int NewTasksCnt = --InFlightTasks;
91 if (NewTasksCnt == 0) {
92 // Note: we can't unlock here because we don't want the object to be
93 // destroyed before we notify.
94 TasksReachedZero.notify_one();
95 }
96 });
97
98 auto Task = [Name = Name.str(), Action = std::move(Action),
99 Cleanup = std::move(CleanupTask)]() mutable {
100 llvm::set_thread_name(Name);
101 Action();
102 // Make sure function stored by ThreadFunc is destroyed before Cleanup runs.
103 Action = nullptr;
104 };
105
106 // Ensure our worker threads have big enough stacks to run clang.
107 llvm::thread Thread(
108 /*clang::DesiredStackSize*/ std::optional<unsigned>(8 << 20),
109 std::move(Task));
110 Thread.detach();
111}
112
113Deadline timeoutSeconds(std::optional<double> Seconds) {
114 using namespace std::chrono;
115 if (!Seconds)
116 return Deadline::infinity();
117 return steady_clock::now() +
118 duration_cast<steady_clock::duration>(duration<double>(*Seconds));
119}
120
121void wait(std::unique_lock<std::mutex> &Lock, std::condition_variable &CV,
122 Deadline D) {
123 if (D == Deadline::zero())
124 return;
125 if (D == Deadline::infinity())
126 return CV.wait(Lock);
127 CV.wait_until(Lock, D.time());
128}
129
131 Rep Now = Stopwatch::now().time_since_epoch().count();
132 Rep OldNext = Next.load(std::memory_order_acquire);
133 if (Now < OldNext)
134 return false;
135 // We're ready to run (but may be racing other threads).
136 // Work out the updated target time, and run if we successfully bump it.
137 Rep NewNext = Now + Period;
138 return Next.compare_exchange_strong(OldNext, NewNext,
139 std::memory_order_acq_rel);
140}
141
142} // namespace clangd
143} // namespace clang
llvm::SmallString< 256U > Name
FieldAction Action
void runAsync(const llvm::Twine &Name, llvm::unique_function< void()> Action)
Definition: Threading.cpp:81
~AsyncTaskRunner()
Destructor waits for all pending tasks to finish.
Definition: Threading.cpp:73
A point in time we can wait for.
Definition: Threading.h:46
static Deadline zero()
Definition: Threading.h:50
static Deadline infinity()
Definition: Threading.h:51
bool operator()()
Returns whether the operation should run at this time.
Definition: Threading.cpp:130
Semaphore(std::size_t MaxLocks)
Definition: Threading.cpp:43
Records an event whose duration is the lifetime of the Span object.
Definition: Trace.h:143
void wait(std::unique_lock< std::mutex > &Lock, std::condition_variable &CV, Deadline D)
Wait once on CV for the specified duration.
Definition: Threading.cpp:121
Deadline timeoutSeconds(std::optional< double > Seconds)
Makes a deadline from a timeout in seconds. std::nullopt means wait forever.
Definition: Threading.cpp:113
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//