| 1847 | |
| 1848 | template<AllocationMode allocMode, typename U> |
| 1849 | inline bool enqueue(U&& element) |
| 1850 | { |
| 1851 | index_t currentTailIndex = this->tailIndex.load(std::memory_order_relaxed); |
| 1852 | index_t newTailIndex = 1 + currentTailIndex; |
| 1853 | if ((currentTailIndex & static_cast<index_t>(BLOCK_SIZE - 1)) == 0) { |
| 1854 | // We reached the end of a block, start a new one |
| 1855 | auto startBlock = this->tailBlock; |
| 1856 | auto originalBlockIndexSlotsUsed = pr_blockIndexSlotsUsed; |
| 1857 | if (this->tailBlock != nullptr && this->tailBlock->next->ConcurrentQueue::Block::template is_empty<explicit_context>()) { |
| 1858 | // We can re-use the block ahead of us, it's empty! |
| 1859 | this->tailBlock = this->tailBlock->next; |
| 1860 | this->tailBlock->ConcurrentQueue::Block::template reset_empty<explicit_context>(); |
| 1861 | |
| 1862 | // We'll put the block on the block index (guaranteed to be room since we're conceptually removing the |
| 1863 | // last block from it first -- except instead of removing then adding, we can just overwrite). |
| 1864 | // Note that there must be a valid block index here, since even if allocation failed in the ctor, |
| 1865 | // it would have been re-attempted when adding the first block to the queue; since there is such |
| 1866 | // a block, a block index must have been successfully allocated. |
| 1867 | } |
| 1868 | else { |
| 1869 | // Whatever head value we see here is >= the last value we saw here (relatively), |
| 1870 | // and <= its current value. Since we have the most recent tail, the head must be |
| 1871 | // <= to it. |
| 1872 | auto head = this->headIndex.load(std::memory_order_relaxed); |
| 1873 | assert(!details::circular_less_than<index_t>(currentTailIndex, head)); |
| 1874 | if (!details::circular_less_than<index_t>(head, currentTailIndex + BLOCK_SIZE) |
| 1875 | || (MAX_SUBQUEUE_SIZE != details::const_numeric_max<size_t>::value && (MAX_SUBQUEUE_SIZE == 0 || MAX_SUBQUEUE_SIZE - BLOCK_SIZE < currentTailIndex - head))) { |
| 1876 | // We can't enqueue in another block because there's not enough leeway -- the |
| 1877 | // tail could surpass the head by the time the block fills up! (Or we'll exceed |
| 1878 | // the size limit, if the second part of the condition was true.) |
| 1879 | return false; |
| 1880 | } |
| 1881 | // We're going to need a new block; check that the block index has room |
| 1882 | if (pr_blockIndexRaw == nullptr || pr_blockIndexSlotsUsed == pr_blockIndexSize) { |
| 1883 | // Hmm, the circular block index is already full -- we'll need |
| 1884 | // to allocate a new index. Note pr_blockIndexRaw can only be nullptr if |
| 1885 | // the initial allocation failed in the constructor. |
| 1886 | |
| 1887 | MOODYCAMEL_CONSTEXPR_IF (allocMode == CannotAlloc) { |
| 1888 | return false; |
| 1889 | } |
| 1890 | else if (!new_block_index(pr_blockIndexSlotsUsed)) { |
| 1891 | return false; |
| 1892 | } |
| 1893 | } |
| 1894 | |
| 1895 | // Insert a new block in the circular linked list |
| 1896 | auto newBlock = this->parent->ConcurrentQueue::template requisition_block<allocMode>(); |
| 1897 | if (newBlock == nullptr) { |
| 1898 | return false; |
| 1899 | } |
| 1900 | #ifdef MCDBGQ_TRACKMEM |
| 1901 | newBlock->owner = this; |
| 1902 | #endif |
| 1903 | newBlock->ConcurrentQueue::Block::template reset_empty<explicit_context>(); |
| 1904 | if (this->tailBlock == nullptr) { |
| 1905 | newBlock->next = newBlock; |
| 1906 | } |
nothing calls this directly
no test coverage detected