| 2596 | |
| 2597 | template<AllocationMode allocMode, typename It> |
| 2598 | bool enqueue_bulk(It itemFirst, size_t count) { |
| 2599 | // First, we need to make sure we have enough room to enqueue all of the elements; |
| 2600 | // this means pre-allocating blocks and putting them in the block index (but only if |
| 2601 | // all the allocations succeeded). |
| 2602 | |
| 2603 | // Note that the tailBlock we start off with may not be owned by us any more; |
| 2604 | // this happens if it was filled up exactly to the top (setting tailIndex to |
| 2605 | // the first index of the next block which is not yet allocated), then dequeued |
| 2606 | // completely (putting it on the free list) before we enqueue again. |
| 2607 | |
| 2608 | index_t startTailIndex = this->tailIndex.load(std::memory_order_relaxed); |
| 2609 | auto startBlock = this->tailBlock; |
| 2610 | Block *firstAllocatedBlock = nullptr; |
| 2611 | auto endBlock = this->tailBlock; |
| 2612 | |
| 2613 | // Figure out how many blocks we'll need to allocate, and do so |
| 2614 | size_t blockBaseDiff = |
| 2615 | ((startTailIndex + count - 1) & ~static_cast<index_t>(BLOCK_SIZE - 1)) - |
| 2616 | ((startTailIndex - 1) & ~static_cast<index_t>(BLOCK_SIZE - 1)); |
| 2617 | index_t currentTailIndex = (startTailIndex - 1) & ~static_cast<index_t>(BLOCK_SIZE - 1); |
| 2618 | if (blockBaseDiff > 0) { |
| 2619 | #if MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX |
| 2620 | debug::DebugLock lock(mutex); |
| 2621 | #endif |
| 2622 | do { |
| 2623 | blockBaseDiff -= static_cast<index_t>(BLOCK_SIZE); |
| 2624 | currentTailIndex += static_cast<index_t>(BLOCK_SIZE); |
| 2625 | |
| 2626 | // Find out where we'll be inserting this block in the block index |
| 2627 | BlockIndexEntry *idxEntry = nullptr; // initialization here unnecessary but compiler can't always tell |
| 2628 | Block *newBlock; |
| 2629 | bool indexInserted = false; |
| 2630 | auto head = this->headIndex.load(std::memory_order_relaxed); |
| 2631 | assert(!details::circular_less_than<index_t>(currentTailIndex, head)); |
| 2632 | bool full = !details::circular_less_than<index_t>(head, currentTailIndex + BLOCK_SIZE) || |
| 2633 | (MAX_SUBQUEUE_SIZE != details::const_numeric_max<size_t>::value && |
| 2634 | (MAX_SUBQUEUE_SIZE == 0 || |
| 2635 | MAX_SUBQUEUE_SIZE - BLOCK_SIZE < currentTailIndex - head)); |
| 2636 | if (full || |
| 2637 | !(indexInserted = insert_block_index_entry<allocMode>(idxEntry, currentTailIndex)) || |
| 2638 | (newBlock = this->parent->ConcurrentQueue::template requisition_block<allocMode>()) == |
| 2639 | nullptr) { |
| 2640 | // Index allocation or block allocation failed; revert any other allocations |
| 2641 | // and index insertions done so far for this operation |
| 2642 | if (indexInserted) { |
| 2643 | rewind_block_index_tail(); |
| 2644 | idxEntry->value.store(nullptr, std::memory_order_relaxed); |
| 2645 | } |
| 2646 | currentTailIndex = (startTailIndex - 1) & ~static_cast<index_t>(BLOCK_SIZE - 1); |
| 2647 | for (auto block = firstAllocatedBlock; block != nullptr; block = block->next) { |
| 2648 | currentTailIndex += static_cast<index_t>(BLOCK_SIZE); |
| 2649 | idxEntry = get_block_index_entry_for_index(currentTailIndex); |
| 2650 | idxEntry->value.store(nullptr, std::memory_order_relaxed); |
| 2651 | rewind_block_index_tail(); |
| 2652 | } |
| 2653 | this->parent->add_blocks_to_free_list(firstAllocatedBlock); |
| 2654 | this->tailBlock = startBlock; |
| 2655 |
nothing calls this directly
no test coverage detected