1 //===--- Threading.h - Abstractions for multithreading -----------*- C++-*-===//
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
7 //===----------------------------------------------------------------------===//
9 #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_SUPPORT_THREADING_H
10 #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_SUPPORT_THREADING_H
12 #include "support/Context.h"
13 #include "llvm/ADT/FunctionExtras.h"
14 #include "llvm/ADT/Twine.h"
17 #include <condition_variable>
28 /// Limits the number of threads that can acquire the lock at the same time.
31 Semaphore(std::size_t MaxLocks
);
39 std::condition_variable SlotsChanged
;
40 std::size_t FreeSlots
;
43 /// A point in time we can wait for.
44 /// Can be zero (don't wait) or infinity (wait forever).
45 /// (Not time_point::max(), because many std::chrono implementations overflow).
48 Deadline(std::chrono::steady_clock::time_point Time
)
49 : Type(Finite
), Time(Time
) {}
50 static Deadline
zero() { return Deadline(Zero
); }
51 static Deadline
infinity() { return Deadline(Infinite
); }
53 std::chrono::steady_clock::time_point
time() const {
54 assert(Type
== Finite
);
57 bool expired() const {
58 return (Type
== Zero
) ||
59 (Type
== Finite
&& Time
< std::chrono::steady_clock::now());
61 bool operator==(const Deadline
&Other
) const {
62 return (Type
== Other
.Type
) && (Type
!= Finite
|| Time
== Other
.Time
);
66 enum Type
{ Zero
, Infinite
, Finite
};
68 Deadline(enum Type Type
) : Type(Type
) {}
70 std::chrono::steady_clock::time_point Time
;
73 /// Makes a deadline from a timeout in seconds. std::nullopt means wait forever.
74 Deadline
timeoutSeconds(std::optional
<double> Seconds
);
75 /// Wait once on CV for the specified duration.
76 void wait(std::unique_lock
<std::mutex
> &Lock
, std::condition_variable
&CV
,
78 /// Waits on a condition variable until F() is true or D expires.
79 template <typename Func
>
80 [[nodiscard
]] bool wait(std::unique_lock
<std::mutex
> &Lock
,
81 std::condition_variable
&CV
, Deadline D
, Func F
) {
90 /// A threadsafe flag that is initially clear.
93 // Sets the flag. No-op if already set.
95 // Blocks until flag is set.
96 void wait() const { (void)wait(Deadline::infinity()); }
97 [[nodiscard
]] bool wait(Deadline D
) const;
100 bool Notified
= false;
101 mutable std::condition_variable CV
;
102 mutable std::mutex Mu
;
105 /// Runs tasks on separate (detached) threads and wait for all tasks to finish.
106 /// Objects that need to spawn threads can own an AsyncTaskRunner to ensure they
107 /// all complete on destruction.
108 class AsyncTaskRunner
{
110 /// Destructor waits for all pending tasks to finish.
113 void wait() const { (void)wait(Deadline::infinity()); }
114 [[nodiscard
]] bool wait(Deadline D
) const;
115 // The name is used for tracing and debugging (e.g. to name a spawned thread).
116 void runAsync(const llvm::Twine
&Name
, llvm::unique_function
<void()> Action
);
119 mutable std::mutex Mutex
;
120 mutable std::condition_variable TasksReachedZero
;
121 std::size_t InFlightTasks
= 0;
124 /// Runs \p Action asynchronously with a new std::thread. The context will be
126 template <typename T
>
127 std::future
<T
> runAsync(llvm::unique_function
<T()> Action
) {
130 [](llvm::unique_function
<T()> &&Action
, Context Ctx
) {
131 WithContext
WithCtx(std::move(Ctx
));
134 std::move(Action
), Context::current().clone());
137 /// Memoize is a cache to store and reuse computation results based on a key.
139 /// Memoize<DenseMap<int, bool>> PrimeCache;
140 /// for (int I : RepetitiveNumbers)
141 /// if (PrimeCache.get(I, [&] { return expensiveIsPrime(I); }))
142 /// llvm::errs() << "Prime: " << I << "\n";
144 /// The computation will only be run once for each key.
145 /// This class is threadsafe. Concurrent calls for the same key may run the
146 /// computation multiple times, but each call will return the same result.
147 template <typename Container
> class Memoize
{
148 mutable Container Cache
;
149 std::unique_ptr
<std::mutex
> Mu
;
152 Memoize() : Mu(std::make_unique
<std::mutex
>()) {}
154 template <typename T
, typename Func
>
155 typename
Container::mapped_type
get(T
&&Key
, Func Compute
) const {
157 std::lock_guard
<std::mutex
> Lock(*Mu
);
158 auto It
= Cache
.find(Key
);
159 if (It
!= Cache
.end())
162 // Don't hold the mutex while computing.
165 std::lock_guard
<std::mutex
> Lock(*Mu
);
166 auto R
= Cache
.try_emplace(std::forward
<T
>(Key
), V
);
167 // Insert into cache may fail if we raced with another thread.
169 return R
.first
->second
; // Canonical value, from other thread.
175 /// Used to guard an operation that should run at most every N seconds.
178 /// mutable PeriodicThrottler ShouldLog(std::chrono::seconds(1));
179 /// void calledFrequently() {
181 /// log("this is not spammy");
184 /// This class is threadsafe. If multiple threads are involved, then the guarded
185 /// operation still needs to be threadsafe!
186 class PeriodicThrottler
{
187 using Stopwatch
= std::chrono::steady_clock
;
188 using Rep
= Stopwatch::duration::rep
;
191 std::atomic
<Rep
> Next
;
194 /// If Period is zero, the throttler will return true every time.
195 PeriodicThrottler(Stopwatch::duration Period
, Stopwatch::duration Delay
= {})
196 : Period(Period
.count()),
197 Next((Stopwatch::now() + Delay
).time_since_epoch().count()) {}
199 /// Returns whether the operation should run at this time.
200 /// operator() is safe to call concurrently.
204 } // namespace clangd