11#ifndef EIGEN_THREADPOOL_RUNQUEUE_H
12#define EIGEN_THREADPOOL_RUNQUEUE_H
15#include "./InternalHeaderCheck.h"
41template <
typename Work,
unsigned kSize>
44 RunQueue() : front_(0), back_(0) {
46 eigen_plain_assert((kSize & (kSize - 1)) == 0);
47 eigen_plain_assert(kSize > 2);
48 eigen_plain_assert(kSize <= (64 << 10));
49 for (
unsigned i = 0; i < kSize; i++) array_[i].state.store(kEmpty, std::memory_order_relaxed);
52 ~RunQueue() { eigen_plain_assert(Size() == 0); }
56 Work PushFront(Work w) {
57 unsigned front = front_.load(std::memory_order_relaxed);
58 Elem* e = &array_[front & kMask];
59 uint8_t s = e->state.load(std::memory_order_relaxed);
60 if (s != kEmpty || !e->state.compare_exchange_strong(s, kBusy, std::memory_order_acquire))
return w;
61 front_.store(front + 1 + (kSize << 1), std::memory_order_release);
63 e->state.store(kReady, std::memory_order_release);
70 unsigned front = front_.load(std::memory_order_relaxed);
71 Elem* e = &array_[(front - 1) & kMask];
72 uint8_t s = e->state.load(std::memory_order_relaxed);
73 if (s != kReady || !e->state.compare_exchange_strong(s, kBusy, std::memory_order_acquire))
return Work();
74 Work w = std::move(e->w);
75 e->state.store(kEmpty, std::memory_order_release);
76 front = ((front - 1) & kMask2) | (front & ~kMask2);
77 front_.store(front, std::memory_order_release);
83 Work PushBack(Work w) {
84 EIGEN_MUTEX_LOCK lock(mutex_);
85 unsigned back = back_.load(std::memory_order_relaxed);
86 Elem* e = &array_[(back - 1) & kMask];
87 uint8_t s = e->state.load(std::memory_order_relaxed);
88 if (s != kEmpty || !e->state.compare_exchange_strong(s, kBusy, std::memory_order_acquire))
return w;
89 back = ((back - 1) & kMask2) | (back & ~kMask2);
90 back_.store(back, std::memory_order_release);
92 e->state.store(kReady, std::memory_order_release);
98 if (Empty())
return Work();
99 EIGEN_MUTEX_LOCK lock(mutex_);
100 unsigned back = back_.load(std::memory_order_relaxed);
101 Elem* e = &array_[back & kMask];
102 uint8_t s = e->state.load(std::memory_order_relaxed);
103 if (s != kReady || !e->state.compare_exchange_strong(s, kBusy, std::memory_order_acquire))
return Work();
104 Work w = std::move(e->w);
105 e->state.store(kEmpty, std::memory_order_release);
106 back_.store(back + 1 + (kSize << 1), std::memory_order_release);
112 unsigned PopBackHalf(std::vector<Work>* result) {
113 if (Empty())
return 0;
114 EIGEN_MUTEX_LOCK lock(mutex_);
115 unsigned back = back_.load(std::memory_order_relaxed);
116 unsigned size = Size();
118 if (size > 1) mid = back + (size - 1) / 2;
121 for (;
static_cast<int>(mid - back) >= 0; mid--) {
122 Elem* e = &array_[mid & kMask];
123 uint8_t s = e->state.load(std::memory_order_relaxed);
125 if (s != kReady || !e->state.compare_exchange_strong(s, kBusy, std::memory_order_acquire))
continue;
130 eigen_plain_assert(s == kReady);
132 result->push_back(std::move(e->w));
133 e->state.store(kEmpty, std::memory_order_release);
136 if (n != 0) back_.store(start + 1 + (kSize << 1), std::memory_order_release);
142 unsigned Size()
const {
return SizeOrNotEmpty<true>(); }
146 bool Empty()
const {
return SizeOrNotEmpty<false>() == 0; }
156 static const unsigned kMask = kSize - 1;
157 static const unsigned kMask2 = (kSize << 1) - 1;
166 std::atomic<uint8_t> state;
177 EIGEN_ALIGN_TO_AVOID_FALSE_SHARING std::atomic<unsigned> front_;
178 EIGEN_ALIGN_TO_AVOID_FALSE_SHARING std::atomic<unsigned> back_;
181 EIGEN_ALIGN_TO_AVOID_FALSE_SHARING Elem array_[kSize];
186 template <
bool NeedSizeEstimate>
187 unsigned SizeOrNotEmpty()
const {
190 unsigned front = front_.load(std::memory_order_acquire);
193 unsigned back = back_.load(std::memory_order_acquire);
194 unsigned front1 = front_.load(std::memory_order_relaxed);
195 if (front != front1) {
197 std::atomic_thread_fence(std::memory_order_acquire);
200 EIGEN_IF_CONSTEXPR (NeedSizeEstimate) {
201 return CalculateSize(front, back);
204 unsigned maybe_zero = ((front ^ back) & kMask2);
207 eigen_assert((CalculateSize(front, back) == 0) == (maybe_zero == 0));
213 EIGEN_ALWAYS_INLINE
unsigned CalculateSize(
unsigned front,
unsigned back)
const {
214 int size = (front & kMask2) - (back & kMask2);
216 if (EIGEN_PREDICT_FALSE(size < 0)) size += 2 * kSize;
221 if (EIGEN_PREDICT_FALSE(size >
static_cast<int>(kSize))) size = kSize;
222 return static_cast<unsigned>(size);
225 RunQueue(
const RunQueue&) =
delete;
226 void operator=(
const RunQueue&) =
delete;