mirror of
https://github.com/intrepidcs/libicsneo.git
synced 2026-08-05 01:18:36 +02:00
Add ReaderWriterQueue and update ConcurrentQueue
This commit is contained in:
+204
-162
@@ -1,4 +1,4 @@
|
||||
// Provides a C++11 implementation of a multi-producer, multi-consumer lock-free queue.
|
||||
// Provides a C++11 implementation of a multi-producer, multi-consumer lock-free queue.
|
||||
// An overview, including benchmark results, is provided here:
|
||||
// http://moodycamel.com/blog/2014/a-fast-general-purpose-lock-free-queue-for-c++
|
||||
// The full design is also described in excruciating detail at:
|
||||
@@ -30,12 +30,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4127) // ICS: Warnings generated by this file
|
||||
#pragma warning(disable:4706)
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__)
|
||||
// Disable -Wconversion warnings (spuriously triggered when Traits::size_t and
|
||||
// Traits::index_t are set to < 32 bits, causing integer promotion, causing warnings
|
||||
@@ -152,10 +146,21 @@ namespace moodycamel { namespace details {
|
||||
typedef std::uintptr_t thread_id_t;
|
||||
static const thread_id_t invalid_thread_id = 0; // Address can't be nullptr
|
||||
static const thread_id_t invalid_thread_id2 = 1; // Member accesses off a null pointer are also generally invalid. Plus it's not aligned.
|
||||
static inline thread_id_t thread_id() { static MOODYCAMEL_THREADLOCAL int x; return reinterpret_cast<thread_id_t>(&x); }
|
||||
inline thread_id_t thread_id() { static MOODYCAMEL_THREADLOCAL int x; return reinterpret_cast<thread_id_t>(&x); }
|
||||
} }
|
||||
#endif
|
||||
|
||||
// Constexpr if
|
||||
#ifndef MOODYCAMEL_CONSTEXPR_IF
|
||||
#if (defined(_MSC_VER) && defined(_HAS_CXX17) && _HAS_CXX17) || __cplusplus > 201402L
|
||||
#define MOODYCAMEL_CONSTEXPR_IF if constexpr
|
||||
#define MOODYCAMEL_MAYBE_UNUSED [[maybe_unused]]
|
||||
#else
|
||||
#define MOODYCAMEL_CONSTEXPR_IF if
|
||||
#define MOODYCAMEL_MAYBE_UNUSED
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Exceptions
|
||||
#ifndef MOODYCAMEL_EXCEPTIONS_ENABLED
|
||||
#if (defined(_MSC_VER) && defined(_CPPUNWIND)) || (defined(__GNUC__) && defined(__EXCEPTIONS)) || (!defined(_MSC_VER) && !defined(__GNUC__))
|
||||
@@ -168,8 +173,8 @@ namespace moodycamel { namespace details {
|
||||
#define MOODYCAMEL_RETHROW throw
|
||||
#define MOODYCAMEL_THROW(expr) throw (expr)
|
||||
#else
|
||||
#define MOODYCAMEL_TRY if (true)
|
||||
#define MOODYCAMEL_CATCH(...) else if (false)
|
||||
#define MOODYCAMEL_TRY MOODYCAMEL_CONSTEXPR_IF (true)
|
||||
#define MOODYCAMEL_CATCH(...) else MOODYCAMEL_CONSTEXPR_IF (false)
|
||||
#define MOODYCAMEL_RETHROW
|
||||
#define MOODYCAMEL_THROW(expr)
|
||||
#endif
|
||||
@@ -220,6 +225,19 @@ namespace moodycamel { namespace details {
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef MOODYCAMEL_ALIGNAS
|
||||
// VS2013 doesn't support alignas or alignof
|
||||
#if defined(_MSC_VER) && _MSC_VER <= 1800
|
||||
#define MOODYCAMEL_ALIGNAS(alignment) __declspec(align(alignment))
|
||||
#define MOODYCAMEL_ALIGNOF(obj) __alignof(obj)
|
||||
#else
|
||||
#define MOODYCAMEL_ALIGNAS(alignment) alignas(alignment)
|
||||
#define MOODYCAMEL_ALIGNOF(obj) alignof(obj)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// Compiler-specific likely/unlikely hints
|
||||
namespace moodycamel { namespace details {
|
||||
#if defined(__GNUC__)
|
||||
@@ -791,7 +809,7 @@ public:
|
||||
}
|
||||
|
||||
// Destroy implicit producer hash tables
|
||||
if (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE != 0) {
|
||||
MOODYCAMEL_CONSTEXPR_IF (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE != 0) {
|
||||
auto hash = implicitProducerHash.load(std::memory_order_relaxed);
|
||||
while (hash != nullptr) {
|
||||
auto prev = hash->prev;
|
||||
@@ -916,8 +934,8 @@ public:
|
||||
// Thread-safe.
|
||||
inline bool enqueue(T const& item)
|
||||
{
|
||||
if (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false;
|
||||
return inner_enqueue<CanAlloc>(item);
|
||||
MOODYCAMEL_CONSTEXPR_IF (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false;
|
||||
else return inner_enqueue<CanAlloc>(item);
|
||||
}
|
||||
|
||||
// Enqueues a single item (by moving it, if possible).
|
||||
@@ -927,8 +945,8 @@ public:
|
||||
// Thread-safe.
|
||||
inline bool enqueue(T&& item)
|
||||
{
|
||||
if (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false;
|
||||
return inner_enqueue<CanAlloc>(std::move(item));
|
||||
MOODYCAMEL_CONSTEXPR_IF (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false;
|
||||
else return inner_enqueue<CanAlloc>(std::move(item));
|
||||
}
|
||||
|
||||
// Enqueues a single item (by copying it) using an explicit producer token.
|
||||
@@ -958,8 +976,8 @@ public:
|
||||
template<typename It>
|
||||
bool enqueue_bulk(It itemFirst, size_t count)
|
||||
{
|
||||
if (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false;
|
||||
return inner_enqueue_bulk<CanAlloc>(itemFirst, count);
|
||||
MOODYCAMEL_CONSTEXPR_IF (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false;
|
||||
else return inner_enqueue_bulk<CanAlloc>(itemFirst, count);
|
||||
}
|
||||
|
||||
// Enqueues several items using an explicit producer token.
|
||||
@@ -981,8 +999,8 @@ public:
|
||||
// Thread-safe.
|
||||
inline bool try_enqueue(T const& item)
|
||||
{
|
||||
if (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false;
|
||||
return inner_enqueue<CannotAlloc>(item);
|
||||
MOODYCAMEL_CONSTEXPR_IF (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false;
|
||||
else return inner_enqueue<CannotAlloc>(item);
|
||||
}
|
||||
|
||||
// Enqueues a single item (by moving it, if possible).
|
||||
@@ -992,8 +1010,8 @@ public:
|
||||
// Thread-safe.
|
||||
inline bool try_enqueue(T&& item)
|
||||
{
|
||||
if (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false;
|
||||
return inner_enqueue<CannotAlloc>(std::move(item));
|
||||
MOODYCAMEL_CONSTEXPR_IF (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false;
|
||||
else return inner_enqueue<CannotAlloc>(std::move(item));
|
||||
}
|
||||
|
||||
// Enqueues a single item (by copying it) using an explicit producer token.
|
||||
@@ -1022,8 +1040,8 @@ public:
|
||||
template<typename It>
|
||||
bool try_enqueue_bulk(It itemFirst, size_t count)
|
||||
{
|
||||
if (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false;
|
||||
return inner_enqueue_bulk<CannotAlloc>(itemFirst, count);
|
||||
MOODYCAMEL_CONSTEXPR_IF (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false;
|
||||
else return inner_enqueue_bulk<CannotAlloc>(itemFirst, count);
|
||||
}
|
||||
|
||||
// Enqueues several items using an explicit producer token.
|
||||
@@ -1380,7 +1398,7 @@ private:
|
||||
|
||||
inline void add(N* node)
|
||||
{
|
||||
#if MCDBGQ_NOLOCKFREE_FREELIST
|
||||
#ifdef MCDBGQ_NOLOCKFREE_FREELIST
|
||||
debug::DebugLock lock(mutex);
|
||||
#endif
|
||||
// We know that the should-be-on-freelist bit is 0 at this point, so it's safe to
|
||||
@@ -1394,7 +1412,7 @@ private:
|
||||
|
||||
inline N* try_get()
|
||||
{
|
||||
#if MCDBGQ_NOLOCKFREE_FREELIST
|
||||
#ifdef MCDBGQ_NOLOCKFREE_FREELIST
|
||||
debug::DebugLock lock(mutex);
|
||||
#endif
|
||||
auto head = freeListHead.load(std::memory_order_acquire);
|
||||
@@ -1466,7 +1484,7 @@ private:
|
||||
static const std::uint32_t REFS_MASK = 0x7FFFFFFF;
|
||||
static const std::uint32_t SHOULD_BE_ON_FREELIST = 0x80000000;
|
||||
|
||||
#if MCDBGQ_NOLOCKFREE_FREELIST
|
||||
#ifdef MCDBGQ_NOLOCKFREE_FREELIST
|
||||
debug::DebugMutex mutex;
|
||||
#endif
|
||||
};
|
||||
@@ -1483,7 +1501,7 @@ private:
|
||||
Block()
|
||||
: next(nullptr), elementsCompletelyDequeued(0), freeListRefs(0), freeListNext(nullptr), shouldBeOnFreeList(false), dynamicallyAllocated(true)
|
||||
{
|
||||
#if MCDBGQ_TRACKMEM
|
||||
#ifdef MCDBGQ_TRACKMEM
|
||||
owner = nullptr;
|
||||
#endif
|
||||
}
|
||||
@@ -1491,7 +1509,7 @@ private:
|
||||
template<InnerQueueContext context>
|
||||
inline bool is_empty() const
|
||||
{
|
||||
if (context == explicit_context && BLOCK_SIZE <= EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD) {
|
||||
MOODYCAMEL_CONSTEXPR_IF (context == explicit_context && BLOCK_SIZE <= EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD) {
|
||||
// Check flags
|
||||
for (size_t i = 0; i < BLOCK_SIZE; ++i) {
|
||||
if (!emptyFlags[i].load(std::memory_order_relaxed)) {
|
||||
@@ -1516,9 +1534,9 @@ private:
|
||||
|
||||
// Returns true if the block is now empty (does not apply in explicit context)
|
||||
template<InnerQueueContext context>
|
||||
inline bool set_empty(index_t i)
|
||||
inline bool set_empty(MOODYCAMEL_MAYBE_UNUSED index_t i)
|
||||
{
|
||||
if (context == explicit_context && BLOCK_SIZE <= EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD) {
|
||||
MOODYCAMEL_CONSTEXPR_IF (context == explicit_context && BLOCK_SIZE <= EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD) {
|
||||
// Set flag
|
||||
assert(!emptyFlags[BLOCK_SIZE - 1 - static_cast<size_t>(i & static_cast<index_t>(BLOCK_SIZE - 1))].load(std::memory_order_relaxed));
|
||||
emptyFlags[BLOCK_SIZE - 1 - static_cast<size_t>(i & static_cast<index_t>(BLOCK_SIZE - 1))].store(true, std::memory_order_release);
|
||||
@@ -1535,9 +1553,9 @@ private:
|
||||
// Sets multiple contiguous item statuses to 'empty' (assumes no wrapping and count > 0).
|
||||
// Returns true if the block is now empty (does not apply in explicit context).
|
||||
template<InnerQueueContext context>
|
||||
inline bool set_many_empty(index_t i, size_t count)
|
||||
inline bool set_many_empty(MOODYCAMEL_MAYBE_UNUSED index_t i, size_t count)
|
||||
{
|
||||
if (context == explicit_context && BLOCK_SIZE <= EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD) {
|
||||
MOODYCAMEL_CONSTEXPR_IF (context == explicit_context && BLOCK_SIZE <= EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD) {
|
||||
// Set flags
|
||||
std::atomic_thread_fence(std::memory_order_release);
|
||||
i = BLOCK_SIZE - 1 - static_cast<size_t>(i & static_cast<index_t>(BLOCK_SIZE - 1)) - count + 1;
|
||||
@@ -1558,7 +1576,7 @@ private:
|
||||
template<InnerQueueContext context>
|
||||
inline void set_all_empty()
|
||||
{
|
||||
if (context == explicit_context && BLOCK_SIZE <= EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD) {
|
||||
MOODYCAMEL_CONSTEXPR_IF (context == explicit_context && BLOCK_SIZE <= EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD) {
|
||||
// Set all flags
|
||||
for (size_t i = 0; i != BLOCK_SIZE; ++i) {
|
||||
emptyFlags[i].store(true, std::memory_order_relaxed);
|
||||
@@ -1573,7 +1591,7 @@ private:
|
||||
template<InnerQueueContext context>
|
||||
inline void reset_empty()
|
||||
{
|
||||
if (context == explicit_context && BLOCK_SIZE <= EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD) {
|
||||
MOODYCAMEL_CONSTEXPR_IF (context == explicit_context && BLOCK_SIZE <= EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD) {
|
||||
// Reset flags
|
||||
for (size_t i = 0; i != BLOCK_SIZE; ++i) {
|
||||
emptyFlags[i].store(false, std::memory_order_relaxed);
|
||||
@@ -1589,20 +1607,8 @@ private:
|
||||
inline T const* operator[](index_t idx) const MOODYCAMEL_NOEXCEPT { return static_cast<T const*>(static_cast<void const*>(elements)) + static_cast<size_t>(idx & static_cast<index_t>(BLOCK_SIZE - 1)); }
|
||||
|
||||
private:
|
||||
// IMPORTANT: This must be the first member in Block, so that if T depends on the alignment of
|
||||
// addresses returned by malloc, that alignment will be preserved. Apparently clang actually
|
||||
// generates code that uses this assumption for AVX instructions in some cases. Ideally, we
|
||||
// should also align Block to the alignment of T in case it's higher than malloc's 16-byte
|
||||
// alignment, but this is hard to do in a cross-platform way. Assert for this case:
|
||||
static_assert(std::alignment_of<T>::value <= std::alignment_of<details::max_align_t>::value, "The queue does not support super-aligned types at this time");
|
||||
// Additionally, we need the alignment of Block itself to be a multiple of max_align_t since
|
||||
// otherwise the appropriate padding will not be added at the end of Block in order to make
|
||||
// arrays of Blocks all be properly aligned (not just the first one). We use a union to force
|
||||
// this.
|
||||
union {
|
||||
char elements[sizeof(T) * BLOCK_SIZE];
|
||||
details::max_align_t dummy;
|
||||
};
|
||||
static_assert(std::alignment_of<T>::value <= sizeof(T), "The queue does not support types with an alignment greater than their size at this time");
|
||||
MOODYCAMEL_ALIGNAS(MOODYCAMEL_ALIGNOF(T)) char elements[sizeof(T) * BLOCK_SIZE];
|
||||
public:
|
||||
Block* next;
|
||||
std::atomic<size_t> elementsCompletelyDequeued;
|
||||
@@ -1613,14 +1619,14 @@ private:
|
||||
std::atomic<bool> shouldBeOnFreeList;
|
||||
bool dynamicallyAllocated; // Perhaps a better name for this would be 'isNotPartOfInitialBlockPool'
|
||||
|
||||
#if MCDBGQ_TRACKMEM
|
||||
#ifdef MCDBGQ_TRACKMEM
|
||||
void* owner;
|
||||
#endif
|
||||
};
|
||||
static_assert(std::alignment_of<Block>::value >= std::alignment_of<details::max_align_t>::value, "Internal error: Blocks must be at least as aligned as the type they are wrapping");
|
||||
static_assert(std::alignment_of<Block>::value >= std::alignment_of<T>::value, "Internal error: Blocks must be at least as aligned as the type they are wrapping");
|
||||
|
||||
|
||||
#if MCDBGQ_TRACKMEM
|
||||
#ifdef MCDBGQ_TRACKMEM
|
||||
public:
|
||||
struct MemStats;
|
||||
private:
|
||||
@@ -1691,7 +1697,7 @@ private:
|
||||
ConcurrentQueue* parent;
|
||||
|
||||
protected:
|
||||
#if MCDBGQ_TRACKMEM
|
||||
#ifdef MCDBGQ_TRACKMEM
|
||||
friend struct MemStats;
|
||||
#endif
|
||||
};
|
||||
@@ -1703,8 +1709,8 @@ private:
|
||||
|
||||
struct ExplicitProducer : public ProducerBase
|
||||
{
|
||||
explicit ExplicitProducer(ConcurrentQueue* parent) :
|
||||
ProducerBase(parent, true),
|
||||
explicit ExplicitProducer(ConcurrentQueue* parent_) :
|
||||
ProducerBase(parent_, true),
|
||||
blockIndex(nullptr),
|
||||
pr_blockIndexSlotsUsed(0),
|
||||
pr_blockIndexSize(EXPLICIT_INITIAL_INDEX_SIZE >> 1),
|
||||
@@ -1712,7 +1718,7 @@ private:
|
||||
pr_blockIndexEntries(nullptr),
|
||||
pr_blockIndexRaw(nullptr)
|
||||
{
|
||||
size_t poolBasedIndexSize = details::ceil_to_pow_2(parent->initialBlockPoolSize) >> 1;
|
||||
size_t poolBasedIndexSize = details::ceil_to_pow_2(parent_->initialBlockPoolSize) >> 1;
|
||||
if (poolBasedIndexSize > pr_blockIndexSize) {
|
||||
pr_blockIndexSize = poolBasedIndexSize;
|
||||
}
|
||||
@@ -1824,7 +1830,10 @@ private:
|
||||
// to allocate a new index. Note pr_blockIndexRaw can only be nullptr if
|
||||
// the initial allocation failed in the constructor.
|
||||
|
||||
if (allocMode == CannotAlloc || !new_block_index(pr_blockIndexSlotsUsed)) {
|
||||
MOODYCAMEL_CONSTEXPR_IF (allocMode == CannotAlloc) {
|
||||
return false;
|
||||
}
|
||||
else if (!new_block_index(pr_blockIndexSlotsUsed)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1834,7 +1843,7 @@ private:
|
||||
if (newBlock == nullptr) {
|
||||
return false;
|
||||
}
|
||||
#if MCDBGQ_TRACKMEM
|
||||
#ifdef MCDBGQ_TRACKMEM
|
||||
newBlock->owner = this;
|
||||
#endif
|
||||
newBlock->ConcurrentQueue::Block::template reset_empty<explicit_context>();
|
||||
@@ -1848,8 +1857,8 @@ private:
|
||||
this->tailBlock = newBlock;
|
||||
++pr_blockIndexSlotsUsed;
|
||||
}
|
||||
|
||||
if (!MOODYCAMEL_NOEXCEPT_CTOR(T, U, new (nullptr) T(std::forward<U>(element)))) {
|
||||
|
||||
if (!MOODYCAMEL_NOEXCEPT_CTOR(T, U, new ((T*)nullptr) T(std::forward<U>(element)))) {
|
||||
// The constructor may throw. We want the element not to appear in the queue in
|
||||
// that case (without corrupting the queue):
|
||||
MOODYCAMEL_TRY {
|
||||
@@ -1875,7 +1884,7 @@ private:
|
||||
blockIndex.load(std::memory_order_relaxed)->front.store(pr_blockIndexFront, std::memory_order_release);
|
||||
pr_blockIndexFront = (pr_blockIndexFront + 1) & (pr_blockIndexSize - 1);
|
||||
|
||||
if (!MOODYCAMEL_NOEXCEPT_CTOR(T, U, new (nullptr) T(std::forward<U>(element)))) {
|
||||
if (!MOODYCAMEL_NOEXCEPT_CTOR(T, U, new ((T*)nullptr) T(std::forward<U>(element)))) {
|
||||
this->tailIndex.store(newTailIndex, std::memory_order_release);
|
||||
return true;
|
||||
}
|
||||
@@ -1968,12 +1977,12 @@ private:
|
||||
block->ConcurrentQueue::Block::template set_empty<explicit_context>(index);
|
||||
}
|
||||
} guard = { block, index };
|
||||
|
||||
element = std::move(el);
|
||||
|
||||
element = std::move(el); // NOLINT
|
||||
}
|
||||
else {
|
||||
element = std::move(el);
|
||||
el.~T();
|
||||
element = std::move(el); // NOLINT
|
||||
el.~T(); // NOLINT
|
||||
block->ConcurrentQueue::Block::template set_empty<explicit_context>(index);
|
||||
}
|
||||
|
||||
@@ -2028,7 +2037,14 @@ private:
|
||||
assert(!details::circular_less_than<index_t>(currentTailIndex, head));
|
||||
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));
|
||||
if (pr_blockIndexRaw == nullptr || pr_blockIndexSlotsUsed == pr_blockIndexSize || full) {
|
||||
if (allocMode == CannotAlloc || full || !new_block_index(originalBlockIndexSlotsUsed)) {
|
||||
MOODYCAMEL_CONSTEXPR_IF (allocMode == CannotAlloc) {
|
||||
// Failed to allocate, undo changes (but keep injected blocks)
|
||||
pr_blockIndexFront = originalBlockIndexFront;
|
||||
pr_blockIndexSlotsUsed = originalBlockIndexSlotsUsed;
|
||||
this->tailBlock = startBlock == nullptr ? firstAllocatedBlock : startBlock;
|
||||
return false;
|
||||
}
|
||||
else if (full || !new_block_index(originalBlockIndexSlotsUsed)) {
|
||||
// Failed to allocate, undo changes (but keep injected blocks)
|
||||
pr_blockIndexFront = originalBlockIndexFront;
|
||||
pr_blockIndexSlotsUsed = originalBlockIndexSlotsUsed;
|
||||
@@ -2051,7 +2067,7 @@ private:
|
||||
return false;
|
||||
}
|
||||
|
||||
#if MCDBGQ_TRACKMEM
|
||||
#ifdef MCDBGQ_TRACKMEM
|
||||
newBlock->owner = this;
|
||||
#endif
|
||||
newBlock->ConcurrentQueue::Block::template set_all_empty<explicit_context>();
|
||||
@@ -2084,7 +2100,7 @@ private:
|
||||
block = block->next;
|
||||
}
|
||||
|
||||
if (MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst), new (nullptr) T(details::deref_noexcept(itemFirst)))) {
|
||||
if (MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst), new ((T*)nullptr) T(details::deref_noexcept(itemFirst)))) {
|
||||
blockIndex.load(std::memory_order_relaxed)->front.store((pr_blockIndexFront - 1) & (pr_blockIndexSize - 1), std::memory_order_release);
|
||||
}
|
||||
}
|
||||
@@ -2103,7 +2119,7 @@ private:
|
||||
if (details::circular_less_than<index_t>(newTailIndex, stopIndex)) {
|
||||
stopIndex = newTailIndex;
|
||||
}
|
||||
if (MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst), new (nullptr) T(details::deref_noexcept(itemFirst)))) {
|
||||
if (MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst), new ((T*)nullptr) T(details::deref_noexcept(itemFirst)))) {
|
||||
while (currentTailIndex != stopIndex) {
|
||||
new ((*this->tailBlock)[currentTailIndex++]) T(*itemFirst++);
|
||||
}
|
||||
@@ -2118,7 +2134,7 @@ private:
|
||||
// may only define a (noexcept) move constructor, and so calls to the
|
||||
// cctor will not compile, even if they are in an if branch that will never
|
||||
// be executed
|
||||
new ((*this->tailBlock)[currentTailIndex]) T(details::nomove_if<(bool)!MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst), new (nullptr) T(details::deref_noexcept(itemFirst)))>::eval(*itemFirst));
|
||||
new ((*this->tailBlock)[currentTailIndex]) T(details::nomove_if<(bool)!MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst), new ((T*)nullptr) T(details::deref_noexcept(itemFirst)))>::eval(*itemFirst));
|
||||
++currentTailIndex;
|
||||
++itemFirst;
|
||||
}
|
||||
@@ -2165,7 +2181,7 @@ private:
|
||||
this->tailBlock = this->tailBlock->next;
|
||||
}
|
||||
|
||||
if (!MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst), new (nullptr) T(details::deref_noexcept(itemFirst))) && firstAllocatedBlock != nullptr) {
|
||||
if (!MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst), new ((T*)nullptr) T(details::deref_noexcept(itemFirst))) && firstAllocatedBlock != nullptr) {
|
||||
blockIndex.load(std::memory_order_relaxed)->front.store((pr_blockIndexFront - 1) & (pr_blockIndexSize - 1), std::memory_order_release);
|
||||
}
|
||||
|
||||
@@ -2337,7 +2353,7 @@ private:
|
||||
private:
|
||||
#endif
|
||||
|
||||
#if MCDBGQ_TRACKMEM
|
||||
#ifdef MCDBGQ_TRACKMEM
|
||||
friend struct MemStats;
|
||||
#endif
|
||||
};
|
||||
@@ -2349,8 +2365,8 @@ private:
|
||||
|
||||
struct ImplicitProducer : public ProducerBase
|
||||
{
|
||||
ImplicitProducer(ConcurrentQueue* parent) :
|
||||
ProducerBase(parent, false),
|
||||
ImplicitProducer(ConcurrentQueue* parent_) :
|
||||
ProducerBase(parent_, false),
|
||||
nextBlockIndexCapacity(IMPLICIT_INITIAL_INDEX_SIZE),
|
||||
blockIndex(nullptr)
|
||||
{
|
||||
@@ -2424,7 +2440,7 @@ private:
|
||||
if (!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))) {
|
||||
return false;
|
||||
}
|
||||
#if MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX
|
||||
#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX
|
||||
debug::DebugLock lock(mutex);
|
||||
#endif
|
||||
// Find out where we'll be inserting this block in the block index
|
||||
@@ -2440,12 +2456,12 @@ private:
|
||||
idxEntry->value.store(nullptr, std::memory_order_relaxed);
|
||||
return false;
|
||||
}
|
||||
#if MCDBGQ_TRACKMEM
|
||||
#ifdef MCDBGQ_TRACKMEM
|
||||
newBlock->owner = this;
|
||||
#endif
|
||||
newBlock->ConcurrentQueue::Block::template reset_empty<implicit_context>();
|
||||
|
||||
if (!MOODYCAMEL_NOEXCEPT_CTOR(T, U, new (nullptr) T(std::forward<U>(element)))) {
|
||||
if (!MOODYCAMEL_NOEXCEPT_CTOR(T, U, new ((T*)nullptr) T(std::forward<U>(element)))) {
|
||||
// May throw, try to insert now before we publish the fact that we have this new block
|
||||
MOODYCAMEL_TRY {
|
||||
new ((*newBlock)[currentTailIndex]) T(std::forward<U>(element));
|
||||
@@ -2463,7 +2479,7 @@ private:
|
||||
|
||||
this->tailBlock = newBlock;
|
||||
|
||||
if (!MOODYCAMEL_NOEXCEPT_CTOR(T, U, new (nullptr) T(std::forward<U>(element)))) {
|
||||
if (!MOODYCAMEL_NOEXCEPT_CTOR(T, U, new ((T*)nullptr) T(std::forward<U>(element)))) {
|
||||
this->tailIndex.store(newTailIndex, std::memory_order_release);
|
||||
return true;
|
||||
}
|
||||
@@ -2498,7 +2514,7 @@ private:
|
||||
auto& el = *((*block)[index]);
|
||||
|
||||
if (!MOODYCAMEL_NOEXCEPT_ASSIGN(T, T&&, element = std::move(el))) {
|
||||
#if MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX
|
||||
#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX
|
||||
// Note: Acquiring the mutex with every dequeue instead of only when a block
|
||||
// is released is very sub-optimal, but it is, after all, purely debug code.
|
||||
debug::DebugLock lock(producer->mutex);
|
||||
@@ -2518,16 +2534,16 @@ private:
|
||||
}
|
||||
}
|
||||
} guard = { block, index, entry, this->parent };
|
||||
|
||||
element = std::move(el);
|
||||
|
||||
element = std::move(el); // NOLINT
|
||||
}
|
||||
else {
|
||||
element = std::move(el);
|
||||
el.~T();
|
||||
|
||||
element = std::move(el); // NOLINT
|
||||
el.~T(); // NOLINT
|
||||
|
||||
if (block->ConcurrentQueue::Block::template set_empty<implicit_context>(index)) {
|
||||
{
|
||||
#if MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX
|
||||
#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX
|
||||
debug::DebugLock lock(mutex);
|
||||
#endif
|
||||
// Add the block back into the global free pool (and remove from block index)
|
||||
@@ -2568,7 +2584,7 @@ private:
|
||||
size_t blockBaseDiff = ((startTailIndex + count - 1) & ~static_cast<index_t>(BLOCK_SIZE - 1)) - ((startTailIndex - 1) & ~static_cast<index_t>(BLOCK_SIZE - 1));
|
||||
index_t currentTailIndex = (startTailIndex - 1) & ~static_cast<index_t>(BLOCK_SIZE - 1);
|
||||
if (blockBaseDiff > 0) {
|
||||
#if MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX
|
||||
#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX
|
||||
debug::DebugLock lock(mutex);
|
||||
#endif
|
||||
do {
|
||||
@@ -2602,7 +2618,7 @@ private:
|
||||
return false;
|
||||
}
|
||||
|
||||
#if MCDBGQ_TRACKMEM
|
||||
#ifdef MCDBGQ_TRACKMEM
|
||||
newBlock->owner = this;
|
||||
#endif
|
||||
newBlock->ConcurrentQueue::Block::template reset_empty<implicit_context>();
|
||||
@@ -2636,7 +2652,7 @@ private:
|
||||
if (details::circular_less_than<index_t>(newTailIndex, stopIndex)) {
|
||||
stopIndex = newTailIndex;
|
||||
}
|
||||
if (MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst), new (nullptr) T(details::deref_noexcept(itemFirst)))) {
|
||||
if (MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst), new ((T*)nullptr) T(details::deref_noexcept(itemFirst)))) {
|
||||
while (currentTailIndex != stopIndex) {
|
||||
new ((*this->tailBlock)[currentTailIndex++]) T(*itemFirst++);
|
||||
}
|
||||
@@ -2644,7 +2660,7 @@ private:
|
||||
else {
|
||||
MOODYCAMEL_TRY {
|
||||
while (currentTailIndex != stopIndex) {
|
||||
new ((*this->tailBlock)[currentTailIndex]) T(details::nomove_if<(bool)!MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst), new (nullptr) T(details::deref_noexcept(itemFirst)))>::eval(*itemFirst));
|
||||
new ((*this->tailBlock)[currentTailIndex]) T(details::nomove_if<(bool)!MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst), new ((T*)nullptr) T(details::deref_noexcept(itemFirst)))>::eval(*itemFirst));
|
||||
++currentTailIndex;
|
||||
++itemFirst;
|
||||
}
|
||||
@@ -2759,7 +2775,7 @@ private:
|
||||
}
|
||||
|
||||
if (block->ConcurrentQueue::Block::template set_many_empty<implicit_context>(blockStartIndex, static_cast<size_t>(endIndex - blockStartIndex))) {
|
||||
#if MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX
|
||||
#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX
|
||||
debug::DebugLock lock(mutex);
|
||||
#endif
|
||||
entry->value.store(nullptr, std::memory_order_relaxed);
|
||||
@@ -2777,7 +2793,7 @@ private:
|
||||
}
|
||||
if (block->ConcurrentQueue::Block::template set_many_empty<implicit_context>(blockStartIndex, static_cast<size_t>(endIndex - blockStartIndex))) {
|
||||
{
|
||||
#if MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX
|
||||
#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX
|
||||
debug::DebugLock lock(mutex);
|
||||
#endif
|
||||
// Note that the set_many_empty above did a release, meaning that anybody who acquires the block
|
||||
@@ -2836,7 +2852,10 @@ private:
|
||||
}
|
||||
|
||||
// No room in the old block index, try to allocate another one!
|
||||
if (allocMode == CannotAlloc || !new_block_index()) {
|
||||
MOODYCAMEL_CONSTEXPR_IF (allocMode == CannotAlloc) {
|
||||
return false;
|
||||
}
|
||||
else if (!new_block_index()) {
|
||||
return false;
|
||||
}
|
||||
localBlockIndex = blockIndex.load(std::memory_order_relaxed);
|
||||
@@ -2863,7 +2882,7 @@ private:
|
||||
|
||||
inline size_t get_block_index_index_for_index(index_t index, BlockIndexHeader*& localBlockIndex) const
|
||||
{
|
||||
#if MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX
|
||||
#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX
|
||||
debug::DebugLock lock(mutex);
|
||||
#endif
|
||||
index &= ~static_cast<index_t>(BLOCK_SIZE - 1);
|
||||
@@ -2939,10 +2958,10 @@ private:
|
||||
private:
|
||||
#endif
|
||||
|
||||
#if MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX
|
||||
#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX
|
||||
mutable debug::DebugMutex mutex;
|
||||
#endif
|
||||
#if MCDBGQ_TRACKMEM
|
||||
#ifdef MCDBGQ_TRACKMEM
|
||||
friend struct MemStats;
|
||||
#endif
|
||||
};
|
||||
@@ -2982,7 +3001,7 @@ private:
|
||||
|
||||
inline void add_block_to_free_list(Block* block)
|
||||
{
|
||||
#if MCDBGQ_TRACKMEM
|
||||
#ifdef MCDBGQ_TRACKMEM
|
||||
block->owner = nullptr;
|
||||
#endif
|
||||
freeList.add(block);
|
||||
@@ -3016,15 +3035,16 @@ private:
|
||||
return block;
|
||||
}
|
||||
|
||||
if (canAlloc == CanAlloc) {
|
||||
MOODYCAMEL_CONSTEXPR_IF (canAlloc == CanAlloc) {
|
||||
return create<Block>();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if MCDBGQ_TRACKMEM
|
||||
#ifdef MCDBGQ_TRACKMEM
|
||||
public:
|
||||
struct MemStats {
|
||||
size_t allocatedBlocks;
|
||||
@@ -3142,7 +3162,7 @@ private:
|
||||
|
||||
ProducerBase* recycle_or_create_producer(bool isExplicit, bool& recycled)
|
||||
{
|
||||
#if MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH
|
||||
#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH
|
||||
debug::DebugLock lock(implicitProdMutex);
|
||||
#endif
|
||||
// Try to re-use one first
|
||||
@@ -3249,50 +3269,56 @@ private:
|
||||
|
||||
inline void populate_initial_implicit_producer_hash()
|
||||
{
|
||||
if (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return;
|
||||
|
||||
implicitProducerHashCount.store(0, std::memory_order_relaxed);
|
||||
auto hash = &initialImplicitProducerHash;
|
||||
hash->capacity = INITIAL_IMPLICIT_PRODUCER_HASH_SIZE;
|
||||
hash->entries = &initialImplicitProducerHashEntries[0];
|
||||
for (size_t i = 0; i != INITIAL_IMPLICIT_PRODUCER_HASH_SIZE; ++i) {
|
||||
initialImplicitProducerHashEntries[i].key.store(details::invalid_thread_id, std::memory_order_relaxed);
|
||||
MOODYCAMEL_CONSTEXPR_IF (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) {
|
||||
return;
|
||||
}
|
||||
else {
|
||||
implicitProducerHashCount.store(0, std::memory_order_relaxed);
|
||||
auto hash = &initialImplicitProducerHash;
|
||||
hash->capacity = INITIAL_IMPLICIT_PRODUCER_HASH_SIZE;
|
||||
hash->entries = &initialImplicitProducerHashEntries[0];
|
||||
for (size_t i = 0; i != INITIAL_IMPLICIT_PRODUCER_HASH_SIZE; ++i) {
|
||||
initialImplicitProducerHashEntries[i].key.store(details::invalid_thread_id, std::memory_order_relaxed);
|
||||
}
|
||||
hash->prev = nullptr;
|
||||
implicitProducerHash.store(hash, std::memory_order_relaxed);
|
||||
}
|
||||
hash->prev = nullptr;
|
||||
implicitProducerHash.store(hash, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void swap_implicit_producer_hashes(ConcurrentQueue& other)
|
||||
{
|
||||
if (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return;
|
||||
|
||||
// Swap (assumes our implicit producer hash is initialized)
|
||||
initialImplicitProducerHashEntries.swap(other.initialImplicitProducerHashEntries);
|
||||
initialImplicitProducerHash.entries = &initialImplicitProducerHashEntries[0];
|
||||
other.initialImplicitProducerHash.entries = &other.initialImplicitProducerHashEntries[0];
|
||||
|
||||
details::swap_relaxed(implicitProducerHashCount, other.implicitProducerHashCount);
|
||||
|
||||
details::swap_relaxed(implicitProducerHash, other.implicitProducerHash);
|
||||
if (implicitProducerHash.load(std::memory_order_relaxed) == &other.initialImplicitProducerHash) {
|
||||
implicitProducerHash.store(&initialImplicitProducerHash, std::memory_order_relaxed);
|
||||
MOODYCAMEL_CONSTEXPR_IF (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) {
|
||||
return;
|
||||
}
|
||||
else {
|
||||
ImplicitProducerHash* hash;
|
||||
for (hash = implicitProducerHash.load(std::memory_order_relaxed); hash->prev != &other.initialImplicitProducerHash; hash = hash->prev) {
|
||||
continue;
|
||||
// Swap (assumes our implicit producer hash is initialized)
|
||||
initialImplicitProducerHashEntries.swap(other.initialImplicitProducerHashEntries);
|
||||
initialImplicitProducerHash.entries = &initialImplicitProducerHashEntries[0];
|
||||
other.initialImplicitProducerHash.entries = &other.initialImplicitProducerHashEntries[0];
|
||||
|
||||
details::swap_relaxed(implicitProducerHashCount, other.implicitProducerHashCount);
|
||||
|
||||
details::swap_relaxed(implicitProducerHash, other.implicitProducerHash);
|
||||
if (implicitProducerHash.load(std::memory_order_relaxed) == &other.initialImplicitProducerHash) {
|
||||
implicitProducerHash.store(&initialImplicitProducerHash, std::memory_order_relaxed);
|
||||
}
|
||||
hash->prev = &initialImplicitProducerHash;
|
||||
}
|
||||
if (other.implicitProducerHash.load(std::memory_order_relaxed) == &initialImplicitProducerHash) {
|
||||
other.implicitProducerHash.store(&other.initialImplicitProducerHash, std::memory_order_relaxed);
|
||||
}
|
||||
else {
|
||||
ImplicitProducerHash* hash;
|
||||
for (hash = other.implicitProducerHash.load(std::memory_order_relaxed); hash->prev != &initialImplicitProducerHash; hash = hash->prev) {
|
||||
continue;
|
||||
else {
|
||||
ImplicitProducerHash* hash;
|
||||
for (hash = implicitProducerHash.load(std::memory_order_relaxed); hash->prev != &other.initialImplicitProducerHash; hash = hash->prev) {
|
||||
continue;
|
||||
}
|
||||
hash->prev = &initialImplicitProducerHash;
|
||||
}
|
||||
if (other.implicitProducerHash.load(std::memory_order_relaxed) == &initialImplicitProducerHash) {
|
||||
other.implicitProducerHash.store(&other.initialImplicitProducerHash, std::memory_order_relaxed);
|
||||
}
|
||||
else {
|
||||
ImplicitProducerHash* hash;
|
||||
for (hash = other.implicitProducerHash.load(std::memory_order_relaxed); hash->prev != &initialImplicitProducerHash; hash = hash->prev) {
|
||||
continue;
|
||||
}
|
||||
hash->prev = &other.initialImplicitProducerHash;
|
||||
}
|
||||
hash->prev = &other.initialImplicitProducerHash;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3309,7 +3335,7 @@ private:
|
||||
|
||||
// Code and algorithm adapted from http://preshing.com/20130605/the-worlds-simplest-lock-free-hash-table
|
||||
|
||||
#if MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH
|
||||
#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH
|
||||
debug::DebugLock lock(implicitProdMutex);
|
||||
#endif
|
||||
|
||||
@@ -3317,6 +3343,7 @@ private:
|
||||
auto hashedId = details::hash_thread_id(id);
|
||||
|
||||
auto mainHash = implicitProducerHash.load(std::memory_order_acquire);
|
||||
assert(mainHash != nullptr); // silence clang-tidy and MSVC warnings (hash cannot be null)
|
||||
for (auto hash = mainHash; hash != nullptr; hash = hash->prev) {
|
||||
// Look for the id in this hash
|
||||
auto index = hashedId;
|
||||
@@ -3363,6 +3390,7 @@ private:
|
||||
// Insert!
|
||||
auto newCount = 1 + implicitProducerHashCount.fetch_add(1, std::memory_order_relaxed);
|
||||
while (true) {
|
||||
// NOLINTNEXTLINE(clang-analyzer-core.NullDereference)
|
||||
if (newCount >= (mainHash->capacity >> 1) && !implicitProducerHashResizeInProgress.test_and_set(std::memory_order_acquire)) {
|
||||
// We've acquired the resize lock, try to allocate a bigger hash table.
|
||||
// Note the acquire fence synchronizes with the release fence at the end of this block, and hence when
|
||||
@@ -3454,7 +3482,7 @@ private:
|
||||
details::ThreadExitNotifier::unsubscribe(&producer->threadExitListener);
|
||||
|
||||
// Remove from hash
|
||||
#if MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH
|
||||
#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH
|
||||
debug::DebugLock lock(implicitProdMutex);
|
||||
#endif
|
||||
auto hash = implicitProducerHash.load(std::memory_order_acquire);
|
||||
@@ -3493,55 +3521,73 @@ private:
|
||||
//////////////////////////////////
|
||||
// Utility functions
|
||||
//////////////////////////////////
|
||||
|
||||
|
||||
template<typename TAlign>
|
||||
static inline void* aligned_malloc(size_t size)
|
||||
{
|
||||
if (std::alignment_of<TAlign>::value <= std::alignment_of<details::max_align_t>::value)
|
||||
return (Traits::malloc)(size);
|
||||
size_t alignment = std::alignment_of<TAlign>::value;
|
||||
void* raw = (Traits::malloc)(size + alignment - 1 + sizeof(void*));
|
||||
if (!raw)
|
||||
return nullptr;
|
||||
char* ptr = details::align_for<TAlign>(reinterpret_cast<char*>(raw) + sizeof(void*));
|
||||
*(reinterpret_cast<void**>(ptr) - 1) = raw;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
template<typename TAlign>
|
||||
static inline void aligned_free(void* ptr)
|
||||
{
|
||||
if (std::alignment_of<TAlign>::value <= std::alignment_of<details::max_align_t>::value)
|
||||
return (Traits::free)(ptr);
|
||||
(Traits::free)(ptr ? *(reinterpret_cast<void**>(ptr) - 1) : nullptr);
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
static inline U* create_array(size_t count)
|
||||
{
|
||||
assert(count > 0);
|
||||
auto p = static_cast<U*>((Traits::malloc)(sizeof(U) * count));
|
||||
if (p == nullptr) {
|
||||
U* p = static_cast<U*>(aligned_malloc<U>(sizeof(U) * count));
|
||||
if (p == nullptr)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i != count; ++i) {
|
||||
|
||||
for (size_t i = 0; i != count; ++i)
|
||||
new (p + i) U();
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
template<typename U>
|
||||
static inline void destroy_array(U* p, size_t count)
|
||||
{
|
||||
if (p != nullptr) {
|
||||
assert(count > 0);
|
||||
for (size_t i = count; i != 0; ) {
|
||||
for (size_t i = count; i != 0; )
|
||||
(p + --i)->~U();
|
||||
}
|
||||
(Traits::free)(p);
|
||||
}
|
||||
aligned_free<U>(p);
|
||||
}
|
||||
|
||||
|
||||
template<typename U>
|
||||
static inline U* create()
|
||||
{
|
||||
auto p = (Traits::malloc)(sizeof(U));
|
||||
void* p = aligned_malloc<U>(sizeof(U));
|
||||
return p != nullptr ? new (p) U : nullptr;
|
||||
}
|
||||
|
||||
|
||||
template<typename U, typename A1>
|
||||
static inline U* create(A1&& a1)
|
||||
{
|
||||
auto p = (Traits::malloc)(sizeof(U));
|
||||
void* p = aligned_malloc<U>(sizeof(U));
|
||||
return p != nullptr ? new (p) U(std::forward<A1>(a1)) : nullptr;
|
||||
}
|
||||
|
||||
|
||||
template<typename U>
|
||||
static inline void destroy(U* p)
|
||||
{
|
||||
if (p != nullptr) {
|
||||
if (p != nullptr)
|
||||
p->~U();
|
||||
}
|
||||
(Traits::free)(p);
|
||||
aligned_free<U>(p);
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -3552,7 +3598,7 @@ private:
|
||||
Block* initialBlockPool;
|
||||
size_t initialBlockPoolSize;
|
||||
|
||||
#if !MCDBGQ_USEDEBUGFREELIST
|
||||
#ifndef MCDBGQ_USEDEBUGFREELIST
|
||||
FreeList<Block> freeList;
|
||||
#else
|
||||
debug::DebugFreeList<Block> freeList;
|
||||
@@ -3567,7 +3613,7 @@ private:
|
||||
std::atomic<std::uint32_t> nextExplicitConsumerId;
|
||||
std::atomic<std::uint32_t> globalExplicitConsumerOffset;
|
||||
|
||||
#if MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH
|
||||
#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH
|
||||
debug::DebugMutex implicitProdMutex;
|
||||
#endif
|
||||
|
||||
@@ -3639,7 +3685,3 @@ inline void swap(typename ConcurrentQueue<T, Traits>::ImplicitProducerKVP& a, ty
|
||||
#if defined(__GNUC__)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VAR)
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
Reference in New Issue
Block a user