3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2026-06-03 15:47:58 +00:00

threading: make no-op locks specialized to Mutex instead of templates

This commit is contained in:
Emil J. Tywoniak 2026-05-18 16:13:19 +02:00
parent 1c831aa50d
commit 0c2786be1f

View file

@ -20,8 +20,8 @@ YOSYS_NAMESPACE_BEGIN
#ifdef YOSYS_ENABLE_THREADS #ifdef YOSYS_ENABLE_THREADS
using Mutex = std::mutex; using Mutex = std::mutex;
using CondVar = std::condition_variable; using CondVar = std::condition_variable;
template <class M> using UniqueLock = std::unique_lock<M>; using UniqueLock = std::unique_lock<Mutex>;
template <class M> using LockGuard = std::lock_guard<M>; using LockGuard = std::lock_guard<Mutex>;
#else #else
struct Mutex { struct Mutex {
void lock() {} void lock() {}
@ -34,11 +34,11 @@ struct CondVar {
void notify_one() {} void notify_one() {}
void notify_all() {} void notify_all() {}
}; };
template <class M> struct UniqueLock { struct UniqueLock {
UniqueLock(M &) {} UniqueLock(Mutex &) {}
}; };
template <class M> struct LockGuard { struct LockGuard {
LockGuard(M &) {} LockGuard(Mutex &) {}
}; };
#endif #endif
@ -54,7 +54,7 @@ public:
// Push an element into the queue. If it's at capacity, block until there is room. // Push an element into the queue. If it's at capacity, block until there is room.
void push_back(T t) void push_back(T t)
{ {
UniqueLock<Mutex> lock(mutex); UniqueLock lock(mutex);
not_full_condition.wait(lock, [this] { return static_cast<int>(contents.size()) < capacity; }); not_full_condition.wait(lock, [this] { return static_cast<int>(contents.size()) < capacity; });
if (contents.empty()) if (contents.empty())
not_empty_condition.notify_one(); not_empty_condition.notify_one();
@ -66,7 +66,7 @@ public:
// Signal that no more elements will be produced. `pop_front()` will return nullopt. // Signal that no more elements will be produced. `pop_front()` will return nullopt.
void close() void close()
{ {
UniqueLock<Mutex> lock(mutex); UniqueLock lock(mutex);
not_empty_condition.notify_all(); not_empty_condition.notify_all();
closed = true; closed = true;
} }
@ -85,7 +85,7 @@ public:
private: private:
std::optional<T> pop_front_internal(bool wait) std::optional<T> pop_front_internal(bool wait)
{ {
UniqueLock<Mutex> lock(mutex); UniqueLock lock(mutex);
if (wait) { if (wait) {
not_empty_condition.wait(lock, [this] { return !contents.empty() || closed; }); not_empty_condition.wait(lock, [this] { return !contents.empty() || closed; });
} }
@ -262,7 +262,7 @@ private:
CondVar main_to_workers_signal_cv; CondVar main_to_workers_signal_cv;
std::vector<uint8_t> main_to_workers_signal; std::vector<uint8_t> main_to_workers_signal;
void signal_workers_start() { void signal_workers_start() {
UniqueLock<Mutex> lock(main_to_workers_signal_mutex); UniqueLock lock(main_to_workers_signal_mutex);
int num_active_worker_threads = num_active_worker_threads_.load(std::memory_order_relaxed); int num_active_worker_threads = num_active_worker_threads_.load(std::memory_order_relaxed);
std::fill(main_to_workers_signal.begin(), main_to_workers_signal.begin() + num_active_worker_threads, 1); std::fill(main_to_workers_signal.begin(), main_to_workers_signal.begin() + num_active_worker_threads, 1);
// When `num_active_worker_threads_` is small compared to `num_worker_threads_`, we have a "thundering herd" // When `num_active_worker_threads_` is small compared to `num_worker_threads_`, we have a "thundering herd"
@ -270,7 +270,7 @@ private:
main_to_workers_signal_cv.notify_all(); main_to_workers_signal_cv.notify_all();
} }
void worker_wait_for_start(int thread_num) { void worker_wait_for_start(int thread_num) {
UniqueLock<Mutex> lock(main_to_workers_signal_mutex); UniqueLock lock(main_to_workers_signal_mutex);
main_to_workers_signal_cv.wait(lock, [this, thread_num] { return main_to_workers_signal[thread_num] > 0; }); main_to_workers_signal_cv.wait(lock, [this, thread_num] { return main_to_workers_signal[thread_num] > 0; });
main_to_workers_signal[thread_num] = 0; main_to_workers_signal[thread_num] = 0;
} }
@ -286,12 +286,12 @@ private:
int num_active_worker_threads = num_active_worker_threads_.load(std::memory_order_relaxed); int num_active_worker_threads = num_active_worker_threads_.load(std::memory_order_relaxed);
int d = done_workers.fetch_add(1, std::memory_order_release); int d = done_workers.fetch_add(1, std::memory_order_release);
if (d + 1 == num_active_worker_threads) { if (d + 1 == num_active_worker_threads) {
UniqueLock<Mutex> lock(workers_to_main_signal_mutex); UniqueLock lock(workers_to_main_signal_mutex);
workers_to_main_signal_cv.notify_all(); workers_to_main_signal_cv.notify_all();
} }
} }
void wait_for_workers_done() { void wait_for_workers_done() {
UniqueLock<Mutex> lock(workers_to_main_signal_mutex); UniqueLock lock(workers_to_main_signal_mutex);
workers_to_main_signal_cv.wait(lock, [this] { workers_to_main_signal_cv.wait(lock, [this] {
int num_active_worker_threads = num_active_worker_threads_.load(std::memory_order_relaxed); int num_active_worker_threads = num_active_worker_threads_.load(std::memory_order_relaxed);
return done_workers.load(std::memory_order_acquire) == num_active_worker_threads; return done_workers.load(std::memory_order_acquire) == num_active_worker_threads;
@ -309,11 +309,11 @@ class ConcurrentStack
{ {
public: public:
void push_back(T &&t) { void push_back(T &&t) {
LockGuard<Mutex> lock(mutex); LockGuard lock(mutex);
contents.push_back(std::move(t)); contents.push_back(std::move(t));
} }
std::optional<T> try_pop_back() { std::optional<T> try_pop_back() {
LockGuard<Mutex> lock(mutex); LockGuard lock(mutex);
if (contents.empty()) if (contents.empty())
return std::nullopt; return std::nullopt;
T result = std::move(contents.back()); T result = std::move(contents.back());
@ -598,12 +598,12 @@ public:
return; return;
bool was_empty; bool was_empty;
{ {
UniqueLock<Mutex> lock(thread_state.batches_lock); UniqueLock lock(thread_state.batches_lock);
was_empty = thread_state.batches.empty(); was_empty = thread_state.batches.empty();
thread_state.batches.push_back(std::move(thread_state.next_batch)); thread_state.batches.push_back(std::move(thread_state.next_batch));
} }
if (was_empty) { if (was_empty) {
UniqueLock<Mutex> lock(waiters_lock); UniqueLock lock(waiters_lock);
if (num_waiters > 0) { if (num_waiters > 0) {
waiters_cv.notify_one(); waiters_cv.notify_one();
} }
@ -619,7 +619,7 @@ public:
return std::move(thread_state.next_batch); return std::move(thread_state.next_batch);
// Empty our own work queue first. // Empty our own work queue first.
{ {
UniqueLock<Mutex> lock(thread_state.batches_lock); UniqueLock lock(thread_state.batches_lock);
if (!thread_state.batches.empty()) { if (!thread_state.batches.empty()) {
std::vector<T> batch = std::move(thread_state.batches.back()); std::vector<T> batch = std::move(thread_state.batches.back());
thread_state.batches.pop_back(); thread_state.batches.pop_back();
@ -638,7 +638,7 @@ public:
// num_waiters and wait, so num_waiters == num_threads() // num_waiters and wait, so num_waiters == num_threads()
// will become true. In single-threaded builds, num_threads() is 1, // will become true. In single-threaded builds, num_threads() is 1,
// so we always terminate on the first iteration. // so we always terminate on the first iteration.
UniqueLock<Mutex> lock(waiters_lock); UniqueLock lock(waiters_lock);
++num_waiters; ++num_waiters;
if (num_waiters == num_threads()) { if (num_waiters == num_threads()) {
waiters_cv.notify_all(); waiters_cv.notify_all();
@ -657,7 +657,7 @@ private:
for (int i = 1; i < num_threads(); i++) { for (int i = 1; i < num_threads(); i++) {
int other_thread_num = (thread.thread_num + i) % num_threads(); int other_thread_num = (thread.thread_num + i) % num_threads();
ThreadState &other_thread_state = thread_states[other_thread_num]; ThreadState &other_thread_state = thread_states[other_thread_num];
UniqueLock<Mutex> lock(other_thread_state.batches_lock); UniqueLock lock(other_thread_state.batches_lock);
if (!other_thread_state.batches.empty()) { if (!other_thread_state.batches.empty()) {
std::vector<T> batch = std::move(other_thread_state.batches.front()); std::vector<T> batch = std::move(other_thread_state.batches.front());
other_thread_state.batches.pop_front(); other_thread_state.batches.pop_front();