NexusForce 1.0.0
A rigorously engineered full-stack C++ backend library.
载入中...
搜索中...
未找到
lock_free_queue.hpp
浏览该文件的文档.
1#ifndef NEFORCE_CORE_ASYNC_LOCK_FREE_QUEUE_HPP__
2#define NEFORCE_CORE_ASYNC_LOCK_FREE_QUEUE_HPP__
3
11
15#include "NeForce/core/async/thread_exit_notifier.hpp"
17NEFORCE_BEGIN_NAMESPACE__
18
20NEFORCE_BEGIN_INNER__
21
22NEFORCE_INLINE17 constexpr thread::id invalid_thread_id_zero{0};
23#ifdef NEFORCE_PLATFORM_WINDOWS
24NEFORCE_INLINE17 constexpr thread::id invalid_thread_id_max{static_cast<thread::id::native_id_type>(-1)};
25#endif
26
27template <typename T>
28constexpr char* align_for(char* ptr) noexcept {
29 constexpr size_t alignment = alignment_of_v<T>;
30 return ptr + (alignment - (reinterpret_cast<uintptr_t>(ptr) % alignment)) % alignment;
31}
32
33template <typename T>
34constexpr T ceil_to_pow_2(T x) noexcept {
35 --x;
36 x |= x >> 1;
37 x |= x >> 2;
38 x |= x >> 4;
39 for (size_t i = 1; i < sizeof(T); i <<= 1) {
40 x |= x >> (i << 3);
41 }
42 ++x;
43 return x;
44}
45
46constexpr bool circular_less_than(size_t a, size_t b) noexcept { return static_cast<ptrdiff_t>(a - b) < 0; }
47
48NEFORCE_END_INNER__
50
56
62
72template <typename T>
74public:
76 static constexpr size_t BLOCK_SIZE = 32;
78 static constexpr size_t INITIAL_IMPLICIT_PRODUCER_HASH_SIZE = 32;
80 static constexpr size_t IMPLICIT_INITIAL_INDEX_SIZE = 32;
81
82private:
83 struct block;
84 struct implicit_producer;
85 struct implicit_producer_kvp;
86 struct implicit_producer_hash;
87
88 static constexpr uint32_t REFS_MASK = 0x7FFFFFFF;
89 static constexpr uint32_t SHOULD_BE_ON_FREELIST = 0x80000000;
90
91 struct free_list {
92 atomic<block*> free_list_head_{nullptr};
93
94 free_list() noexcept = default;
95
96 free_list(free_list&& other) noexcept :
97 free_list_head_(other.free_list_head_.load(memory_order_relaxed)) {
98 other.free_list_head_.store(nullptr, memory_order_relaxed);
99 }
100
101 free_list(const free_list&) = delete;
102 free_list& operator=(const free_list&) = delete;
103
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);
108 }
109
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);
112 if (prev == 0) {
113 this->add_knowing_refcount_is_zero(node);
114 }
115 }
116
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);
125 continue;
126 }
127 auto next = head->free_list_next_.load(memory_order_relaxed);
128 if (free_list_head_.compare_exchange_strong(head, next, memory_order_acquire, memory_order_relaxed)) {
129 head->free_list_refs_.fetch_sub(2, memory_order_release);
130 return head;
131 }
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);
135 }
136 }
137 return nullptr;
138 }
139
140 block* head_unsafe() const noexcept { return free_list_head_.load(memory_order_relaxed); }
141
142 private:
143 void add_knowing_refcount_is_zero(block* node) noexcept {
144 auto head = free_list_head_.load(memory_order_relaxed);
145 while (true) {
146 node->free_list_next_.store(head, memory_order_relaxed);
147 node->free_list_refs_.store(1, memory_order_release);
148 if (!free_list_head_.compare_exchange_strong(head, node, memory_order_release, memory_order_relaxed)) {
149 if (node->free_list_refs_.fetch_add(SHOULD_BE_ON_FREELIST - 1, memory_order_acq_rel) == 1) {
150 continue;
151 }
152 }
153 return;
154 }
155 }
156 };
157
158 struct block {
159 block* next_{nullptr}; // 生产者 block 链
160 atomic<size_t> elements_completely_dequeued_{0}; // 已出队元素数
161 atomic<uint32_t> free_list_refs_{0}; // free_list 引用计数
162 atomic<block*> free_list_next_{nullptr}; // free_list 链接
163 bool dynamically_allocated_{true}; // 是否堆分配
164
165 aligned_storage_t<sizeof(T), alignof(T)> elements_[BLOCK_SIZE];
166
167 block() noexcept = default;
168
169 NEFORCE_NODISCARD T* at(const size_t idx) noexcept {
170 return reinterpret_cast<T*>(&elements_[idx & (BLOCK_SIZE - 1)]);
171 }
172
173 bool set_empty(size_t /*i*/) noexcept {
174 const size_t prev = elements_completely_dequeued_.fetch_add(1, memory_order_acq_rel);
175 return prev == BLOCK_SIZE - 1;
176 }
177
178 void reset_empty() noexcept { elements_completely_dequeued_.store(0, memory_order_relaxed); }
179
180 NEFORCE_NODISCARD bool is_empty() const noexcept {
181 if (elements_completely_dequeued_.load(memory_order_relaxed) == BLOCK_SIZE) {
182 atomic_thread_fence(memory_order_acquire);
183 return true;
184 }
185 return false;
186 }
187 };
188
189 static constexpr size_t INVALID_BLOCK_BASE = 1;
190
191 struct block_index_entry {
192 atomic<size_t> key_;
193 atomic<block*> value_;
194
195 block_index_entry() noexcept :
196 key_(INVALID_BLOCK_BASE),
197 value_(nullptr) {}
198 };
199
200 struct block_index_header {
201 size_t capacity_;
202 atomic<size_t> tail_;
203 block_index_entry* entries_;
204 block_index_entry** index_;
205 block_index_header* prev_;
206 };
207
208 struct implicit_producer_kvp {
209 atomic<thread::id> key_{inner::invalid_thread_id_zero};
210 implicit_producer* value_{nullptr};
211
212 implicit_producer_kvp() noexcept = default;
213 };
214
215 struct implicit_producer_hash {
216 size_t capacity_;
217 implicit_producer_kvp* entries_;
218 implicit_producer_hash* prev_;
219 };
220
221 struct implicit_producer {
222 implicit_producer* next_{nullptr};
223 atomic<bool> inactive_{false};
224 thread_exit_listener thread_exit_listener_;
225
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};
233
234 lock_free_queue* parent_;
235
236 explicit implicit_producer(lock_free_queue* parent) :
237 parent_(parent) {
238 new_block_index();
239 }
240
241 ~implicit_producer() {
242 if (!inactive_.load(memory_order_relaxed)) {
243 thread_exit_notifier::unsubscribe(&thread_exit_listener_);
244 }
245
246 size_t index = head_index_.load(memory_order_relaxed);
247 const size_t tail = tail_index_.load(memory_order_relaxed);
248 block* block = nullptr;
249 const bool force_free_last = (index != tail);
250
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);
255 }
256 auto* entry = get_block_index_entry_for_index(index);
257 block = entry->value_.load(memory_order_relaxed);
258 }
259 block->at(index)->~T();
260 ++index;
261 }
262
263 if (tail_block_ != nullptr && (force_free_last || (tail & (BLOCK_SIZE - 1)) != 0)) {
264 parent_->add_block_to_free_list(tail_block_);
265 }
266
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();
271 }
272 do {
273 auto prev = local_index->prev_;
274 delete[] local_index->index_;
275 local_index->~block_index_header();
276 ::operator delete(local_index);
277 local_index = prev;
278 } while (local_index != nullptr);
279 }
280 }
281
282 implicit_producer(const implicit_producer&) = delete;
283 implicit_producer& operator=(const implicit_producer&) = delete;
284
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;
288
289 if ((current_tail & (BLOCK_SIZE - 1)) == 0) {
290 const size_t head = head_index_.load(memory_order_relaxed);
291
292 if (!inner::circular_less_than(head, current_tail + BLOCK_SIZE)) {
293 return;
294 }
295
296 block_index_entry* idx_entry = nullptr;
297 if (!insert_block_index_entry(idx_entry, current_tail)) {
298 return;
299 }
300
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);
305 return;
306 }
307 new_block->reset_empty();
308
309 new (new_block->at(current_tail)) T(move(element));
310
311 idx_entry->value_.store(new_block, memory_order_relaxed);
312 tail_block_ = new_block;
313 } else {
314 new (tail_block_->at(current_tail)) T(move(element));
315 }
316
317 tail_index_.store(new_tail, memory_order_release);
318 }
319
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);
323
324 if (!inner::circular_less_than(dequeue_optimistic_count_.load(memory_order_relaxed) - overcommit, tail)) {
325 return false;
326 }
327
328 atomic_thread_fence(memory_order_acquire);
329
330 const size_t my_count = dequeue_optimistic_count_.fetch_add(1, memory_order_relaxed);
331 tail = tail_index_.load(memory_order_acquire);
332
333 if (inner::circular_less_than(my_count - overcommit, tail)) {
334 size_t index = head_index_.fetch_add(1, memory_order_acq_rel);
335
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);
339
340 element = move(el);
341 el.~T();
342
343 if (block->set_empty(index)) {
344 {
345 entry->value_.store(nullptr, memory_order_relaxed);
346 }
347 parent_->add_block_to_free_list(block);
348 }
349
350 return true;
351 }
352
353 dequeue_overcommit_.fetch_add(1, memory_order_release);
354
355 return false;
356 }
357
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;
362 }
363
364 private:
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;
369
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) {
374 return false;
375 }
376
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)));
380
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;
385 size_t i = 0;
386 do {
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");
391 }
392
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;
397 }
398
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);
404
405 block_index_.store(header, memory_order_release);
406 next_block_index_capacity_ <<= 1;
407 return true;
408 }
409
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) {
413 return false;
414 }
415
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];
418
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);
423 return true;
424 }
425
426 if (!new_block_index()) {
427 return false;
428 }
429
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);
435 return true;
436 }
437
438 block_index_entry* get_block_index_entry_for_index(size_t index) const {
439 const size_t block_base = index & ~(BLOCK_SIZE - 1);
440
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);
444
445 const ptrdiff_t offset =
446 static_cast<ptrdiff_t>(block_base - tail_base) / static_cast<ptrdiff_t>(BLOCK_SIZE);
447 size_t idx = (tail + offset) & (local_index->capacity_ - 1);
448 return local_index->index_[idx];
449 }
450
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);
455 }
456 };
457
458 atomic<implicit_producer*> producer_list_tail_{nullptr};
459 atomic<uint32_t> producer_count_{0};
460
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];
466
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};
471
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);
479 }
480 hash->prev_ = nullptr;
481 implicit_producer_hash_.store(hash, memory_order_relaxed);
482 }
483
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;
488 return;
489 }
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;
493 }
494 }
495
496 block* try_get_block_from_initial_pool() {
497 if (initial_block_pool_index_.load(memory_order_relaxed) >= initial_block_pool_size_) {
498 return nullptr;
499 }
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;
502 }
503
504 block* try_get_block_from_free_list() { return free_list_.try_get(); }
505
506 block* requisition_block() {
507 auto* blk = try_get_block_from_initial_pool();
508 if (blk != nullptr) {
509 return blk;
510 }
511 blk = try_get_block_from_free_list();
512 if (blk != nullptr) {
513 return blk;
514 }
515 return new block;
516 }
517
518 void add_block_to_free_list(block* block) { free_list_.add(block); }
519
520 void add_blocks_to_free_list(block* block) {
521 while (block != nullptr) {
522 auto* next = block->next_;
523 add_block_to_free_list(block);
524 block = next;
525 }
526 }
527
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)) {
534 return ptr;
535 }
536 }
537 }
538 return add_producer(new implicit_producer(this));
539 }
540
541 implicit_producer* add_producer(implicit_producer* producer) {
542 if (producer == nullptr) {
543 return nullptr;
544 }
545 producer_count_.fetch_add(1, memory_order_relaxed);
546
547 auto prev_tail = producer_list_tail_.load(memory_order_relaxed);
548 do {
549 producer->next_ = prev_tail;
550 } while (!producer_list_tail_.compare_exchange_weak(prev_tail, producer, memory_order_release,
551 memory_order_relaxed));
552 return producer;
553 }
554
555 implicit_producer* get_or_add_implicit_producer() {
556 auto id = this_thread::id();
557 const auto hashed_id = id.to_hash();
558
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;
562 while (true) {
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) {
568 index = hashed_id;
569 while (true) {
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)) {
578#else
579 if (main_hash->entries_[index].key_.compare_exchange_strong(empty, id, memory_order_seq_cst,
580 memory_order_relaxed)) {
581#endif
582 main_hash->entries_[index].value_ = value;
583 break;
584 }
585 ++index;
586 }
587 }
588 return value;
589 }
590 if (probed_key == inner::invalid_thread_id_zero) {
591 break;
592 }
593 ++index;
594 }
595 }
596
597 auto new_count = 1 + implicit_producer_hash_count_.fetch_add(1, memory_order_relaxed);
598 while (true) {
599 // NOLINTNEXTLINE(clang-analyzer-core.NullDereference)
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)) {
606 new_capacity <<= 1;
607 }
608
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);
616 return nullptr;
617 }
618
619 auto* new_hash = new (raw) implicit_producer_hash;
620 new_hash->capacity_ = new_capacity;
621 new_hash->entries_ =
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);
627 }
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;
632 } else {
633 implicit_producer_hash_resize_in_progress_.clear(memory_order_release);
634 }
635 }
636
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);
641 return nullptr;
642 }
643
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_);
647
648 auto index = hashed_id;
649 while (true) {
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;
658 break;
659 }
660#endif
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;
664 break;
665 }
666 ++index;
667 }
668 return producer;
669 }
670
671 main_hash = implicit_producer_hash_.load(memory_order_acquire);
672 }
673 }
674
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();
679
680 for (; hash != nullptr; hash = hash->prev_) {
681 auto index = hashed_id;
682 thread::id probed_key;
683 do {
684 index &= hash->capacity_ - 1;
685 probed_key = id;
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)) {
689 break;
690 }
691#else
692 if (hash->entries_[index].key_.compare_exchange_strong(probed_key, inner::invalid_thread_id_zero,
693 memory_order_seq_cst, memory_order_relaxed)) {
694 break;
695 }
696#endif
697 ++index;
698 } while (probed_key != inner::invalid_thread_id_zero);
699 }
700
701 producer->inactive_.store(true, memory_order_release);
702 }
703
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);
707 }
708
709 size_t ceil_to_pow_2_size_t(size_t x) { return inner::ceil_to_pow_2(x); }
710
711public:
719 explicit lock_free_queue(size_t capacity = 32 * BLOCK_SIZE) {
720 implicit_producer_hash_resize_in_progress_.clear(memory_order_relaxed);
721 populate_initial_implicit_producer_hash();
722 populate_initial_block_list(capacity / BLOCK_SIZE + ((capacity & (BLOCK_SIZE - 1)) == 0 ? 0 : 1));
723 }
724
733 auto ptr = producer_list_tail_.load(memory_order_relaxed);
734 while (ptr != nullptr) {
735 auto next = ptr->next_;
736 delete ptr;
737 ptr = next;
738 }
739
740 auto* hash = implicit_producer_hash_.load(memory_order_relaxed);
741 while (hash != nullptr) {
742 auto prev = hash->prev_;
743 if (prev != nullptr) {
744 for (size_t i = 0; i != hash->capacity_; ++i) {
745 hash->entries_[i].~implicit_producer_kvp();
746 }
747 hash->~implicit_producer_hash();
748 ::operator delete(hash);
749 }
750 hash = prev;
751 }
752
753 auto* block = free_list_.head_unsafe();
754 while (block != nullptr) {
755 auto next = block->free_list_next_.load(memory_order_relaxed);
756 if (block->dynamically_allocated_) {
757 delete block;
758 }
759 block = next;
760 }
761
762 delete[] initial_block_pool_;
763 }
764
765 lock_free_queue(const lock_free_queue&) = delete;
766 lock_free_queue& operator=(const lock_free_queue&) = delete;
767
776 void push(T new_value) {
777 auto* producer = get_or_add_implicit_producer();
778 if (producer != nullptr) {
779 producer->enqueue(_NEFORCE move(new_value));
780 }
781 }
782
791 T element;
792 size_t non_empty_count = 0;
793 implicit_producer* best = nullptr;
794 size_t best_size = 0;
795
796 for (auto ptr = producer_list_tail_.load(memory_order_acquire); ptr != nullptr && non_empty_count < 3;
797 ptr = ptr->next_) {
798 auto sz = ptr->size_approx();
799 if (sz > 0) {
800 if (sz > best_size) {
801 best_size = sz;
802 best = ptr;
803 }
804 ++non_empty_count;
805 }
806 }
807
808 if (non_empty_count > 0) {
809 if (best->dequeue(element)) {
810 return unique_ptr<T>(new T(_NEFORCE move(element)));
811 }
812 for (auto ptr = producer_list_tail_.load(memory_order_acquire); ptr != nullptr; ptr = ptr->next_) {
813 if (ptr != best && ptr->dequeue(element)) {
814 return unique_ptr<T>(new T(_NEFORCE move(element)));
815 }
816 }
817 }
818
819 return unique_ptr<T>();
820 }
821
827 for (;;) {
828 auto result = try_pop();
829 if (result) {
830 return result;
831 }
833 }
834 }
835
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) {
844 return false;
845 }
846 }
847 return true;
848 }
849
855 NEFORCE_NODISCARD size_t size() const {
856 size_t total = 0;
857 for (auto ptr = producer_list_tail_.load(memory_order_acquire); ptr != nullptr; ptr = ptr->next_) {
858 total += ptr->size_approx();
859 }
860 return total;
861 }
862
867 void clear() {
868 while (try_pop()) {
870 }
871 }
872};
873 // LockFreeQueue
875 // AsyncComponents
877
878NEFORCE_END_NAMESPACE__
879#endif
原子类型完整实现
void push(T new_value)
入队操作
lock_free_queue(size_t capacity=32 *BLOCK_SIZE)
构造函数
static constexpr size_t IMPLICIT_INITIAL_INDEX_SIZE
block 索引初始容量
unique_ptr< T > pop()
阻塞出队操作
unique_ptr< T > try_pop()
非阻塞出队操作
static constexpr size_t INITIAL_IMPLICIT_PRODUCER_HASH_SIZE
隐式生产者哈希表初始大小
static constexpr size_t BLOCK_SIZE
每个 block 存储的元素数量
bool empty() const
检查队列是否为空
size_t size() const
获取队列中元素的近似数量
独占智能指针
typename aligned_storage< Len, Align >::type aligned_storage_t
aligned_storage的便捷别名
void atomic_thread_fence(const memory_order mo) noexcept
线程内存屏障
unsigned int uint32_t
32位无符号整数类型
constexpr Iterator prev(Iterator iter, iter_difference_t< Iterator > n=1)
获取迭代器的前一个位置
constexpr Iterator next(Iterator iter, iter_difference_t< Iterator > n=1)
获取迭代器的后一个位置
@ block
阻塞等待直到队列有空间
constexpr auto memory_order_acquire
获取内存顺序常量
constexpr auto memory_order_relaxed
宽松内存顺序常量
uint64_t uintptr_t
可容纳指针的无符号整数类型
int64_t ptrdiff_t
指针差类型
constexpr Iterator2 move(Iterator1 first, Iterator1 last, Iterator2 result) noexcept(noexcept(inner::__move_aux(first, last, result)))
移动范围元素
thread::id id() noexcept
获取当前线程标识符
void relax() noexcept
线程放松
constexpr bool empty(const Container &cont) noexcept(noexcept(cont.empty()))
检查容器是否为空
互斥锁
通用原子类型模板
T load(const memory_order mo=memory_order_seq_cst) const noexcept
原子加载操作
bool compare_exchange_strong(T &expected, T desired, const memory_order success, const memory_order failure) noexcept
强比较交换操作
void store(T value, const memory_order mo=memory_order_seq_cst) noexcept
原子存储操作
哈希函数的主模板
线程管理类
独占智能指针