84 struct implicit_producer;
85 struct implicit_producer_kvp;
86 struct implicit_producer_hash;
88 static constexpr uint32_t REFS_MASK = 0x7FFFFFFF;
89 static constexpr uint32_t SHOULD_BE_ON_FREELIST = 0x80000000;
94 free_list() noexcept = default;
96 free_list(free_list&& other) noexcept :
101 free_list(
const free_list&) =
delete;
102 free_list& operator=(
const free_list&) =
delete;
104 void swap(free_list& other)
noexcept {
105 block* tmp = free_list_head_.
load(memory_order_relaxed);
106 free_list_head_.
store(other.free_list_head_.load(memory_order_relaxed), memory_order_relaxed);
107 other.free_list_head_.store(tmp, memory_order_relaxed);
110 void add(block* node)
noexcept {
111 const uint32_t prev = node->free_list_refs_.fetch_add(SHOULD_BE_ON_FREELIST, memory_order_acq_rel);
113 this->add_knowing_refcount_is_zero(node);
117 block* try_get() noexcept {
118 auto head = free_list_head_.
load(memory_order_acquire);
119 while (head !=
nullptr) {
120 auto prev_head = head;
121 auto refs = head->free_list_refs_.load(memory_order_relaxed);
122 if ((refs & REFS_MASK) == 0 ||
123 !head->free_list_refs_.compare_exchange_strong(refs, refs + 1, memory_order_acquire)) {
124 head = free_list_head_.
load(memory_order_acquire);
127 auto next = head->free_list_next_.load(memory_order_relaxed);
129 head->free_list_refs_.fetch_sub(2, memory_order_release);
132 refs = prev_head->free_list_refs_.fetch_sub(1, memory_order_acq_rel);
133 if (refs == SHOULD_BE_ON_FREELIST + 1) {
134 this->add_knowing_refcount_is_zero(prev_head);
140 block* head_unsafe() const noexcept {
return free_list_head_.
load(memory_order_relaxed); }
143 void add_knowing_refcount_is_zero(block* node)
noexcept {
144 auto head = free_list_head_.
load(memory_order_relaxed);
146 node->free_list_next_.store(head, memory_order_relaxed);
147 node->free_list_refs_.store(1, memory_order_release);
149 if (node->free_list_refs_.fetch_add(SHOULD_BE_ON_FREELIST - 1, memory_order_acq_rel) == 1) {
159 block* next_{
nullptr};
160 atomic<size_t> elements_completely_dequeued_{0};
161 atomic<uint32_t> free_list_refs_{0};
162 atomic<block*> free_list_next_{
nullptr};
163 bool dynamically_allocated_{
true};
167 block() noexcept = default;
169 NEFORCE_NODISCARD T* at(const
size_t idx) noexcept {
170 return reinterpret_cast<T*
>(&elements_[idx & (BLOCK_SIZE - 1)]);
173 bool set_empty(
size_t )
noexcept {
174 const size_t prev = elements_completely_dequeued_.fetch_add(1, memory_order_acq_rel);
175 return prev == BLOCK_SIZE - 1;
178 void reset_empty() noexcept { elements_completely_dequeued_.store(0, memory_order_relaxed); }
180 NEFORCE_NODISCARD
bool is_empty() const noexcept {
181 if (elements_completely_dequeued_.load(memory_order_relaxed) == BLOCK_SIZE) {
189 static constexpr size_t INVALID_BLOCK_BASE = 1;
191 struct block_index_entry {
193 atomic<block*> value_;
195 block_index_entry() noexcept :
196 key_(INVALID_BLOCK_BASE),
200 struct block_index_header {
202 atomic<size_t> tail_;
203 block_index_entry* entries_;
204 block_index_entry** index_;
205 block_index_header* prev_;
208 struct implicit_producer_kvp {
209 atomic<thread::id> key_{inner::invalid_thread_id_zero};
210 implicit_producer* value_{
nullptr};
212 implicit_producer_kvp() noexcept = default;
215 struct implicit_producer_hash {
217 implicit_producer_kvp* entries_;
218 implicit_producer_hash* prev_;
221 struct implicit_producer {
222 implicit_producer* next_{
nullptr};
223 atomic<bool> inactive_{
false};
224 thread_exit_listener thread_exit_listener_;
226 atomic<size_t> tail_index_{0};
227 atomic<size_t> head_index_{0};
228 atomic<size_t> dequeue_optimistic_count_{0};
229 atomic<size_t> dequeue_overcommit_{0};
230 block* tail_block_{
nullptr};
231 atomic<block_index_header*> block_index_{
nullptr};
232 size_t next_block_index_capacity_{IMPLICIT_INITIAL_INDEX_SIZE};
234 lock_free_queue* parent_;
236 explicit implicit_producer(lock_free_queue* parent) :
241 ~implicit_producer() {
242 if (!inactive_.load(memory_order_relaxed)) {
243 thread_exit_notifier::unsubscribe(&thread_exit_listener_);
246 size_t index = head_index_.load(memory_order_relaxed);
247 const size_t tail = tail_index_.load(memory_order_relaxed);
249 const bool force_free_last = (index != tail);
251 while (index != tail) {
252 if ((index & (BLOCK_SIZE - 1)) == 0 || block ==
nullptr) {
253 if (block !=
nullptr) {
254 parent_->add_block_to_free_list(block);
256 auto* entry = get_block_index_entry_for_index(index);
257 block = entry->value_.load(memory_order_relaxed);
259 block->at(index)->~T();
263 if (tail_block_ !=
nullptr && (force_free_last || (tail & (BLOCK_SIZE - 1)) != 0)) {
264 parent_->add_block_to_free_list(tail_block_);
267 auto* local_index = block_index_.load(memory_order_relaxed);
268 if (local_index !=
nullptr) {
269 for (
size_t i = 0; i != local_index->capacity_; ++i) {
270 local_index->entries_[i].~block_index_entry();
273 auto prev = local_index->prev_;
274 delete[] local_index->index_;
275 local_index->~block_index_header();
276 ::operator
delete(local_index);
278 }
while (local_index !=
nullptr);
282 implicit_producer(
const implicit_producer&) =
delete;
283 implicit_producer& operator=(
const implicit_producer&) =
delete;
285 void enqueue(T&& element) {
286 size_t current_tail = tail_index_.load(memory_order_relaxed);
287 const size_t new_tail = current_tail + 1;
289 if ((current_tail & (BLOCK_SIZE - 1)) == 0) {
290 const size_t head = head_index_.load(memory_order_relaxed);
292 if (!inner::circular_less_than(head, current_tail + BLOCK_SIZE)) {
296 block_index_entry* idx_entry =
nullptr;
297 if (!insert_block_index_entry(idx_entry, current_tail)) {
301 auto* new_block = parent_->requisition_block();
302 if (new_block ==
nullptr) {
303 rewind_block_index_tail();
304 idx_entry->value_.store(
nullptr, memory_order_relaxed);
307 new_block->reset_empty();
309 new (new_block->at(current_tail)) T(
move(element));
311 idx_entry->value_.store(new_block, memory_order_relaxed);
312 tail_block_ = new_block;
314 new (tail_block_->at(current_tail)) T(
move(element));
317 tail_index_.store(new_tail, memory_order_release);
320 bool dequeue(T& element) {
321 size_t tail = tail_index_.load(memory_order_relaxed);
322 const size_t overcommit = dequeue_overcommit_.load(memory_order_relaxed);
324 if (!inner::circular_less_than(dequeue_optimistic_count_.load(memory_order_relaxed) - overcommit, tail)) {
330 const size_t my_count = dequeue_optimistic_count_.fetch_add(1, memory_order_relaxed);
331 tail = tail_index_.load(memory_order_acquire);
333 if (inner::circular_less_than(my_count - overcommit, tail)) {
334 size_t index = head_index_.fetch_add(1, memory_order_acq_rel);
336 auto* entry = get_block_index_entry_for_index(index);
337 auto*
block = entry->value_.load(memory_order_relaxed);
338 auto& el = *
block->at(index);
343 if (
block->set_empty(index)) {
345 entry->value_.store(
nullptr, memory_order_relaxed);
347 parent_->add_block_to_free_list(block);
353 dequeue_overcommit_.fetch_add(1, memory_order_release);
358 NEFORCE_NODISCARD
size_t size_approx()
const {
359 const size_t tail = tail_index_.load(memory_order_relaxed);
360 const size_t head = head_index_.load(memory_order_relaxed);
361 return inner::circular_less_than(head, tail) ? (tail - head) : 0;
365 bool new_block_index() {
366 auto prev = block_index_.load(memory_order_relaxed);
367 size_t prev_capacity = (
prev ==
nullptr) ? 0 :
prev->capacity_;
368 size_t entry_count = (
prev ==
nullptr) ? next_block_index_capacity_ : prev_capacity;
370 const size_t alloc_size =
sizeof(block_index_header) + inner::align_for<block_index_entry>(
nullptr) -
371 static_cast<char*
>(
nullptr) +
sizeof(block_index_entry) * entry_count;
372 void* raw = ::operator
new(alloc_size);
373 if (raw ==
nullptr) {
377 auto* header =
new (raw) block_index_header;
378 auto* entries =
reinterpret_cast<block_index_entry*
>(
379 inner::align_for<block_index_entry>(
static_cast<char*
>(raw) +
sizeof(block_index_header)));
381 auto** index =
new block_index_entry*[entry_count];
382 if (prev !=
nullptr) {
383 size_t prev_tail =
prev->tail_.load(memory_order_relaxed);
384 size_t prev_pos = prev_tail;
387 prev_pos = (prev_pos + 1) & (
prev->capacity_ - 1);
388 index[i++] =
prev->index_[prev_pos];
389 }
while (prev_pos != prev_tail);
390 NEFORCE_DEBUG_VERIFY(i == prev_capacity,
"i == prev_capacity failed in lock-free queue");
393 for (
size_t i = prev_capacity; i < entry_count; ++i) {
394 new (entries + i) block_index_entry;
395 entries[i].key_.store(INVALID_BLOCK_BASE, memory_order_relaxed);
396 index[i] = entries + i;
399 header->prev_ =
prev;
400 header->entries_ = entries;
401 header->index_ = index;
402 header->capacity_ = entry_count;
403 header->tail_.store((prev_capacity - 1) & (entry_count - 1), memory_order_relaxed);
405 block_index_.store(header, memory_order_release);
406 next_block_index_capacity_ <<= 1;
410 bool insert_block_index_entry(block_index_entry*& idx_entry,
size_t block_start_index) {
411 auto* local_index = block_index_.load(memory_order_relaxed);
412 if (local_index ==
nullptr) {
416 size_t new_tail = (local_index->tail_.load(memory_order_relaxed) + 1) & (local_index->capacity_ - 1);
417 idx_entry = local_index->index_[new_tail];
419 if (idx_entry->key_.load(memory_order_relaxed) == INVALID_BLOCK_BASE ||
420 idx_entry->value_.load(memory_order_relaxed) ==
nullptr) {
421 idx_entry->key_.store(block_start_index, memory_order_relaxed);
422 local_index->tail_.store(new_tail, memory_order_release);
426 if (!new_block_index()) {
430 local_index = block_index_.load(memory_order_relaxed);
431 new_tail = (local_index->tail_.load(memory_order_relaxed) + 1) & (local_index->capacity_ - 1);
432 idx_entry = local_index->index_[new_tail];
433 idx_entry->key_.store(block_start_index, memory_order_relaxed);
434 local_index->tail_.store(new_tail, memory_order_release);
438 block_index_entry* get_block_index_entry_for_index(
size_t index)
const {
439 const size_t block_base = index & ~(BLOCK_SIZE - 1);
441 auto* local_index = block_index_.load(memory_order_acquire);
442 size_t tail = local_index->tail_.load(memory_order_acquire);
443 const size_t tail_base = local_index->index_[tail]->key_.load(memory_order_relaxed);
447 size_t idx = (tail + offset) & (local_index->capacity_ - 1);
448 return local_index->index_[idx];
451 void rewind_block_index_tail() {
452 auto* local_index = block_index_.load(memory_order_relaxed);
453 local_index->tail_.store((local_index->tail_.load(memory_order_relaxed) - 1) & (local_index->capacity_ - 1),
454 memory_order_relaxed);
458 atomic<implicit_producer*> producer_list_tail_{
nullptr};
459 atomic<uint32_t> producer_count_{0};
461 atomic<implicit_producer_hash*> implicit_producer_hash_;
462 atomic<size_t> implicit_producer_hash_count_;
463 atomic_flag implicit_producer_hash_resize_in_progress_;
464 implicit_producer_hash initial_implicit_producer_hash_;
465 implicit_producer_kvp initial_implicit_producer_hash_entries_[INITIAL_IMPLICIT_PRODUCER_HASH_SIZE];
467 free_list free_list_;
468 atomic<size_t> initial_block_pool_index_{0};
469 block* initial_block_pool_{
nullptr};
470 size_t initial_block_pool_size_{0};
472 void populate_initial_implicit_producer_hash() {
473 implicit_producer_hash_count_.store(0, memory_order_relaxed);
474 auto* hash = &initial_implicit_producer_hash_;
475 hash->capacity_ = INITIAL_IMPLICIT_PRODUCER_HASH_SIZE;
476 hash->entries_ = initial_implicit_producer_hash_entries_;
477 for (
size_t i = 0; i != INITIAL_IMPLICIT_PRODUCER_HASH_SIZE; ++i) {
478 initial_implicit_producer_hash_entries_[i].key_.store(inner::invalid_thread_id_zero, memory_order_relaxed);
480 hash->prev_ =
nullptr;
481 implicit_producer_hash_.store(hash, memory_order_relaxed);
484 void populate_initial_block_list(
size_t block_count) {
485 initial_block_pool_size_ = block_count;
486 if (block_count == 0) {
487 initial_block_pool_ =
nullptr;
490 initial_block_pool_ =
new block[block_count];
491 for (
size_t i = 0; i < block_count; ++i) {
492 initial_block_pool_[i].dynamically_allocated_ =
false;
496 block* try_get_block_from_initial_pool() {
497 if (initial_block_pool_index_.load(memory_order_relaxed) >= initial_block_pool_size_) {
500 auto index = initial_block_pool_index_.fetch_add(1, memory_order_relaxed);
501 return index < initial_block_pool_size_ ? initial_block_pool_ + index :
nullptr;
504 block* try_get_block_from_free_list() {
return free_list_.try_get(); }
506 block* requisition_block() {
507 auto* blk = try_get_block_from_initial_pool();
508 if (blk !=
nullptr) {
511 blk = try_get_block_from_free_list();
512 if (blk !=
nullptr) {
518 void add_block_to_free_list(block* block) { free_list_.add(block); }
520 void add_blocks_to_free_list(block* block) {
521 while (block !=
nullptr) {
523 add_block_to_free_list(block);
528 implicit_producer* recycle_or_create_producer() {
529 for (
auto ptr = producer_list_tail_.load(memory_order_acquire); ptr !=
nullptr; ptr = ptr->next_) {
530 if (ptr->inactive_.load(memory_order_relaxed)) {
531 bool expected =
true;
532 if (ptr->inactive_.compare_exchange_strong(expected,
false, memory_order_acquire,
533 memory_order_relaxed)) {
538 return add_producer(
new implicit_producer(
this));
541 implicit_producer* add_producer(implicit_producer* producer) {
542 if (producer ==
nullptr) {
545 producer_count_.fetch_add(1, memory_order_relaxed);
547 auto prev_tail = producer_list_tail_.load(memory_order_relaxed);
549 producer->next_ = prev_tail;
550 }
while (!producer_list_tail_.compare_exchange_weak(prev_tail, producer, memory_order_release,
551 memory_order_relaxed));
555 implicit_producer* get_or_add_implicit_producer() {
556 auto id = this_thread::id();
557 const auto hashed_id =
id.to_hash();
559 auto* main_hash = implicit_producer_hash_.load(memory_order_acquire);
560 for (
auto* hash = main_hash; hash !=
nullptr; hash = hash->prev_) {
561 auto index = hashed_id;
563 index &= hash->capacity_ - 1;
564 auto probed_key = hash->entries_[index].key_.load(memory_order_relaxed);
565 if (probed_key ==
id) {
566 auto* value = hash->entries_[index].value_;
567 if (hash != main_hash) {
570 index &= main_hash->capacity_ - 1;
571 auto empty = inner::invalid_thread_id_zero;
572#ifdef NEFORCE_PLATFORM_WINDOWS
573 auto reusable = inner::invalid_thread_id_max;
574 if (main_hash->entries_[index].key_.compare_exchange_strong(empty,
id, memory_order_seq_cst,
575 memory_order_relaxed) ||
576 main_hash->entries_[index].key_.compare_exchange_strong(
577 reusable,
id, memory_order_seq_cst, memory_order_relaxed)) {
579 if (main_hash->entries_[index].key_.compare_exchange_strong(empty,
id, memory_order_seq_cst,
580 memory_order_relaxed)) {
582 main_hash->entries_[index].value_ = value;
590 if (probed_key == inner::invalid_thread_id_zero) {
597 auto new_count = 1 + implicit_producer_hash_count_.fetch_add(1, memory_order_relaxed);
600 if (new_count >= (main_hash->capacity_ >> 1) &&
601 !implicit_producer_hash_resize_in_progress_.test_and_set(memory_order_acquire)) {
602 main_hash = implicit_producer_hash_.load(memory_order_acquire);
603 if (new_count >= (main_hash->capacity_ >> 1)) {
604 size_t new_capacity = main_hash->capacity_ << 1;
605 while (new_count >= (new_capacity >> 1)) {
609 const size_t alloc_size =
610 sizeof(implicit_producer_hash) + inner::align_for<implicit_producer_kvp>(
nullptr) -
611 static_cast<char*
>(
nullptr) +
sizeof(implicit_producer_kvp) * new_capacity;
612 void* raw = ::operator
new(alloc_size);
613 if (raw ==
nullptr) {
614 implicit_producer_hash_count_.fetch_sub(1, memory_order_relaxed);
615 implicit_producer_hash_resize_in_progress_.clear(memory_order_relaxed);
619 auto* new_hash =
new (raw) implicit_producer_hash;
620 new_hash->capacity_ = new_capacity;
622 reinterpret_cast<implicit_producer_kvp*
>(inner::align_for<implicit_producer_kvp>(
623 static_cast<char*
>(raw) +
sizeof(implicit_producer_hash)));
624 for (
size_t i = 0; i != new_capacity; ++i) {
625 new (&new_hash->entries_[i]) implicit_producer_kvp;
626 new_hash->entries_[i].key_.store(inner::invalid_thread_id_zero, memory_order_relaxed);
628 new_hash->prev_ = main_hash;
629 implicit_producer_hash_.store(new_hash, memory_order_release);
630 implicit_producer_hash_resize_in_progress_.clear(memory_order_release);
631 main_hash = new_hash;
633 implicit_producer_hash_resize_in_progress_.clear(memory_order_release);
637 if (new_count < (main_hash->capacity_ >> 1) + (main_hash->capacity_ >> 2)) {
638 auto* producer = recycle_or_create_producer();
639 if (producer ==
nullptr) {
640 implicit_producer_hash_count_.fetch_sub(1, memory_order_relaxed);
644 producer->thread_exit_listener_.callback = &lock_free_queue::implicit_producer_thread_exited_callback;
645 producer->thread_exit_listener_.user_data = producer;
646 thread_exit_notifier::subscribe(&producer->thread_exit_listener_);
648 auto index = hashed_id;
650 index &= main_hash->capacity_ - 1;
651 auto empty = inner::invalid_thread_id_zero;
652#ifdef NEFORCE_PLATFORM_WINDOWS
653 auto reusable = inner::invalid_thread_id_max;
654 if (main_hash->entries_[index].key_.compare_exchange_strong(reusable,
id, memory_order_seq_cst,
655 memory_order_relaxed)) {
656 implicit_producer_hash_count_.fetch_sub(1, memory_order_relaxed);
657 main_hash->entries_[index].value_ = producer;
661 if (main_hash->entries_[index].key_.compare_exchange_strong(empty,
id, memory_order_seq_cst,
662 memory_order_relaxed)) {
663 main_hash->entries_[index].value_ = producer;
671 main_hash = implicit_producer_hash_.load(memory_order_acquire);
675 void implicit_producer_thread_exited(implicit_producer* producer) {
676 auto* hash = implicit_producer_hash_.load(memory_order_acquire);
677 const auto id = this_thread::id();
678 const auto hashed_id =
id.to_hash();
680 for (; hash !=
nullptr; hash = hash->prev_) {
681 auto index = hashed_id;
682 thread::id probed_key;
684 index &= hash->capacity_ - 1;
686#ifdef NEFORCE_PLATFORM_WINDOWS
687 if (hash->entries_[index].key_.compare_exchange_strong(probed_key, inner::invalid_thread_id_max,
688 memory_order_seq_cst, memory_order_relaxed)) {
692 if (hash->entries_[index].key_.compare_exchange_strong(probed_key, inner::invalid_thread_id_zero,
693 memory_order_seq_cst, memory_order_relaxed)) {
698 }
while (probed_key != inner::invalid_thread_id_zero);
701 producer->inactive_.store(
true, memory_order_release);
704 static void implicit_producer_thread_exited_callback(
void* user_data) {
705 auto* producer =
static_cast<implicit_producer*
>(user_data);
706 producer->parent_->implicit_producer_thread_exited(producer);
709 size_t ceil_to_pow_2_size_t(
size_t x) {
return inner::ceil_to_pow_2(x); }
721 populate_initial_implicit_producer_hash();
722 populate_initial_block_list(capacity /
BLOCK_SIZE + ((capacity & (
BLOCK_SIZE - 1)) == 0 ? 0 : 1));
734 while (ptr !=
nullptr) {
735 auto next = ptr->next_;
741 while (
hash !=
nullptr) {
743 if (
prev !=
nullptr) {
744 for (
size_t i = 0; i !=
hash->capacity_; ++i) {
745 hash->entries_[i].~implicit_producer_kvp();
747 hash->~implicit_producer_hash();
748 ::operator
delete(
hash);
753 auto* block = free_list_.head_unsafe();
754 while (block !=
nullptr) {
756 if (block->dynamically_allocated_) {
762 delete[] initial_block_pool_;
777 auto* producer = get_or_add_implicit_producer();
778 if (producer !=
nullptr) {
779 producer->enqueue(_NEFORCE
move(new_value));
792 size_t non_empty_count = 0;
793 implicit_producer* best =
nullptr;
794 size_t best_size = 0;
796 for (
auto ptr = producer_list_tail_.load(
memory_order_acquire); ptr !=
nullptr && non_empty_count < 3;
798 auto sz = ptr->size_approx();
800 if (sz > best_size) {
808 if (non_empty_count > 0) {
809 if (best->dequeue(element)) {
812 for (
auto ptr = producer_list_tail_.load(
memory_order_acquire); ptr !=
nullptr; ptr = ptr->next_) {
813 if (ptr != best && ptr->dequeue(element)) {
841 NEFORCE_NODISCARD
bool empty()
const {
842 for (
auto ptr = producer_list_tail_.load(
memory_order_acquire); ptr !=
nullptr; ptr = ptr->next_) {
843 if (ptr->size_approx() > 0) {
855 NEFORCE_NODISCARD
size_t size()
const {
857 for (
auto ptr = producer_list_tail_.load(
memory_order_acquire); ptr !=
nullptr; ptr = ptr->next_) {
858 total += ptr->size_approx();