| 2038 | |
| 2039 | template<AllocationMode allocMode, typename It> |
| 2040 | bool MOODYCAMEL_NO_TSAN enqueue_bulk(It itemFirst, size_t count) |
| 2041 | { |
| 2042 | // First, we need to make sure we have enough room to enqueue all of the elements; |
| 2043 | // this means pre-allocating blocks and putting them in the block index (but only if |
| 2044 | // all the allocations succeeded). |
| 2045 | index_t startTailIndex = this->tailIndex.load(std::memory_order_relaxed); |
| 2046 | auto startBlock = this->tailBlock; |
| 2047 | auto originalBlockIndexFront = pr_blockIndexFront; |
| 2048 | auto originalBlockIndexSlotsUsed = pr_blockIndexSlotsUsed; |
| 2049 | |
| 2050 | Block* firstAllocatedBlock = nullptr; |
| 2051 | |
| 2052 | // Figure out how many blocks we'll need to allocate, and do so |
| 2053 | size_t blockBaseDiff = ((startTailIndex + count - 1) & ~static_cast<index_t>(BLOCK_SIZE - 1)) - ((startTailIndex - 1) & ~static_cast<index_t>(BLOCK_SIZE - 1)); |
| 2054 | index_t currentTailIndex = (startTailIndex - 1) & ~static_cast<index_t>(BLOCK_SIZE - 1); |
| 2055 | if (blockBaseDiff > 0) { |
| 2056 | // Allocate as many blocks as possible from ahead |
| 2057 | while (blockBaseDiff > 0 && this->tailBlock != nullptr && this->tailBlock->next != firstAllocatedBlock && this->tailBlock->next->ConcurrentQueue::Block::template is_empty<explicit_context>()) { |
| 2058 | blockBaseDiff -= static_cast<index_t>(BLOCK_SIZE); |
| 2059 | currentTailIndex += static_cast<index_t>(BLOCK_SIZE); |
| 2060 | |
| 2061 | this->tailBlock = this->tailBlock->next; |
| 2062 | firstAllocatedBlock = firstAllocatedBlock == nullptr ? this->tailBlock : firstAllocatedBlock; |
| 2063 | |
| 2064 | auto& entry = blockIndex.load(std::memory_order_relaxed)->entries[pr_blockIndexFront]; |
| 2065 | entry.base = currentTailIndex; |
| 2066 | entry.block = this->tailBlock; |
| 2067 | pr_blockIndexFront = (pr_blockIndexFront + 1) & (pr_blockIndexSize - 1); |
| 2068 | } |
| 2069 | |
| 2070 | // Now allocate as many blocks as necessary from the block pool |
| 2071 | while (blockBaseDiff > 0) { |
| 2072 | blockBaseDiff -= static_cast<index_t>(BLOCK_SIZE); |
| 2073 | currentTailIndex += static_cast<index_t>(BLOCK_SIZE); |
| 2074 | |
| 2075 | auto head = this->headIndex.load(std::memory_order_relaxed); |
| 2076 | assert(!details::circular_less_than<index_t>(currentTailIndex, head)); |
| 2077 | bool full = !details::circular_less_than<index_t>(head, currentTailIndex + BLOCK_SIZE) || (MAX_SUBQUEUE_SIZE != details::const_numeric_max<size_t>::value && (MAX_SUBQUEUE_SIZE == 0 || MAX_SUBQUEUE_SIZE - BLOCK_SIZE < currentTailIndex - head)); |
| 2078 | if (pr_blockIndexRaw == nullptr || pr_blockIndexSlotsUsed == pr_blockIndexSize || full) { |
| 2079 | MOODYCAMEL_CONSTEXPR_IF (allocMode == CannotAlloc) { |
| 2080 | // Failed to allocate, undo changes (but keep injected blocks) |
| 2081 | pr_blockIndexFront = originalBlockIndexFront; |
| 2082 | pr_blockIndexSlotsUsed = originalBlockIndexSlotsUsed; |
| 2083 | this->tailBlock = startBlock == nullptr ? firstAllocatedBlock : startBlock; |
| 2084 | return false; |
| 2085 | } |
| 2086 | else if (full || !new_block_index(originalBlockIndexSlotsUsed)) { |
| 2087 | // Failed to allocate, undo changes (but keep injected blocks) |
| 2088 | pr_blockIndexFront = originalBlockIndexFront; |
| 2089 | pr_blockIndexSlotsUsed = originalBlockIndexSlotsUsed; |
| 2090 | this->tailBlock = startBlock == nullptr ? firstAllocatedBlock : startBlock; |
| 2091 | return false; |
| 2092 | } |
| 2093 | |
| 2094 | // pr_blockIndexFront is updated inside new_block_index, so we need to |
| 2095 | // update our fallback value too (since we keep the new index even if we |
| 2096 | // later fail) |
| 2097 | originalBlockIndexFront = originalBlockIndexSlotsUsed; |
nothing calls this directly
no test coverage detected