11#ifndef EIGEN_THREADPOOL_EVENTCOUNT_H
12#define EIGEN_THREADPOOL_EVENTCOUNT_H
15#include "./InternalHeaderCheck.h"
57 EventCount(MaxSizeVector<Waiter>& waiters) : state_(kStackMask), waiters_(waiters) {
58 eigen_plain_assert(waiters.size() < (1 << kWaiterBits) - 1);
61 EventCount(
const EventCount&) =
delete;
62 void operator=(
const EventCount&) =
delete;
66 eigen_plain_assert(state_.load() == kStackMask);
73 uint64_t state = state_.load(std::memory_order_relaxed);
76 uint64_t newstate = state + kWaiterInc;
78 if (state_.compare_exchange_weak(state, newstate, std::memory_order_seq_cst))
return;
83 void CommitWait(Waiter* w) {
84 eigen_plain_assert((w->epoch & ~kEpochMask) == 0);
85 w->state = Waiter::kNotSignaled;
86 const uint64_t me = (w - &waiters_[0]) | w->epoch;
87 uint64_t state = state_.load(std::memory_order_seq_cst);
89 CheckState(state,
true);
91 if ((state & kSignalMask) != 0) {
93 newstate = state - kWaiterInc - kSignalInc;
96 newstate = ((state & kWaiterMask) - kWaiterInc) | me;
97 w->next.store(state & (kStackMask | kEpochMask), std::memory_order_relaxed);
100 if (state_.compare_exchange_weak(state, newstate, std::memory_order_acq_rel)) {
101 if ((state & kSignalMask) == 0) {
102 w->epoch += kEpochInc;
112 uint64_t state = state_.load(std::memory_order_relaxed);
114 CheckState(state,
true);
115 uint64_t newstate = state - kWaiterInc;
121 const bool notify = (((state & kWaiterMask) >> kWaiterShift) == ((state & kSignalMask) >> kSignalShift));
123 newstate -= kSignalInc;
125 CheckState(newstate);
126 if (state_.compare_exchange_weak(state, newstate, std::memory_order_acq_rel)) {
137 void Notify(
bool notifyAll) {
138 std::atomic_thread_fence(std::memory_order_seq_cst);
139 uint64_t state = state_.load(std::memory_order_acquire);
142 const uint64_t waiters = (state & kWaiterMask) >> kWaiterShift;
143 const uint64_t signals = (state & kSignalMask) >> kSignalShift;
145 if ((state & kStackMask) == kStackMask && waiters == signals)
return;
149 newstate = (state & kWaiterMask) | (waiters << kSignalShift) | kStackMask;
150 }
else if (signals < waiters) {
152 newstate = state + kSignalInc;
155 Waiter* w = &waiters_[state & kStackMask];
156 uint64_t next = w->next.load(std::memory_order_relaxed);
157 newstate = (state & (kWaiterMask | kSignalMask)) | next;
159 CheckState(newstate);
160 if (state_.compare_exchange_weak(state, newstate, std::memory_order_acq_rel)) {
161 if (!notifyAll && (signals < waiters))
return;
162 if ((state & kStackMask) == kStackMask)
return;
163 Waiter* w = &waiters_[state & kStackMask];
164 if (!notifyAll) w->next.store(kStackMask, std::memory_order_relaxed);
180 static const uint64_t kWaiterBits = 14;
181 static const uint64_t kStackMask = (1ull << kWaiterBits) - 1;
182 static const uint64_t kWaiterShift = kWaiterBits;
183 static const uint64_t kWaiterMask = ((1ull << kWaiterBits) - 1) << kWaiterShift;
184 static const uint64_t kWaiterInc = 1ull << kWaiterShift;
185 static const uint64_t kSignalShift = 2 * kWaiterBits;
186 static const uint64_t kSignalMask = ((1ull << kWaiterBits) - 1) << kSignalShift;
187 static const uint64_t kSignalInc = 1ull << kSignalShift;
188 static const uint64_t kEpochShift = 3 * kWaiterBits;
189 static const uint64_t kEpochBits = 64 - kEpochShift;
190 static const uint64_t kEpochMask = ((1ull << kEpochBits) - 1) << kEpochShift;
191 static const uint64_t kEpochInc = 1ull << kEpochShift;
195 friend class EventCount;
203 EIGEN_ALIGN_TO_AVOID_FALSE_SHARING std::atomic<uint64_t> next{kStackMask};
207 unsigned state{kNotSignaled};
211 static void CheckState(uint64_t state,
bool waiter =
false) {
212 static_assert(kEpochBits >= 20,
"not enough bits to prevent ABA problem");
213 const uint64_t waiters = (state & kWaiterMask) >> kWaiterShift;
214 const uint64_t signals = (state & kSignalMask) >> kSignalShift;
215 eigen_plain_assert(waiters >= signals);
216 eigen_plain_assert(waiters < (1 << kWaiterBits) - 1);
217 eigen_plain_assert(!waiter || waiters > 0);
223 void Park(Waiter* w) {
224 EIGEN_MUTEX_LOCK lock(w->mu);
225 while (w->state != Waiter::kSignaled) {
226 w->state = Waiter::kWaiting;
231 void Unpark(Waiter* w) {
232 for (Waiter* next; w; w = next) {
233 uint64_t wnext = w->next.load(std::memory_order_relaxed) & kStackMask;
234 next = wnext == kStackMask ? nullptr : &waiters_[internal::convert_index<size_t>(wnext)];
237 EIGEN_MUTEX_LOCK lock(w->mu);
239 w->state = Waiter::kSignaled;
242 if (state == Waiter::kWaiting) w->cv.notify_one();
246 std::atomic<uint64_t> state_;
247 MaxSizeVector<Waiter>& waiters_;