| 1766 | } |
| 1767 | |
| 1768 | ~ExplicitProducer() |
| 1769 | { |
| 1770 | // Destruct any elements not yet dequeued. |
| 1771 | // Since we're in the destructor, we can assume all elements |
| 1772 | // are either completely dequeued or completely not (no halfways). |
| 1773 | if (this->tailBlock != nullptr) { // Note this means there must be a block index too |
| 1774 | // First find the block that's partially dequeued, if any |
| 1775 | Block* halfDequeuedBlock = nullptr; |
| 1776 | if ((this->headIndex.load(std::memory_order_relaxed) & static_cast<index_t>(BLOCK_SIZE - 1)) != 0) { |
| 1777 | // The head's not on a block boundary, meaning a block somewhere is partially dequeued |
| 1778 | // (or the head block is the tail block and was fully dequeued, but the head/tail are still not on a boundary) |
| 1779 | size_t i = (pr_blockIndexFront - pr_blockIndexSlotsUsed) & (pr_blockIndexSize - 1); |
| 1780 | while (details::circular_less_than<index_t>(pr_blockIndexEntries[i].base + BLOCK_SIZE, this->headIndex.load(std::memory_order_relaxed))) { |
| 1781 | i = (i + 1) & (pr_blockIndexSize - 1); |
| 1782 | } |
| 1783 | assert(details::circular_less_than<index_t>(pr_blockIndexEntries[i].base, this->headIndex.load(std::memory_order_relaxed))); |
| 1784 | halfDequeuedBlock = pr_blockIndexEntries[i].block; |
| 1785 | } |
| 1786 | |
| 1787 | // Start at the head block (note the first line in the loop gives us the head from the tail on the first iteration) |
| 1788 | auto block = this->tailBlock; |
| 1789 | do { |
| 1790 | block = block->next; |
| 1791 | if (block->ConcurrentQueue::Block::template is_empty<explicit_context>()) { |
| 1792 | continue; |
| 1793 | } |
| 1794 | |
| 1795 | size_t i = 0; // Offset into block |
| 1796 | if (block == halfDequeuedBlock) { |
| 1797 | i = static_cast<size_t>(this->headIndex.load(std::memory_order_relaxed) & static_cast<index_t>(BLOCK_SIZE - 1)); |
| 1798 | } |
| 1799 | |
| 1800 | // Walk through all the items in the block; if this is the tail block, we need to stop when we reach the tail index |
| 1801 | auto lastValidIndex = (this->tailIndex.load(std::memory_order_relaxed) & static_cast<index_t>(BLOCK_SIZE - 1)) == 0 ? BLOCK_SIZE : static_cast<size_t>(this->tailIndex.load(std::memory_order_relaxed) & static_cast<index_t>(BLOCK_SIZE - 1)); |
| 1802 | while (i != BLOCK_SIZE && (block != this->tailBlock || i != lastValidIndex)) { |
| 1803 | (*block)[i++]->~T(); |
| 1804 | } |
| 1805 | } while (block != this->tailBlock); |
| 1806 | } |
| 1807 | |
| 1808 | // Destroy all blocks that we own |
| 1809 | if (this->tailBlock != nullptr) { |
| 1810 | auto block = this->tailBlock; |
| 1811 | do { |
| 1812 | auto nextBlock = block->next; |
| 1813 | if (block->dynamicallyAllocated) { |
| 1814 | destroy(block); |
| 1815 | } |
| 1816 | else { |
| 1817 | this->parent->add_block_to_free_list(block); |
| 1818 | } |
| 1819 | block = nextBlock; |
| 1820 | } while (block != this->tailBlock); |
| 1821 | } |
| 1822 | |
| 1823 | // Destroy the block indices |
| 1824 | auto header = static_cast<BlockIndexHeader*>(pr_blockIndexRaw); |
| 1825 | while (header != nullptr) { |