From 7a91597392c64ec8bd2b3cdfc44df98b928f41d3 Mon Sep 17 00:00:00 2001 From: Johannes Stoelp Date: Wed, 8 Nov 2023 00:14:42 +0100 Subject: latch: add simple latch based on a mutex and cv --- Makefile | 1 + latch.h | 41 +++++++++++++++++++++++++++++++++++++++++ test/latch.cc | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+) create mode 100644 latch.h create mode 100644 test/latch.cc diff --git a/Makefile b/Makefile index 9b14354..fe046c9 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,7 @@ TEST += option TEST += timer TEST += log TEST += owning_mutex +TEST += latch # -- INTERNALS ----------------------------------------------------------------- diff --git a/latch.h b/latch.h new file mode 100644 index 0000000..ec2006e --- /dev/null +++ b/latch.h @@ -0,0 +1,41 @@ +#ifndef UTILS_TIMER_H +#define UTILS_TIMER_H + +#include +#include + +#include + +/// latch +/// +/// A simple one-shot latch based on a mutex and condition variable. +/// +/// The latch is initialized with a number N of threads to wait for. Different +/// threads can call arrive_and_wait() on the latch. Those threads enter a wait +/// state until the Nth thread calls arrive_and_wait(), which then releases all +/// threads at the same time. +struct latch { + explicit latch(std::size_t cnt) : m_cnt{cnt} {} + ~latch() { + assert(m_cnt == 0); + } + + void arrive_and_wait() { + std::unique_lock lk(m_mtx); + m_cnt -= 1; + + if (m_cnt != 0) { + m_cv.wait(lk, [this]() { return m_cnt == 0; }); + } else { + m_cv.notify_all(); + } + } + + private: + std::size_t m_cnt; + + std::mutex m_mtx; + std::condition_variable m_cv; +}; + +#endif diff --git a/test/latch.cc b/test/latch.cc new file mode 100644 index 0000000..567e3db --- /dev/null +++ b/test/latch.cc @@ -0,0 +1,37 @@ +#include + +#include +#include +#include + +#include +#include + +constexpr unsigned kNumThreads = 16; + +int main() { + latch gate(kNumThreads); + + std::vector threads; + threads.reserve(kNumThreads); + + for (unsigned t = 0; t < kNumThreads; ++t) { + threads.emplace_back([&gate, t]() { + if (t % 2 == 0) { + unsigned sec = t / 2; + std::printf("th%02u sleep %us\n", t, sec); + std::this_thread::sleep_for(std::chrono::seconds(sec)); + } + + std::printf("th%02u at gate\n", t); + gate.arrive_and_wait(); + std::printf("th%02u finished\n", t); + }); + } + + for (auto& th : threads) { + th.join(); + } + + return 0; +} -- cgit v1.2.3