aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile1
-rw-r--r--latch.h41
-rw-r--r--test/latch.cc37
3 files changed, 79 insertions, 0 deletions
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 <condition_variable>
+#include <mutex>
+
+#include <cassert>
+
+/// 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<std::mutex> 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 <latch.h>
+
+#include <chrono>
+#include <thread>
+#include <vector>
+
+#include <cassert>
+#include <cstdio>
+
+constexpr unsigned kNumThreads = 16;
+
+int main() {
+ latch gate(kNumThreads);
+
+ std::vector<std::thread> 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;
+}