41 ,
size_(1ULL << log_size)
44 for (std::size_t i = 0; i <
size_; ++i) {
45 buffer_[i].store(T{}, std::memory_order_relaxed);
61 [[nodiscard]] std::size_t
size() const noexcept {
70 [[nodiscard]] T
get(std::int64_t index)
const noexcept {
71 return buffer_[index &
mask_].load(std::memory_order_relaxed);
79 void put(std::int64_t index, T value)
noexcept {
80 buffer_[index &
mask_].store(value, std::memory_order_relaxed);
91 for (std::int64_t i = top; i < bottom; ++i) {
92 new_array->put(i,
get(i));
166 delete array_.load(std::memory_order_relaxed);
185 std::int64_t b =
bottom_.load(std::memory_order_relaxed);
186 std::int64_t t =
top_.load(std::memory_order_acquire);
190 if (b - t >
static_cast<std::int64_t
>(a->
size()) - 1) {
196 array_.store(new_array, std::memory_order_release);
201 std::atomic_thread_fence(std::memory_order_release);
202 bottom_.store(b + 1, std::memory_order_relaxed);
214 [[nodiscard]] std::optional<T>
pop() {
215 std::int64_t b =
bottom_.load(std::memory_order_relaxed) - 1;
217 bottom_.store(b, std::memory_order_relaxed);
218 std::atomic_thread_fence(std::memory_order_seq_cst);
219 std::int64_t t =
top_.load(std::memory_order_relaxed);
226 if (!
top_.compare_exchange_strong(
228 std::memory_order_seq_cst,
229 std::memory_order_relaxed)) {
231 bottom_.store(b + 1, std::memory_order_relaxed);
234 bottom_.store(b + 1, std::memory_order_relaxed);
239 bottom_.store(b + 1, std::memory_order_relaxed);
253 [[nodiscard]] std::optional<T>
steal() {
254 std::int64_t t =
top_.load(std::memory_order_acquire);
255 std::atomic_thread_fence(std::memory_order_seq_cst);
256 std::int64_t b =
bottom_.load(std::memory_order_acquire);
263 if (!
top_.compare_exchange_strong(
265 std::memory_order_seq_cst,
266 std::memory_order_relaxed)) {
300 if (max_count == 0) {
304 std::int64_t t =
top_.load(std::memory_order_acquire);
305 std::atomic_thread_fence(std::memory_order_seq_cst);
306 std::int64_t b =
bottom_.load(std::memory_order_acquire);
314 std::int64_t available = b - t;
315 std::size_t to_steal = std::min(
317 static_cast<std::size_t
>(available)
321 std::int64_t new_top = t +
static_cast<std::int64_t
>(to_steal);
323 if (!
top_.compare_exchange_strong(
325 std::memory_order_seq_cst,
326 std::memory_order_relaxed)) {
338 for (std::int64_t i = t; i < new_top; ++i) {
352 [[nodiscard]]
bool empty() const noexcept {
353 std::int64_t b =
bottom_.load(std::memory_order_relaxed);
354 std::int64_t t =
top_.load(std::memory_order_relaxed);
364 [[nodiscard]] std::size_t
size() const noexcept {
365 std::int64_t b =
bottom_.load(std::memory_order_relaxed);
366 std::int64_t t =
top_.load(std::memory_order_relaxed);
367 std::int64_t diff = b - t;
368 return diff > 0 ?
static_cast<std::size_t
>(diff) : 0;
375 [[nodiscard]] std::size_t
capacity() const noexcept {
376 return array_.load(std::memory_order_relaxed)->size();
Dynamic circular array for work-stealing deque.
circular_array * grow(std::int64_t bottom, std::int64_t top) const
Create a new array with double the capacity, copying elements.
std::atomic< T > * buffer_
void put(std::int64_t index, T value) noexcept
Store element at index with relaxed memory ordering.
circular_array(std::size_t log_size)
Constructs a circular array with given capacity.
T get(std::int64_t index) const noexcept
Get element at index with relaxed memory ordering.
std::size_t size() const noexcept
Get the capacity of the array.
circular_array(const circular_array &)=delete
circular_array & operator=(const circular_array &)=delete
Lock-free work-stealing deque based on Chase-Lev algorithm.
static constexpr std::size_t CACHE_LINE_SIZE
std::atomic< std::int64_t > top_
std::atomic< std::int64_t > bottom_
work_stealing_deque(const work_stealing_deque &)=delete
std::size_t capacity() const noexcept
Get the capacity of the current array.
std::size_t size() const noexcept
Get approximate size of the deque.
~work_stealing_deque()
Destructor - cleans up the circular array.
work_stealing_deque & operator=(work_stealing_deque &&)=delete
static constexpr std::size_t LOG_INITIAL_SIZE
Default initial log capacity (2^LOG_INITIAL_SIZE = 32 elements)
bool empty() const noexcept
Check if the deque appears empty.
work_stealing_deque(std::size_t log_initial_size=LOG_INITIAL_SIZE)
Constructs an empty work-stealing deque.
std::vector< T > steal_batch(std::size_t max_count)
Steal multiple elements from the top of the deque (thief threads)
work_stealing_deque(work_stealing_deque &&)=delete
std::atomic< circular_array< T > * > array_
std::optional< T > steal()
Steal an element from the top of the deque (thief threads)
std::vector< circular_array< T > * > old_arrays_
void cleanup_old_arrays()
Clear all old arrays (for memory cleanup)
work_stealing_deque & operator=(const work_stealing_deque &)=delete
void push(T item)
Push an element onto the bottom of the deque (owner only)
std::optional< T > pop()
Pop an element from the bottom of the deque (owner only)
A template class representing either a value or an error.