| 536 | #else |
| 537 | template<AllocationMode canAlloc, typename U> |
| 538 | bool inner_enqueue(U&& element) AE_NO_TSAN |
| 539 | #endif |
| 540 | { |
| 541 | #ifndef NDEBUG |
| 542 | ReentrantGuard guard(this->enqueuing); |
| 543 | #endif |
| 544 | |
| 545 | // High-level pseudocode (assuming we're allowed to alloc a new block): |
| 546 | // If room in tail block, add to tail |
| 547 | // Else check next block |
| 548 | // If next block is not the head block, enqueue on next block |
| 549 | // Else create a new block and enqueue there |
| 550 | // Advance tail to the block we just enqueued to |
| 551 | |
| 552 | Block* tailBlock_ = tailBlock.load(); |
| 553 | size_t blockFront = tailBlock_->localFront; |
| 554 | size_t blockTail = tailBlock_->tail.load(); |
| 555 | |
| 556 | size_t nextBlockTail = (blockTail + 1) & tailBlock_->sizeMask; |
| 557 | if (nextBlockTail != blockFront || nextBlockTail != (tailBlock_->localFront = tailBlock_->front.load())) { |
| 558 | fence(memory_order_acquire); |
| 559 | // This block has room for at least one more element |
| 560 | char* location = tailBlock_->data + blockTail * sizeof(T); |
| 561 | #if MOODYCAMEL_HAS_EMPLACE |
| 562 | new (location) T(std::forward<Args>(args)...); |
| 563 | #else |
| 564 | new (location) T(std::forward<U>(element)); |
| 565 | #endif |
| 566 | |
| 567 | fence(memory_order_release); |
| 568 | tailBlock_->tail = nextBlockTail; |
| 569 | } |
| 570 | else { |
| 571 | fence(memory_order_acquire); |
| 572 | if (tailBlock_->next.load() != frontBlock) { |
| 573 | // Note that the reason we can't advance to the frontBlock and start adding new entries there |
| 574 | // is because if we did, then dequeue would stay in that block, eventually reading the new values, |
| 575 | // instead of advancing to the next full block (whose values were enqueued first and so should be |
| 576 | // consumed first). |
| 577 | |
| 578 | fence(memory_order_acquire); // Ensure we get latest writes if we got the latest frontBlock |
| 579 | |
| 580 | // tailBlock is full, but there's a free block ahead, use it |
| 581 | Block* tailBlockNext = tailBlock_->next.load(); |
| 582 | size_t nextBlockFront = tailBlockNext->localFront = tailBlockNext->front.load(); |
| 583 | nextBlockTail = tailBlockNext->tail.load(); |
| 584 | fence(memory_order_acquire); |
| 585 | |
| 586 | // This block must be empty since it's not the head block and we |
| 587 | // go through the blocks in a circle |
| 588 | assert(nextBlockFront == nextBlockTail); |
| 589 | tailBlockNext->localFront = nextBlockFront; |
| 590 | |
| 591 | char* location = tailBlockNext->data + nextBlockTail * sizeof(T); |
| 592 | #if MOODYCAMEL_HAS_EMPLACE |
| 593 | new (location) T(std::forward<Args>(args)...); |
| 594 | #else |
| 595 | new (location) T(std::forward<U>(element)); |
no test coverage detected