173
|
1 #include "support/Threading.h"
|
|
2 #include "support/Trace.h"
|
|
3 #include "llvm/ADT/ScopeExit.h"
|
|
4 #include "llvm/Support/FormatVariadic.h"
|
|
5 #include "llvm/Support/Threading.h"
|
|
6 #include <atomic>
|
|
7 #include <thread>
|
|
8 #ifdef __USE_POSIX
|
|
9 #include <pthread.h>
|
|
10 #elif defined(__APPLE__)
|
|
11 #include <sys/resource.h>
|
|
12 #elif defined(_WIN32)
|
|
13 #include <windows.h>
|
|
14 #endif
|
|
15
|
|
16 namespace clang {
|
|
17 namespace clangd {
|
|
18
|
|
19 void Notification::notify() {
|
|
20 {
|
|
21 std::lock_guard<std::mutex> Lock(Mu);
|
|
22 Notified = true;
|
|
23 // Broadcast with the lock held. This ensures that it's safe to destroy
|
|
24 // a Notification after wait() returns, even from another thread.
|
|
25 CV.notify_all();
|
|
26 }
|
|
27 }
|
|
28
|
|
29 void Notification::wait() const {
|
|
30 std::unique_lock<std::mutex> Lock(Mu);
|
|
31 CV.wait(Lock, [this] { return Notified; });
|
|
32 }
|
|
33
|
|
34 Semaphore::Semaphore(std::size_t MaxLocks) : FreeSlots(MaxLocks) {}
|
|
35
|
|
36 bool Semaphore::try_lock() {
|
|
37 std::unique_lock<std::mutex> Lock(Mutex);
|
|
38 if (FreeSlots > 0) {
|
|
39 --FreeSlots;
|
|
40 return true;
|
|
41 }
|
|
42 return false;
|
|
43 }
|
|
44
|
|
45 void Semaphore::lock() {
|
|
46 trace::Span Span("WaitForFreeSemaphoreSlot");
|
|
47 // trace::Span can also acquire locks in ctor and dtor, we make sure it
|
|
48 // happens when Semaphore's own lock is not held.
|
|
49 {
|
|
50 std::unique_lock<std::mutex> Lock(Mutex);
|
|
51 SlotsChanged.wait(Lock, [&]() { return FreeSlots > 0; });
|
|
52 --FreeSlots;
|
|
53 }
|
|
54 }
|
|
55
|
|
56 void Semaphore::unlock() {
|
|
57 std::unique_lock<std::mutex> Lock(Mutex);
|
|
58 ++FreeSlots;
|
|
59 Lock.unlock();
|
|
60
|
|
61 SlotsChanged.notify_one();
|
|
62 }
|
|
63
|
|
64 AsyncTaskRunner::~AsyncTaskRunner() { wait(); }
|
|
65
|
|
66 bool AsyncTaskRunner::wait(Deadline D) const {
|
|
67 std::unique_lock<std::mutex> Lock(Mutex);
|
|
68 return clangd::wait(Lock, TasksReachedZero, D,
|
|
69 [&] { return InFlightTasks == 0; });
|
|
70 }
|
|
71
|
|
72 void AsyncTaskRunner::runAsync(const llvm::Twine &Name,
|
|
73 llvm::unique_function<void()> Action) {
|
|
74 {
|
|
75 std::lock_guard<std::mutex> Lock(Mutex);
|
|
76 ++InFlightTasks;
|
|
77 }
|
|
78
|
|
79 auto CleanupTask = llvm::make_scope_exit([this]() {
|
|
80 std::lock_guard<std::mutex> Lock(Mutex);
|
|
81 int NewTasksCnt = --InFlightTasks;
|
|
82 if (NewTasksCnt == 0) {
|
|
83 // Note: we can't unlock here because we don't want the object to be
|
|
84 // destroyed before we notify.
|
|
85 TasksReachedZero.notify_one();
|
|
86 }
|
|
87 });
|
|
88
|
|
89 auto Task = [Name = Name.str(), Action = std::move(Action),
|
|
90 Cleanup = std::move(CleanupTask)]() mutable {
|
|
91 llvm::set_thread_name(Name);
|
|
92 Action();
|
|
93 // Make sure function stored by ThreadFunc is destroyed before Cleanup runs.
|
|
94 Action = nullptr;
|
|
95 };
|
|
96
|
|
97 // Ensure our worker threads have big enough stacks to run clang.
|
|
98 llvm::llvm_execute_on_thread_async(std::move(Task),
|
|
99 /*clang::DesiredStackSize*/ 8 << 20);
|
|
100 }
|
|
101
|
|
102 Deadline timeoutSeconds(llvm::Optional<double> Seconds) {
|
|
103 using namespace std::chrono;
|
|
104 if (!Seconds)
|
|
105 return Deadline::infinity();
|
|
106 return steady_clock::now() +
|
|
107 duration_cast<steady_clock::duration>(duration<double>(*Seconds));
|
|
108 }
|
|
109
|
|
110 void wait(std::unique_lock<std::mutex> &Lock, std::condition_variable &CV,
|
|
111 Deadline D) {
|
|
112 if (D == Deadline::zero())
|
|
113 return;
|
|
114 if (D == Deadline::infinity())
|
|
115 return CV.wait(Lock);
|
|
116 CV.wait_until(Lock, D.time());
|
|
117 }
|
|
118
|
|
119 } // namespace clangd
|
|
120 } // namespace clang
|