1// Modeled after the java cyclic barrier.
2// Allows n threads to synchronize.
3// Call Break() and join your threads before this object goes out of scope
4#pragma once
5
6#include <mutex>
7#include <condition_variable>
8
9
10class CyclicBarrier
11{
12public:
13 explicit CyclicBarrier(unsigned numThreads)
14 : m_numThreads(numThreads)
15 , m_counts{ 0, 0 }
16 , m_index(0)
17 , m_disabled(false)
18 { }
19
20 CyclicBarrier(const CyclicBarrier&) = delete;
21 CyclicBarrier(CyclicBarrier &&) = delete;
22 CyclicBarrier & operator=(const CyclicBarrier&) = delete;
23 CyclicBarrier & operator=(CyclicBarrier &&) = delete;
24
25 // sync point
26 void Await()
27 {
28 std::unique_lock<std::mutex> lock(m_requestsLock);
29 if (m_disabled)
30 return;
31
32 unsigned currentIndex = m_index;
33 ++m_counts[currentIndex];
34
35 // "spurious wakeup" means this thread could wake up even if no one called m_condition.notify!
36 if (m_counts[currentIndex] < m_numThreads)
37 {
38 while (m_counts[currentIndex] < m_numThreads)
39 m_condition.wait(lock);
40 }
41 else
42 {
43 m_index ^= 1; // flip index
44 m_counts[m_index] = 0;
45 m_condition.notify_all();
46 }
47 }
48
49 // Call this to free current sleeping threads and prevent any further awaits.
50 // After calling this, the object is no longer usable.
51 void Break()
52 {
53 std::unique_lock<std::mutex> lock(m_requestsLock);
54 m_disabled = true;
55 m_counts[0] = m_numThreads;
56 m_counts[1] = m_numThreads;
57 m_condition.notify_all();
58 }
59
60private:
61 std::mutex m_requestsLock;
62 std::condition_variable m_condition;
63 const unsigned m_numThreads;
64 unsigned m_counts[2];
65 unsigned m_index;
66 bool m_disabled;
67};
68