96template <
typename T,
size_t Capacity = 32>
97#if DISPENSO_HAS_CONCEPTS
98 requires std::is_trivially_copyable_v<T>
101 static_assert(Capacity >= 1,
"ChaseLevDeque capacity must be at least 1");
102 static_assert((Capacity & (Capacity - 1)) == 0,
"ChaseLevDeque capacity must be a power of two");
103#if !DISPENSO_HAS_CONCEPTS
105 std::is_trivially_copyable<T>::value,
106 "ChaseLevDeque requires a trivially copyable element type. "
107 "Wrap move-only payloads in a raw pointer or POD container.");
111 using value_type = T;
112 using size_type = size_t;
148 const int64_t b = bottom_.load(std::memory_order_relaxed);
149 const int64_t t = top_.load(std::memory_order_acquire);
150 if (b - t >=
static_cast<int64_t
>(Capacity)) {
157 DISPENSO_TSAN_ANNOTATE_IGNORE_WRITES_BEGIN();
159 DISPENSO_TSAN_ANNOTATE_IGNORE_WRITES_END();
162 bottom_.store(b + 1, std::memory_order_release);
177 const int64_t b = bottom_.load(std::memory_order_relaxed) - 1;
178 bottom_.store(b, std::memory_order_relaxed);
180 std::atomic_thread_fence(std::memory_order_seq_cst);
181 int64_t t = top_.load(std::memory_order_relaxed);
185 bottom_.store(b + 1, std::memory_order_relaxed);
196 bottom_.store(b + 1, std::memory_order_relaxed);
197 return top_.compare_exchange_strong(
198 t, t + 1, std::memory_order_seq_cst, std::memory_order_relaxed);
209 const int64_t b = bottom_.load(std::memory_order_relaxed) - 1;
210 bottom_.store(b, std::memory_order_relaxed);
211 std::atomic_thread_fence(std::memory_order_seq_cst);
212 int64_t t = top_.load(std::memory_order_relaxed);
215 bottom_.store(b + 1, std::memory_order_relaxed);
219 std::memcpy(storage, slotPtr(b),
sizeof(T));
222 bottom_.store(b + 1, std::memory_order_relaxed);
223 alignas(T)
char tmp[
sizeof(T)];
224 std::memcpy(tmp, slotPtr(b),
sizeof(T));
225 if (!top_.compare_exchange_strong(
226 t, t + 1, std::memory_order_seq_cst, std::memory_order_relaxed)) {
229 std::memcpy(storage, tmp,
sizeof(T));
248 int64_t t = top_.load(std::memory_order_acquire);
251 std::atomic_thread_fence(std::memory_order_seq_cst);
252 const int64_t b = bottom_.load(std::memory_order_acquire);
264 DISPENSO_TSAN_ANNOTATE_IGNORE_READS_BEGIN();
266 DISPENSO_TSAN_ANNOTATE_IGNORE_READS_END();
267 return top_.compare_exchange_strong(
268 t, t + 1, std::memory_order_seq_cst, std::memory_order_relaxed);
276 int64_t t = top_.load(std::memory_order_acquire);
277 std::atomic_thread_fence(std::memory_order_seq_cst);
278 const int64_t b = bottom_.load(std::memory_order_acquire);
284 alignas(T)
char tmp[
sizeof(T)];
285 DISPENSO_TSAN_ANNOTATE_IGNORE_READS_BEGIN();
286 std::memcpy(tmp, slotPtr(t),
sizeof(T));
287 DISPENSO_TSAN_ANNOTATE_IGNORE_READS_END();
288 if (!top_.compare_exchange_strong(
289 t, t + 1, std::memory_order_seq_cst, std::memory_order_relaxed)) {
292 std::memcpy(storage, tmp,
sizeof(T));
302 const int64_t b = bottom_.load(std::memory_order_acquire);
303 const int64_t t = top_.load(std::memory_order_acquire);
309 const int64_t b = bottom_.load(std::memory_order_acquire);
310 const int64_t t = top_.load(std::memory_order_acquire);
311 return b > t ?
static_cast<size_type
>(b - t) : 0;
320 static constexpr size_t kMask = Capacity - 1;
321 static constexpr size_t kStorageAlign =
324 T* slotPtr(int64_t index) {
325 return reinterpret_cast<T*
>(&storage_[(
static_cast<size_t>(index) & kMask) *
sizeof(T)]);
331 alignas(kCacheLineSize) std::atomic<int64_t> top_{0};
337 alignas(kStorageAlign)
char storage_[
sizeof(T) * Capacity];
A lock-free SPMC bounded work-stealing deque.
bool try_pop(T &out)
Pops the most recently pushed element (LIFO, owner-only).
bool try_steal_into(T *storage)
Steals the oldest element into uninitialized storage (FIFO).
bool try_pop_into(T *storage)
Pops the most recently pushed element into uninitialized storage (LIFO, owner-only)....
static constexpr size_type capacity() noexcept
Returns the maximum number of elements the deque can hold.
bool empty() const
Returns true if the deque appears empty.
~ChaseLevDeque()=default
Destroys the deque. Trivially-copyable elements need no explicit teardown.
bool try_steal(T &out)
Steals the oldest element from the top of the deque (FIFO).
ChaseLevDeque()=default
Constructs an empty deque. Storage is uninitialized; elements are constructed in-place when pushed.
size_type size() const
Returns the apparent number of elements in the deque.
bool try_push(const T &item)
Pushes an element onto the bottom of the deque (owner-only).