From 98919cbb770b20e41a005a0dcdbd0ccce7681d64 Mon Sep 17 00:00:00 2001
From: patrick96
Date: Tue, 21 Sep 2021 21:00:22 +0200
Subject: [PATCH] Remove unused moodycamel concurrentqueue
---
include/components/controller.hpp | 2 -
lib/CMakeLists.txt | 6 -
.../moodycamel/blockingconcurrentqueue.h | 981 -----
.../include/moodycamel/concurrentqueue.h | 3623 -----------------
src/CMakeLists.txt | 1 -
5 files changed, 4613 deletions(-)
delete mode 100644 lib/concurrentqueue/include/moodycamel/blockingconcurrentqueue.h
delete mode 100644 lib/concurrentqueue/include/moodycamel/concurrentqueue.h
diff --git a/include/components/controller.hpp b/include/components/controller.hpp
index 3deb404a..5f016b38 100644
--- a/include/components/controller.hpp
+++ b/include/components/controller.hpp
@@ -1,7 +1,5 @@
#pragma once
-#include
-
#include
#include
#include
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index 4ab00adc..a24ef482 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -2,12 +2,6 @@
# Configure libs
#
-# Library: concurrentqueue {{{
-
-add_library(moodycamel INTERFACE)
-target_include_directories(moodycamel SYSTEM INTERFACE ${CMAKE_CURRENT_LIST_DIR}/concurrentqueue/include)
-
-# }}}
# Library: xpp {{{
set(XCB_PROTOS xproto)
diff --git a/lib/concurrentqueue/include/moodycamel/blockingconcurrentqueue.h b/lib/concurrentqueue/include/moodycamel/blockingconcurrentqueue.h
deleted file mode 100644
index 325a32bf..00000000
--- a/lib/concurrentqueue/include/moodycamel/blockingconcurrentqueue.h
+++ /dev/null
@@ -1,981 +0,0 @@
-// Provides an efficient blocking version of moodycamel::ConcurrentQueue.
-// ©2015-2016 Cameron Desrochers. Distributed under the terms of the simplified
-// BSD license, available at the top of concurrentqueue.h.
-// Uses Jeff Preshing's semaphore implementation (under the terms of its
-// separate zlib license, embedded below).
-
-#pragma once
-
-#include "concurrentqueue.h"
-#include
-#include
-#include
-#include
-#include
-
-#if defined(_WIN32)
-// Avoid including windows.h in a header; we only need a handful of
-// items, so we'll redeclare them here (this is relatively safe since
-// the API generally has to remain stable between Windows versions).
-// I know this is an ugly hack but it still beats polluting the global
-// namespace with thousands of generic names or adding a .cpp for nothing.
-extern "C" {
- struct _SECURITY_ATTRIBUTES;
- __declspec(dllimport) void* __stdcall CreateSemaphoreW(_SECURITY_ATTRIBUTES* lpSemaphoreAttributes, long lInitialCount, long lMaximumCount, const wchar_t* lpName);
- __declspec(dllimport) int __stdcall CloseHandle(void* hObject);
- __declspec(dllimport) unsigned long __stdcall WaitForSingleObject(void* hHandle, unsigned long dwMilliseconds);
- __declspec(dllimport) int __stdcall ReleaseSemaphore(void* hSemaphore, long lReleaseCount, long* lpPreviousCount);
-}
-#elif defined(__MACH__)
-#include
-#elif defined(__unix__)
-#include
-#endif
-
-namespace moodycamel
-{
-namespace details
-{
- // Code in the mpmc_sema namespace below is an adaptation of Jeff Preshing's
- // portable + lightweight semaphore implementations, originally from
- // https://github.com/preshing/cpp11-on-multicore/blob/master/common/sema.h
- // LICENSE:
- // Copyright (c) 2015 Jeff Preshing
- //
- // This software is provided 'as-is', without any express or implied
- // warranty. In no event will the authors be held liable for any damages
- // arising from the use of this software.
- //
- // Permission is granted to anyone to use this software for any purpose,
- // including commercial applications, and to alter it and redistribute it
- // freely, subject to the following restrictions:
- //
- // 1. The origin of this software must not be misrepresented; you must not
- // claim that you wrote the original software. If you use this software
- // in a product, an acknowledgement in the product documentation would be
- // appreciated but is not required.
- // 2. Altered source versions must be plainly marked as such, and must not be
- // misrepresented as being the original software.
- // 3. This notice may not be removed or altered from any source distribution.
- namespace mpmc_sema
- {
-#if defined(_WIN32)
- class Semaphore
- {
- private:
- void* m_hSema;
-
- Semaphore(const Semaphore& other) MOODYCAMEL_DELETE_FUNCTION;
- Semaphore& operator=(const Semaphore& other) MOODYCAMEL_DELETE_FUNCTION;
-
- public:
- Semaphore(int initialCount = 0)
- {
- assert(initialCount >= 0);
- const long maxLong = 0x7fffffff;
- m_hSema = CreateSemaphoreW(nullptr, initialCount, maxLong, nullptr);
- }
-
- ~Semaphore()
- {
- CloseHandle(m_hSema);
- }
-
- void wait()
- {
- const unsigned long infinite = 0xffffffff;
- WaitForSingleObject(m_hSema, infinite);
- }
-
- bool try_wait()
- {
- const unsigned long RC_WAIT_TIMEOUT = 0x00000102;
- return WaitForSingleObject(m_hSema, 0) != RC_WAIT_TIMEOUT;
- }
-
- bool timed_wait(std::uint64_t usecs)
- {
- const unsigned long RC_WAIT_TIMEOUT = 0x00000102;
- return WaitForSingleObject(m_hSema, (unsigned long)(usecs / 1000)) != RC_WAIT_TIMEOUT;
- }
-
- void signal(int count = 1)
- {
- ReleaseSemaphore(m_hSema, count, nullptr);
- }
- };
-#elif defined(__MACH__)
- //---------------------------------------------------------
- // Semaphore (Apple iOS and OSX)
- // Can't use POSIX semaphores due to http://lists.apple.com/archives/darwin-kernel/2009/Apr/msg00010.html
- //---------------------------------------------------------
- class Semaphore
- {
- private:
- semaphore_t m_sema;
-
- Semaphore(const Semaphore& other) MOODYCAMEL_DELETE_FUNCTION;
- Semaphore& operator=(const Semaphore& other) MOODYCAMEL_DELETE_FUNCTION;
-
- public:
- Semaphore(int initialCount = 0)
- {
- assert(initialCount >= 0);
- semaphore_create(mach_task_self(), &m_sema, SYNC_POLICY_FIFO, initialCount);
- }
-
- ~Semaphore()
- {
- semaphore_destroy(mach_task_self(), m_sema);
- }
-
- void wait()
- {
- semaphore_wait(m_sema);
- }
-
- bool try_wait()
- {
- return timed_wait(0);
- }
-
- bool timed_wait(std::uint64_t timeout_usecs)
- {
- mach_timespec_t ts;
- ts.tv_sec = timeout_usecs / 1000000;
- ts.tv_nsec = (timeout_usecs % 1000000) * 1000;
-
- // added in OSX 10.10: https://developer.apple.com/library/prerelease/mac/documentation/General/Reference/APIDiffsMacOSX10_10SeedDiff/modules/Darwin.html
- kern_return_t rc = semaphore_timedwait(m_sema, ts);
-
- return rc != KERN_OPERATION_TIMED_OUT;
- }
-
- void signal()
- {
- semaphore_signal(m_sema);
- }
-
- void signal(int count)
- {
- while (count-- > 0)
- {
- semaphore_signal(m_sema);
- }
- }
- };
-#elif defined(__unix__)
- //---------------------------------------------------------
- // Semaphore (POSIX, Linux)
- //---------------------------------------------------------
- class Semaphore
- {
- private:
- sem_t m_sema;
-
- Semaphore(const Semaphore& other) MOODYCAMEL_DELETE_FUNCTION;
- Semaphore& operator=(const Semaphore& other) MOODYCAMEL_DELETE_FUNCTION;
-
- public:
- Semaphore(int initialCount = 0)
- {
- assert(initialCount >= 0);
- sem_init(&m_sema, 0, initialCount);
- }
-
- ~Semaphore()
- {
- sem_destroy(&m_sema);
- }
-
- void wait()
- {
- // http://stackoverflow.com/questions/2013181/gdb-causes-sem-wait-to-fail-with-eintr-error
- int rc;
- do {
- rc = sem_wait(&m_sema);
- } while (rc == -1 && errno == EINTR);
- }
-
- bool try_wait()
- {
- int rc;
- do {
- rc = sem_trywait(&m_sema);
- } while (rc == -1 && errno == EINTR);
- return !(rc == -1 && errno == EAGAIN);
- }
-
- bool timed_wait(std::uint64_t usecs)
- {
- struct timespec ts;
- const int usecs_in_1_sec = 1000000;
- const int nsecs_in_1_sec = 1000000000;
- clock_gettime(CLOCK_REALTIME, &ts);
- ts.tv_sec += usecs / usecs_in_1_sec;
- ts.tv_nsec += (usecs % usecs_in_1_sec) * 1000;
- // sem_timedwait bombs if you have more than 1e9 in tv_nsec
- // so we have to clean things up before passing it in
- if (ts.tv_nsec > nsecs_in_1_sec) {
- ts.tv_nsec -= nsecs_in_1_sec;
- ++ts.tv_sec;
- }
-
- int rc;
- do {
- rc = sem_timedwait(&m_sema, &ts);
- } while (rc == -1 && errno == EINTR);
- return !(rc == -1 && errno == ETIMEDOUT);
- }
-
- void signal()
- {
- sem_post(&m_sema);
- }
-
- void signal(int count)
- {
- while (count-- > 0)
- {
- sem_post(&m_sema);
- }
- }
- };
-#else
-#error Unsupported platform! (No semaphore wrapper available)
-#endif
-
- //---------------------------------------------------------
- // LightweightSemaphore
- //---------------------------------------------------------
- class LightweightSemaphore
- {
- public:
- typedef std::make_signed::type ssize_t;
-
- private:
- std::atomic m_count;
- Semaphore m_sema;
-
- bool waitWithPartialSpinning(std::int64_t timeout_usecs = -1)
- {
- ssize_t oldCount;
- // Is there a better way to set the initial spin count?
- // If we lower it to 1000, testBenaphore becomes 15x slower on my Core i7-5930K Windows PC,
- // as threads start hitting the kernel semaphore.
- int spin = 10000;
- while (--spin >= 0)
- {
- oldCount = m_count.load(std::memory_order_relaxed);
- if ((oldCount > 0) && m_count.compare_exchange_strong(oldCount, oldCount - 1, std::memory_order_acquire, std::memory_order_relaxed))
- return true;
- std::atomic_signal_fence(std::memory_order_acquire); // Prevent the compiler from collapsing the loop.
- }
- oldCount = m_count.fetch_sub(1, std::memory_order_acquire);
- if (oldCount > 0)
- return true;
- if (timeout_usecs < 0)
- {
- m_sema.wait();
- return true;
- }
- if (m_sema.timed_wait((std::uint64_t)timeout_usecs))
- return true;
- // At this point, we've timed out waiting for the semaphore, but the
- // count is still decremented indicating we may still be waiting on
- // it. So we have to re-adjust the count, but only if the semaphore
- // wasn't signaled enough times for us too since then. If it was, we
- // need to release the semaphore too.
- while (true)
- {
- oldCount = m_count.load(std::memory_order_acquire);
- if (oldCount >= 0 && m_sema.try_wait())
- return true;
- if (oldCount < 0 && m_count.compare_exchange_strong(oldCount, oldCount + 1, std::memory_order_relaxed))
- return false;
- }
- }
-
- ssize_t waitManyWithPartialSpinning(ssize_t max, std::int64_t timeout_usecs = -1)
- {
- assert(max > 0);
- ssize_t oldCount;
- int spin = 10000;
- while (--spin >= 0)
- {
- oldCount = m_count.load(std::memory_order_relaxed);
- if (oldCount > 0)
- {
- ssize_t newCount = oldCount > max ? oldCount - max : 0;
- if (m_count.compare_exchange_strong(oldCount, newCount, std::memory_order_acquire, std::memory_order_relaxed))
- return oldCount - newCount;
- }
- std::atomic_signal_fence(std::memory_order_acquire);
- }
- oldCount = m_count.fetch_sub(1, std::memory_order_acquire);
- if (oldCount <= 0)
- {
- if (timeout_usecs < 0)
- m_sema.wait();
- else if (!m_sema.timed_wait((std::uint64_t)timeout_usecs))
- {
- while (true)
- {
- oldCount = m_count.load(std::memory_order_acquire);
- if (oldCount >= 0 && m_sema.try_wait())
- break;
- if (oldCount < 0 && m_count.compare_exchange_strong(oldCount, oldCount + 1, std::memory_order_relaxed))
- return 0;
- }
- }
- }
- if (max > 1)
- return 1 + tryWaitMany(max - 1);
- return 1;
- }
-
- public:
- LightweightSemaphore(ssize_t initialCount = 0) : m_count(initialCount)
- {
- assert(initialCount >= 0);
- }
-
- bool tryWait()
- {
- ssize_t oldCount = m_count.load(std::memory_order_relaxed);
- while (oldCount > 0)
- {
- if (m_count.compare_exchange_weak(oldCount, oldCount - 1, std::memory_order_acquire, std::memory_order_relaxed))
- return true;
- }
- return false;
- }
-
- void wait()
- {
- if (!tryWait())
- waitWithPartialSpinning();
- }
-
- bool wait(std::int64_t timeout_usecs)
- {
- return tryWait() || waitWithPartialSpinning(timeout_usecs);
- }
-
- // Acquires between 0 and (greedily) max, inclusive
- ssize_t tryWaitMany(ssize_t max)
- {
- assert(max >= 0);
- ssize_t oldCount = m_count.load(std::memory_order_relaxed);
- while (oldCount > 0)
- {
- ssize_t newCount = oldCount > max ? oldCount - max : 0;
- if (m_count.compare_exchange_weak(oldCount, newCount, std::memory_order_acquire, std::memory_order_relaxed))
- return oldCount - newCount;
- }
- return 0;
- }
-
- // Acquires at least one, and (greedily) at most max
- ssize_t waitMany(ssize_t max, std::int64_t timeout_usecs)
- {
- assert(max >= 0);
- ssize_t result = tryWaitMany(max);
- if (result == 0 && max > 0)
- result = waitManyWithPartialSpinning(max, timeout_usecs);
- return result;
- }
-
- ssize_t waitMany(ssize_t max)
- {
- ssize_t result = waitMany(max, -1);
- assert(result > 0);
- return result;
- }
-
- void signal(ssize_t count = 1)
- {
- assert(count >= 0);
- ssize_t oldCount = m_count.fetch_add(count, std::memory_order_release);
- ssize_t toRelease = -oldCount < count ? -oldCount : count;
- if (toRelease > 0)
- {
- m_sema.signal((int)toRelease);
- }
- }
-
- ssize_t availableApprox() const
- {
- ssize_t count = m_count.load(std::memory_order_relaxed);
- return count > 0 ? count : 0;
- }
- };
- } // end namespace mpmc_sema
-} // end namespace details
-
-
-// This is a blocking version of the queue. It has an almost identical interface to
-// the normal non-blocking version, with the addition of various wait_dequeue() methods
-// and the removal of producer-specific dequeue methods.
-template
-class BlockingConcurrentQueue
-{
-private:
- typedef ::moodycamel::ConcurrentQueue ConcurrentQueue;
- typedef details::mpmc_sema::LightweightSemaphore LightweightSemaphore;
-
-public:
- typedef typename ConcurrentQueue::producer_token_t producer_token_t;
- typedef typename ConcurrentQueue::consumer_token_t consumer_token_t;
-
- typedef typename ConcurrentQueue::index_t index_t;
- typedef typename ConcurrentQueue::size_t size_t;
- typedef typename std::make_signed::type ssize_t;
-
- static const size_t BLOCK_SIZE = ConcurrentQueue::BLOCK_SIZE;
- static const size_t EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD = ConcurrentQueue::EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD;
- static const size_t EXPLICIT_INITIAL_INDEX_SIZE = ConcurrentQueue::EXPLICIT_INITIAL_INDEX_SIZE;
- static const size_t IMPLICIT_INITIAL_INDEX_SIZE = ConcurrentQueue::IMPLICIT_INITIAL_INDEX_SIZE;
- static const size_t INITIAL_IMPLICIT_PRODUCER_HASH_SIZE = ConcurrentQueue::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE;
- static const std::uint32_t EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE = ConcurrentQueue::EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE;
- static const size_t MAX_SUBQUEUE_SIZE = ConcurrentQueue::MAX_SUBQUEUE_SIZE;
-
-public:
- // Creates a queue with at least `capacity` element slots; note that the
- // actual number of elements that can be inserted without additional memory
- // allocation depends on the number of producers and the block size (e.g. if
- // the block size is equal to `capacity`, only a single block will be allocated
- // up-front, which means only a single producer will be able to enqueue elements
- // without an extra allocation -- blocks aren't shared between producers).
- // This method is not thread safe -- it is up to the user to ensure that the
- // queue is fully constructed before it starts being used by other threads (this
- // includes making the memory effects of construction visible, possibly with a
- // memory barrier).
- explicit BlockingConcurrentQueue(size_t capacity = 6 * BLOCK_SIZE)
- : inner(capacity), sema(create(), &BlockingConcurrentQueue::template destroy)
- {
- assert(reinterpret_cast((BlockingConcurrentQueue*)1) == &((BlockingConcurrentQueue*)1)->inner && "BlockingConcurrentQueue must have ConcurrentQueue as its first member");
- if (!sema) {
- MOODYCAMEL_THROW(std::bad_alloc());
- }
- }
-
- BlockingConcurrentQueue(size_t minCapacity, size_t maxExplicitProducers, size_t maxImplicitProducers)
- : inner(minCapacity, maxExplicitProducers, maxImplicitProducers), sema(create(), &BlockingConcurrentQueue::template destroy)
- {
- assert(reinterpret_cast((BlockingConcurrentQueue*)1) == &((BlockingConcurrentQueue*)1)->inner && "BlockingConcurrentQueue must have ConcurrentQueue as its first member");
- if (!sema) {
- MOODYCAMEL_THROW(std::bad_alloc());
- }
- }
-
- // Disable copying and copy assignment
- BlockingConcurrentQueue(BlockingConcurrentQueue const&) MOODYCAMEL_DELETE_FUNCTION;
- BlockingConcurrentQueue& operator=(BlockingConcurrentQueue const&) MOODYCAMEL_DELETE_FUNCTION;
-
- // Moving is supported, but note that it is *not* a thread-safe operation.
- // Nobody can use the queue while it's being moved, and the memory effects
- // of that move must be propagated to other threads before they can use it.
- // Note: When a queue is moved, its tokens are still valid but can only be
- // used with the destination queue (i.e. semantically they are moved along
- // with the queue itself).
- BlockingConcurrentQueue(BlockingConcurrentQueue&& other) MOODYCAMEL_NOEXCEPT
- : inner(std::move(other.inner)), sema(std::move(other.sema))
- { }
-
- inline BlockingConcurrentQueue& operator=(BlockingConcurrentQueue&& other) MOODYCAMEL_NOEXCEPT
- {
- return swap_internal(other);
- }
-
- // Swaps this queue's state with the other's. Not thread-safe.
- // Swapping two queues does not invalidate their tokens, however
- // the tokens that were created for one queue must be used with
- // only the swapped queue (i.e. the tokens are tied to the
- // queue's movable state, not the object itself).
- inline void swap(BlockingConcurrentQueue& other) MOODYCAMEL_NOEXCEPT
- {
- swap_internal(other);
- }
-
-private:
- BlockingConcurrentQueue& swap_internal(BlockingConcurrentQueue& other)
- {
- if (this == &other) {
- return *this;
- }
-
- inner.swap(other.inner);
- sema.swap(other.sema);
- return *this;
- }
-
-public:
- // Enqueues a single item (by copying it).
- // Allocates memory if required. Only fails if memory allocation fails (or implicit
- // production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0,
- // or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed).
- // Thread-safe.
- inline bool enqueue(T const& item)
- {
- if (details::likely(inner.enqueue(item))) {
- sema->signal();
- return true;
- }
- return false;
- }
-
- // Enqueues a single item (by moving it, if possible).
- // Allocates memory if required. Only fails if memory allocation fails (or implicit
- // production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0,
- // or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed).
- // Thread-safe.
- inline bool enqueue(T&& item)
- {
- if (details::likely(inner.enqueue(std::move(item)))) {
- sema->signal();
- return true;
- }
- return false;
- }
-
- // Enqueues a single item (by copying it) using an explicit producer token.
- // Allocates memory if required. Only fails if memory allocation fails (or
- // Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed).
- // Thread-safe.
- inline bool enqueue(producer_token_t const& token, T const& item)
- {
- if (details::likely(inner.enqueue(token, item))) {
- sema->signal();
- return true;
- }
- return false;
- }
-
- // Enqueues a single item (by moving it, if possible) using an explicit producer token.
- // Allocates memory if required. Only fails if memory allocation fails (or
- // Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed).
- // Thread-safe.
- inline bool enqueue(producer_token_t const& token, T&& item)
- {
- if (details::likely(inner.enqueue(token, std::move(item)))) {
- sema->signal();
- return true;
- }
- return false;
- }
-
- // Enqueues several items.
- // Allocates memory if required. Only fails if memory allocation fails (or
- // implicit production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE
- // is 0, or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed).
- // Note: Use std::make_move_iterator if the elements should be moved instead of copied.
- // Thread-safe.
- template
- inline bool enqueue_bulk(It itemFirst, size_t count)
- {
- if (details::likely(inner.enqueue_bulk(std::forward(itemFirst), count))) {
- sema->signal((LightweightSemaphore::ssize_t)(ssize_t)count);
- return true;
- }
- return false;
- }
-
- // Enqueues several items using an explicit producer token.
- // Allocates memory if required. Only fails if memory allocation fails
- // (or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed).
- // Note: Use std::make_move_iterator if the elements should be moved
- // instead of copied.
- // Thread-safe.
- template
- inline bool enqueue_bulk(producer_token_t const& token, It itemFirst, size_t count)
- {
- if (details::likely(inner.enqueue_bulk(token, std::forward(itemFirst), count))) {
- sema->signal((LightweightSemaphore::ssize_t)(ssize_t)count);
- return true;
- }
- return false;
- }
-
- // Enqueues a single item (by copying it).
- // Does not allocate memory. Fails if not enough room to enqueue (or implicit
- // production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE
- // is 0).
- // Thread-safe.
- inline bool try_enqueue(T const& item)
- {
- if (inner.try_enqueue(item)) {
- sema->signal();
- return true;
- }
- return false;
- }
-
- // Enqueues a single item (by moving it, if possible).
- // Does not allocate memory (except for one-time implicit producer).
- // Fails if not enough room to enqueue (or implicit production is
- // disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0).
- // Thread-safe.
- inline bool try_enqueue(T&& item)
- {
- if (inner.try_enqueue(std::move(item))) {
- sema->signal();
- return true;
- }
- return false;
- }
-
- // Enqueues a single item (by copying it) using an explicit producer token.
- // Does not allocate memory. Fails if not enough room to enqueue.
- // Thread-safe.
- inline bool try_enqueue(producer_token_t const& token, T const& item)
- {
- if (inner.try_enqueue(token, item)) {
- sema->signal();
- return true;
- }
- return false;
- }
-
- // Enqueues a single item (by moving it, if possible) using an explicit producer token.
- // Does not allocate memory. Fails if not enough room to enqueue.
- // Thread-safe.
- inline bool try_enqueue(producer_token_t const& token, T&& item)
- {
- if (inner.try_enqueue(token, std::move(item))) {
- sema->signal();
- return true;
- }
- return false;
- }
-
- // Enqueues several items.
- // Does not allocate memory (except for one-time implicit producer).
- // Fails if not enough room to enqueue (or implicit production is
- // disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0).
- // Note: Use std::make_move_iterator if the elements should be moved
- // instead of copied.
- // Thread-safe.
- template
- inline bool try_enqueue_bulk(It itemFirst, size_t count)
- {
- if (inner.try_enqueue_bulk(std::forward(itemFirst), count)) {
- sema->signal((LightweightSemaphore::ssize_t)(ssize_t)count);
- return true;
- }
- return false;
- }
-
- // Enqueues several items using an explicit producer token.
- // Does not allocate memory. Fails if not enough room to enqueue.
- // Note: Use std::make_move_iterator if the elements should be moved
- // instead of copied.
- // Thread-safe.
- template
- inline bool try_enqueue_bulk(producer_token_t const& token, It itemFirst, size_t count)
- {
- if (inner.try_enqueue_bulk(token, std::forward(itemFirst), count)) {
- sema->signal((LightweightSemaphore::ssize_t)(ssize_t)count);
- return true;
- }
- return false;
- }
-
-
- // Attempts to dequeue from the queue.
- // Returns false if all producer streams appeared empty at the time they
- // were checked (so, the queue is likely but not guaranteed to be empty).
- // Never allocates. Thread-safe.
- template
- inline bool try_dequeue(U& item)
- {
- if (sema->tryWait()) {
- while (!inner.try_dequeue(item)) {
- continue;
- }
- return true;
- }
- return false;
- }
-
- // Attempts to dequeue from the queue using an explicit consumer token.
- // Returns false if all producer streams appeared empty at the time they
- // were checked (so, the queue is likely but not guaranteed to be empty).
- // Never allocates. Thread-safe.
- template
- inline bool try_dequeue(consumer_token_t& token, U& item)
- {
- if (sema->tryWait()) {
- while (!inner.try_dequeue(token, item)) {
- continue;
- }
- return true;
- }
- return false;
- }
-
- // Attempts to dequeue several elements from the queue.
- // Returns the number of items actually dequeued.
- // Returns 0 if all producer streams appeared empty at the time they
- // were checked (so, the queue is likely but not guaranteed to be empty).
- // Never allocates. Thread-safe.
- template
- inline size_t try_dequeue_bulk(It itemFirst, size_t max)
- {
- size_t count = 0;
- max = (size_t)sema->tryWaitMany((LightweightSemaphore::ssize_t)(ssize_t)max);
- while (count != max) {
- count += inner.template try_dequeue_bulk(itemFirst, max - count);
- }
- return count;
- }
-
- // Attempts to dequeue several elements from the queue using an explicit consumer token.
- // Returns the number of items actually dequeued.
- // Returns 0 if all producer streams appeared empty at the time they
- // were checked (so, the queue is likely but not guaranteed to be empty).
- // Never allocates. Thread-safe.
- template
- inline size_t try_dequeue_bulk(consumer_token_t& token, It itemFirst, size_t max)
- {
- size_t count = 0;
- max = (size_t)sema->tryWaitMany((LightweightSemaphore::ssize_t)(ssize_t)max);
- while (count != max) {
- count += inner.template try_dequeue_bulk(token, itemFirst, max - count);
- }
- return count;
- }
-
-
-
- // Blocks the current thread until there's something to dequeue, then
- // dequeues it.
- // Never allocates. Thread-safe.
- template
- inline void wait_dequeue(U& item)
- {
- sema->wait();
- while (!inner.try_dequeue(item)) {
- continue;
- }
- }
-
- // Blocks the current thread until either there's something to dequeue
- // or the timeout (specified in microseconds) expires. Returns false
- // without setting `item` if the timeout expires, otherwise assigns
- // to `item` and returns true.
- // Using a negative timeout indicates an indefinite timeout,
- // and is thus functionally equivalent to calling wait_dequeue.
- // Never allocates. Thread-safe.
- template
- inline bool wait_dequeue_timed(U& item, std::int64_t timeout_usecs)
- {
- if (!sema->wait(timeout_usecs)) {
- return false;
- }
- while (!inner.try_dequeue(item)) {
- continue;
- }
- return true;
- }
-
- // Blocks the current thread until either there's something to dequeue
- // or the timeout expires. Returns false without setting `item` if the
- // timeout expires, otherwise assigns to `item` and returns true.
- // Never allocates. Thread-safe.
- template
- inline bool wait_dequeue_timed(U& item, std::chrono::duration const& timeout)
- {
- return wait_dequeue_timed(item, std::chrono::duration_cast(timeout).count());
- }
-
- // Blocks the current thread until there's something to dequeue, then
- // dequeues it using an explicit consumer token.
- // Never allocates. Thread-safe.
- template
- inline void wait_dequeue(consumer_token_t& token, U& item)
- {
- sema->wait();
- while (!inner.try_dequeue(token, item)) {
- continue;
- }
- }
-
- // Blocks the current thread until either there's something to dequeue
- // or the timeout (specified in microseconds) expires. Returns false
- // without setting `item` if the timeout expires, otherwise assigns
- // to `item` and returns true.
- // Using a negative timeout indicates an indefinite timeout,
- // and is thus functionally equivalent to calling wait_dequeue.
- // Never allocates. Thread-safe.
- template
- inline bool wait_dequeue_timed(consumer_token_t& token, U& item, std::int64_t timeout_usecs)
- {
- if (!sema->wait(timeout_usecs)) {
- return false;
- }
- while (!inner.try_dequeue(token, item)) {
- continue;
- }
- return true;
- }
-
- // Blocks the current thread until either there's something to dequeue
- // or the timeout expires. Returns false without setting `item` if the
- // timeout expires, otherwise assigns to `item` and returns true.
- // Never allocates. Thread-safe.
- template
- inline bool wait_dequeue_timed(consumer_token_t& token, U& item, std::chrono::duration const& timeout)
- {
- return wait_dequeue_timed(token, item, std::chrono::duration_cast(timeout).count());
- }
-
- // Attempts to dequeue several elements from the queue.
- // Returns the number of items actually dequeued, which will
- // always be at least one (this method blocks until the queue
- // is non-empty) and at most max.
- // Never allocates. Thread-safe.
- template
- inline size_t wait_dequeue_bulk(It itemFirst, size_t max)
- {
- size_t count = 0;
- max = (size_t)sema->waitMany((LightweightSemaphore::ssize_t)(ssize_t)max);
- while (count != max) {
- count += inner.template try_dequeue_bulk(itemFirst, max - count);
- }
- return count;
- }
-
- // Attempts to dequeue several elements from the queue.
- // Returns the number of items actually dequeued, which can
- // be 0 if the timeout expires while waiting for elements,
- // and at most max.
- // Using a negative timeout indicates an indefinite timeout,
- // and is thus functionally equivalent to calling wait_dequeue_bulk.
- // Never allocates. Thread-safe.
- template
- inline size_t wait_dequeue_bulk_timed(It itemFirst, size_t max, std::int64_t timeout_usecs)
- {
- size_t count = 0;
- max = (size_t)sema->waitMany((LightweightSemaphore::ssize_t)(ssize_t)max, timeout_usecs);
- while (count != max) {
- count += inner.template try_dequeue_bulk(itemFirst, max - count);
- }
- return count;
- }
-
- // Attempts to dequeue several elements from the queue.
- // Returns the number of items actually dequeued, which can
- // be 0 if the timeout expires while waiting for elements,
- // and at most max.
- // Never allocates. Thread-safe.
- template
- inline size_t wait_dequeue_bulk_timed(It itemFirst, size_t max, std::chrono::duration const& timeout)
- {
- return wait_dequeue_bulk_timed(itemFirst, max, std::chrono::duration_cast(timeout).count());
- }
-
- // Attempts to dequeue several elements from the queue using an explicit consumer token.
- // Returns the number of items actually dequeued, which will
- // always be at least one (this method blocks until the queue
- // is non-empty) and at most max.
- // Never allocates. Thread-safe.
- template
- inline size_t wait_dequeue_bulk(consumer_token_t& token, It itemFirst, size_t max)
- {
- size_t count = 0;
- max = (size_t)sema->waitMany((LightweightSemaphore::ssize_t)(ssize_t)max);
- while (count != max) {
- count += inner.template try_dequeue_bulk(token, itemFirst, max - count);
- }
- return count;
- }
-
- // Attempts to dequeue several elements from the queue using an explicit consumer token.
- // Returns the number of items actually dequeued, which can
- // be 0 if the timeout expires while waiting for elements,
- // and at most max.
- // Using a negative timeout indicates an indefinite timeout,
- // and is thus functionally equivalent to calling wait_dequeue_bulk.
- // Never allocates. Thread-safe.
- template
- inline size_t wait_dequeue_bulk_timed(consumer_token_t& token, It itemFirst, size_t max, std::int64_t timeout_usecs)
- {
- size_t count = 0;
- max = (size_t)sema->waitMany((LightweightSemaphore::ssize_t)(ssize_t)max, timeout_usecs);
- while (count != max) {
- count += inner.template try_dequeue_bulk(token, itemFirst, max - count);
- }
- return count;
- }
-
- // Attempts to dequeue several elements from the queue using an explicit consumer token.
- // Returns the number of items actually dequeued, which can
- // be 0 if the timeout expires while waiting for elements,
- // and at most max.
- // Never allocates. Thread-safe.
- template
- inline size_t wait_dequeue_bulk_timed(consumer_token_t& token, It itemFirst, size_t max, std::chrono::duration const& timeout)
- {
- return wait_dequeue_bulk_timed(token, itemFirst, max, std::chrono::duration_cast(timeout).count());
- }
-
-
- // Returns an estimate of the total number of elements currently in the queue. This
- // estimate is only accurate if the queue has completely stabilized before it is called
- // (i.e. all enqueue and dequeue operations have completed and their memory effects are
- // visible on the calling thread, and no further operations start while this method is
- // being called).
- // Thread-safe.
- inline size_t size_approx() const
- {
- return (size_t)sema->availableApprox();
- }
-
-
- // Returns true if the underlying atomic variables used by
- // the queue are lock-free (they should be on most platforms).
- // Thread-safe.
- static bool is_lock_free()
- {
- return ConcurrentQueue::is_lock_free();
- }
-
-
-private:
- template
- static inline U* create()
- {
- auto p = (Traits::malloc)(sizeof(U));
- return p != nullptr ? new (p) U : nullptr;
- }
-
- template
- static inline U* create(A1&& a1)
- {
- auto p = (Traits::malloc)(sizeof(U));
- return p != nullptr ? new (p) U(std::forward(a1)) : nullptr;
- }
-
- template
- static inline void destroy(U* p)
- {
- if (p != nullptr) {
- p->~U();
- }
- (Traits::free)(p);
- }
-
-private:
- ConcurrentQueue inner;
- std::unique_ptr sema;
-};
-
-
-template
-inline void swap(BlockingConcurrentQueue& a, BlockingConcurrentQueue& b) MOODYCAMEL_NOEXCEPT
-{
- a.swap(b);
-}
-
-} // end namespace moodycamel
diff --git a/lib/concurrentqueue/include/moodycamel/concurrentqueue.h b/lib/concurrentqueue/include/moodycamel/concurrentqueue.h
deleted file mode 100644
index 9db83a13..00000000
--- a/lib/concurrentqueue/include/moodycamel/concurrentqueue.h
+++ /dev/null
@@ -1,3623 +0,0 @@
-// 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:
-// http://moodycamel.com/blog/2014/detailed-design-of-a-lock-free-queue
-
-// Simplified BSD license:
-// Copyright (c) 2013-2016, Cameron Desrochers.
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without modification,
-// are permitted provided that the following conditions are met:
-//
-// - Redistributions of source code must retain the above copyright notice, this list of
-// conditions and the following disclaimer.
-// - Redistributions in binary form must reproduce the above copyright notice, this list of
-// conditions and the following disclaimer in the documentation and/or other materials
-// provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
-// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
-// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
-// OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
-// TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
-// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-
-#pragma once
-
-#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
-// upon assigning any computed values)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wconversion"
-
-#ifdef MCDBGQ_USE_RELACY
-#pragma GCC diagnostic ignored "-Wint-to-pointer-cast"
-#endif
-#endif
-
-#if defined(__APPLE__)
-#include "TargetConditionals.h"
-#endif
-
-#ifdef MCDBGQ_USE_RELACY
-#include "relacy/relacy_std.hpp"
-#include "relacy_shims.h"
-// We only use malloc/free anyway, and the delete macro messes up `= delete` method declarations.
-// We'll override the default trait malloc ourselves without a macro.
-#undef new
-#undef delete
-#undef malloc
-#undef free
-#else
-#include // Requires C++11. Sorry VS2010.
-#include
-#endif
-#include // for max_align_t
-#include
-#include
-#include
-#include
-#include
-#include
-#include // for CHAR_BIT
-#include
-#include // partly for __WINPTHREADS_VERSION if on MinGW-w64 w/ POSIX threading
-
-// Platform-specific definitions of a numeric thread ID type and an invalid value
-namespace moodycamel { namespace details {
- template struct thread_id_converter {
- typedef thread_id_t thread_id_numeric_size_t;
- typedef thread_id_t thread_id_hash_t;
- static thread_id_hash_t prehash(thread_id_t const& x) { return x; }
- };
-} }
-#if defined(MCDBGQ_USE_RELACY)
-namespace moodycamel { namespace details {
- typedef std::uint32_t thread_id_t;
- static const thread_id_t invalid_thread_id = 0xFFFFFFFFU;
- static const thread_id_t invalid_thread_id2 = 0xFFFFFFFEU;
- static inline thread_id_t thread_id() { return rl::thread_index(); }
-} }
-#elif defined(_WIN32) || defined(__WINDOWS__) || defined(__WIN32__)
-// No sense pulling in windows.h in a header, we'll manually declare the function
-// we use and rely on backwards-compatibility for this not to break
-extern "C" __declspec(dllimport) unsigned long __stdcall GetCurrentThreadId(void);
-namespace moodycamel { namespace details {
- static_assert(sizeof(unsigned long) == sizeof(std::uint32_t), "Expected size of unsigned long to be 32 bits on Windows");
- typedef std::uint32_t thread_id_t;
- static const thread_id_t invalid_thread_id = 0; // See http://blogs.msdn.com/b/oldnewthing/archive/2004/02/23/78395.aspx
- static const thread_id_t invalid_thread_id2 = 0xFFFFFFFFU; // Not technically guaranteed to be invalid, but is never used in practice. Note that all Win32 thread IDs are presently multiples of 4.
- static inline thread_id_t thread_id() { return static_cast(::GetCurrentThreadId()); }
-} }
-#elif defined(__arm__) || defined(_M_ARM) || defined(__aarch64__) || (defined(__APPLE__) && TARGET_OS_IPHONE)
-namespace moodycamel { namespace details {
- static_assert(sizeof(std::thread::id) == 4 || sizeof(std::thread::id) == 8, "std::thread::id is expected to be either 4 or 8 bytes");
-
- typedef std::thread::id thread_id_t;
- static const thread_id_t invalid_thread_id; // Default ctor creates invalid ID
-
- // Note we don't define a invalid_thread_id2 since std::thread::id doesn't have one; it's
- // only used if MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED is defined anyway, which it won't
- // be.
- static inline thread_id_t thread_id() { return std::this_thread::get_id(); }
-
- template struct thread_id_size { };
- template<> struct thread_id_size<4> { typedef std::uint32_t numeric_t; };
- template<> struct thread_id_size<8> { typedef std::uint64_t numeric_t; };
-
- template<> struct thread_id_converter {
- typedef thread_id_size::numeric_t thread_id_numeric_size_t;
-#ifndef __APPLE__
- typedef std::size_t thread_id_hash_t;
-#else
- typedef thread_id_numeric_size_t thread_id_hash_t;
-#endif
-
- static thread_id_hash_t prehash(thread_id_t const& x)
- {
-#ifndef __APPLE__
- return std::hash()(x);
-#else
- return *reinterpret_cast(&x);
-#endif
- }
- };
-} }
-#else
-// Use a nice trick from this answer: http://stackoverflow.com/a/8438730/21475
-// In order to get a numeric thread ID in a platform-independent way, we use a thread-local
-// static variable's address as a thread identifier :-)
-#if defined(__GNUC__) || defined(__INTEL_COMPILER)
-#define MOODYCAMEL_THREADLOCAL __thread
-#elif defined(_MSC_VER)
-#define MOODYCAMEL_THREADLOCAL __declspec(thread)
-#else
-// Assume C++11 compliant compiler
-#define MOODYCAMEL_THREADLOCAL thread_local
-#endif
-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(&x); }
-} }
-#endif
-
-// Exceptions
-#ifndef MOODYCAMEL_EXCEPTIONS_ENABLED
-#if (defined(_MSC_VER) && defined(_CPPUNWIND)) || (defined(__GNUC__) && defined(__EXCEPTIONS)) || (!defined(_MSC_VER) && !defined(__GNUC__))
-#define MOODYCAMEL_EXCEPTIONS_ENABLED
-#endif
-#endif
-#ifdef MOODYCAMEL_EXCEPTIONS_ENABLED
-#define MOODYCAMEL_TRY try
-#define MOODYCAMEL_CATCH(...) catch(__VA_ARGS__)
-#define MOODYCAMEL_RETHROW throw
-#define MOODYCAMEL_THROW(expr) throw (expr)
-#else
-#define MOODYCAMEL_TRY if (true)
-#define MOODYCAMEL_CATCH(...) else if (false)
-#define MOODYCAMEL_RETHROW
-#define MOODYCAMEL_THROW(expr)
-#endif
-
-#ifndef MOODYCAMEL_NOEXCEPT
-#if !defined(MOODYCAMEL_EXCEPTIONS_ENABLED)
-#define MOODYCAMEL_NOEXCEPT
-#define MOODYCAMEL_NOEXCEPT_CTOR(type, valueType, expr) true
-#define MOODYCAMEL_NOEXCEPT_ASSIGN(type, valueType, expr) true
-#elif defined(_MSC_VER) && defined(_NOEXCEPT) && _MSC_VER < 1800
-// VS2012's std::is_nothrow_[move_]constructible is broken and returns true when it shouldn't :-(
-// We have to assume *all* non-trivial constructors may throw on VS2012!
-#define MOODYCAMEL_NOEXCEPT _NOEXCEPT
-#define MOODYCAMEL_NOEXCEPT_CTOR(type, valueType, expr) (std::is_rvalue_reference::value && std::is_move_constructible::value ? std::is_trivially_move_constructible::value : std::is_trivially_copy_constructible::value)
-#define MOODYCAMEL_NOEXCEPT_ASSIGN(type, valueType, expr) ((std::is_rvalue_reference::value && std::is_move_assignable::value ? std::is_trivially_move_assignable::value || std::is_nothrow_move_assignable::value : std::is_trivially_copy_assignable::value || std::is_nothrow_copy_assignable::value) && MOODYCAMEL_NOEXCEPT_CTOR(type, valueType, expr))
-#elif defined(_MSC_VER) && defined(_NOEXCEPT) && _MSC_VER < 1900
-#define MOODYCAMEL_NOEXCEPT _NOEXCEPT
-#define MOODYCAMEL_NOEXCEPT_CTOR(type, valueType, expr) (std::is_rvalue_reference::value && std::is_move_constructible::value ? std::is_trivially_move_constructible::value || std::is_nothrow_move_constructible::value : std::is_trivially_copy_constructible::value || std::is_nothrow_copy_constructible::value)
-#define MOODYCAMEL_NOEXCEPT_ASSIGN(type, valueType, expr) ((std::is_rvalue_reference::value && std::is_move_assignable::value ? std::is_trivially_move_assignable::value || std::is_nothrow_move_assignable::value : std::is_trivially_copy_assignable::value || std::is_nothrow_copy_assignable::value) && MOODYCAMEL_NOEXCEPT_CTOR(type, valueType, expr))
-#else
-#define MOODYCAMEL_NOEXCEPT noexcept
-#define MOODYCAMEL_NOEXCEPT_CTOR(type, valueType, expr) noexcept(expr)
-#define MOODYCAMEL_NOEXCEPT_ASSIGN(type, valueType, expr) noexcept(expr)
-#endif
-#endif
-
-#ifndef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED
-#ifdef MCDBGQ_USE_RELACY
-#define MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED
-#else
-// VS2013 doesn't support `thread_local`, and MinGW-w64 w/ POSIX threading has a crippling bug: http://sourceforge.net/p/mingw-w64/bugs/445
-// g++ <=4.7 doesn't support thread_local either.
-// Finally, iOS/ARM doesn't have support for it either, and g++/ARM allows it to compile but it's unconfirmed to actually work
-#if (!defined(_MSC_VER) || _MSC_VER >= 1900) && (!defined(__MINGW32__) && !defined(__MINGW64__) || !defined(__WINPTHREADS_VERSION)) && (!defined(__GNUC__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) && (!defined(__APPLE__) || !TARGET_OS_IPHONE) && !defined(__arm__) && !defined(_M_ARM) && !defined(__aarch64__)
-// Assume `thread_local` is fully supported in all other C++11 compilers/platforms
-//#define MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED // always disabled for now since several users report having problems with it on
-#endif
-#endif
-#endif
-
-// VS2012 doesn't support deleted functions.
-// In this case, we declare the function normally but don't define it. A link error will be generated if the function is called.
-#ifndef MOODYCAMEL_DELETE_FUNCTION
-#if defined(_MSC_VER) && _MSC_VER < 1800
-#define MOODYCAMEL_DELETE_FUNCTION
-#else
-#define MOODYCAMEL_DELETE_FUNCTION = delete
-#endif
-#endif
-
-// Compiler-specific likely/unlikely hints
-namespace moodycamel { namespace details {
-#if defined(__GNUC__)
- inline bool likely(bool x) { return __builtin_expect((x), true); }
- inline bool unlikely(bool x) { return __builtin_expect((x), false); }
-#else
- inline bool likely(bool x) { return x; }
- inline bool unlikely(bool x) { return x; }
-#endif
-} }
-
-#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG
-#include "internal/concurrentqueue_internal_debug.h"
-#endif
-
-namespace moodycamel {
-namespace details {
- template
- struct const_numeric_max {
- static_assert(std::is_integral::value, "const_numeric_max can only be used with integers");
- static const T value = std::numeric_limits::is_signed
- ? (static_cast(1) << (sizeof(T) * CHAR_BIT - 1)) - static_cast(1)
- : static_cast(-1);
- };
-
-#if defined(__GNUC__) && !defined( __clang__ )
- typedef ::max_align_t max_align_t; // GCC forgot to add it to std:: for a while
-#else
- typedef std::max_align_t max_align_t; // Others (e.g. MSVC) insist it can *only* be accessed via std::
-#endif
-}
-
-// Default traits for the ConcurrentQueue. To change some of the
-// traits without re-implementing all of them, inherit from this
-// struct and shadow the declarations you wish to be different;
-// since the traits are used as a template type parameter, the
-// shadowed declarations will be used where defined, and the defaults
-// otherwise.
-struct ConcurrentQueueDefaultTraits
-{
- // General-purpose size type. std::size_t is strongly recommended.
- typedef std::size_t size_t;
-
- // The type used for the enqueue and dequeue indices. Must be at least as
- // large as size_t. Should be significantly larger than the number of elements
- // you expect to hold at once, especially if you have a high turnover rate;
- // for example, on 32-bit x86, if you expect to have over a hundred million
- // elements or pump several million elements through your queue in a very
- // short space of time, using a 32-bit type *may* trigger a race condition.
- // A 64-bit int type is recommended in that case, and in practice will
- // prevent a race condition no matter the usage of the queue. Note that
- // whether the queue is lock-free with a 64-int type depends on the whether
- // std::atomic is lock-free, which is platform-specific.
- typedef std::size_t index_t;
-
- // Internally, all elements are enqueued and dequeued from multi-element
- // blocks; this is the smallest controllable unit. If you expect few elements
- // but many producers, a smaller block size should be favoured. For few producers
- // and/or many elements, a larger block size is preferred. A sane default
- // is provided. Must be a power of 2.
- static const size_t BLOCK_SIZE = 32;
-
- // For explicit producers (i.e. when using a producer token), the block is
- // checked for being empty by iterating through a list of flags, one per element.
- // For large block sizes, this is too inefficient, and switching to an atomic
- // counter-based approach is faster. The switch is made for block sizes strictly
- // larger than this threshold.
- static const size_t EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD = 32;
-
- // How many full blocks can be expected for a single explicit producer? This should
- // reflect that number's maximum for optimal performance. Must be a power of 2.
- static const size_t EXPLICIT_INITIAL_INDEX_SIZE = 32;
-
- // How many full blocks can be expected for a single implicit producer? This should
- // reflect that number's maximum for optimal performance. Must be a power of 2.
- static const size_t IMPLICIT_INITIAL_INDEX_SIZE = 32;
-
- // The initial size of the hash table mapping thread IDs to implicit producers.
- // Note that the hash is resized every time it becomes half full.
- // Must be a power of two, and either 0 or at least 1. If 0, implicit production
- // (using the enqueue methods without an explicit producer token) is disabled.
- static const size_t INITIAL_IMPLICIT_PRODUCER_HASH_SIZE = 32;
-
- // Controls the number of items that an explicit consumer (i.e. one with a token)
- // must consume before it causes all consumers to rotate and move on to the next
- // internal queue.
- static const std::uint32_t EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE = 256;
-
- // The maximum number of elements (inclusive) that can be enqueued to a sub-queue.
- // Enqueue operations that would cause this limit to be surpassed will fail. Note
- // that this limit is enforced at the block level (for performance reasons), i.e.
- // it's rounded up to the nearest block size.
- static const size_t MAX_SUBQUEUE_SIZE = details::const_numeric_max::value;
-
-
-#ifndef MCDBGQ_USE_RELACY
- // Memory allocation can be customized if needed.
- // malloc should return nullptr on failure, and handle alignment like std::malloc.
-#if defined(malloc) || defined(free)
- // Gah, this is 2015, stop defining macros that break standard code already!
- // Work around malloc/free being special macros:
- static inline void* WORKAROUND_malloc(size_t size) { return malloc(size); }
- static inline void WORKAROUND_free(void* ptr) { return free(ptr); }
- static inline void* (malloc)(size_t size) { return WORKAROUND_malloc(size); }
- static inline void (free)(void* ptr) { return WORKAROUND_free(ptr); }
-#else
- static inline void* malloc(size_t size) { return std::malloc(size); }
- static inline void free(void* ptr) { return std::free(ptr); }
-#endif
-#else
- // Debug versions when running under the Relacy race detector (ignore
- // these in user code)
- static inline void* malloc(size_t size) { return rl::rl_malloc(size, $); }
- static inline void free(void* ptr) { return rl::rl_free(ptr, $); }
-#endif
-};
-
-
-// When producing or consuming many elements, the most efficient way is to:
-// 1) Use one of the bulk-operation methods of the queue with a token
-// 2) Failing that, use the bulk-operation methods without a token
-// 3) Failing that, create a token and use that with the single-item methods
-// 4) Failing that, use the single-parameter methods of the queue
-// Having said that, don't create tokens willy-nilly -- ideally there should be
-// a maximum of one token per thread (of each kind).
-struct ProducerToken;
-struct ConsumerToken;
-
-template class ConcurrentQueue;
-template class BlockingConcurrentQueue;
-class ConcurrentQueueTests;
-
-
-namespace details
-{
- struct ConcurrentQueueProducerTypelessBase
- {
- ConcurrentQueueProducerTypelessBase* next;
- std::atomic inactive;
- ProducerToken* token;
-
- ConcurrentQueueProducerTypelessBase()
- : next(nullptr), inactive(false), token(nullptr)
- {
- }
- };
-
- template struct _hash_32_or_64 {
- static inline std::uint32_t hash(std::uint32_t h)
- {
- // MurmurHash3 finalizer -- see https://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp
- // Since the thread ID is already unique, all we really want to do is propagate that
- // uniqueness evenly across all the bits, so that we can use a subset of the bits while
- // reducing collisions significantly
- h ^= h >> 16;
- h *= 0x85ebca6b;
- h ^= h >> 13;
- h *= 0xc2b2ae35;
- return h ^ (h >> 16);
- }
- };
- template<> struct _hash_32_or_64<1> {
- static inline std::uint64_t hash(std::uint64_t h)
- {
- h ^= h >> 33;
- h *= 0xff51afd7ed558ccd;
- h ^= h >> 33;
- h *= 0xc4ceb9fe1a85ec53;
- return h ^ (h >> 33);
- }
- };
- template struct hash_32_or_64 : public _hash_32_or_64<(size > 4)> { };
-
- static inline size_t hash_thread_id(thread_id_t id)
- {
- static_assert(sizeof(thread_id_t) <= 8, "Expected a platform where thread IDs are at most 64-bit values");
- return static_cast(hash_32_or_64::thread_id_hash_t)>::hash(
- thread_id_converter::prehash(id)));
- }
-
- template
- static inline bool circular_less_than(T a, T b)
- {
-#ifdef _MSC_VER
-#pragma warning(push)
-#pragma warning(disable: 4554)
-#endif
- static_assert(std::is_integral::value && !std::numeric_limits::is_signed, "circular_less_than is intended to be used only with unsigned integer types");
- return static_cast(a - b) > static_cast(static_cast(1) << static_cast(sizeof(T) * CHAR_BIT - 1));
-#ifdef _MSC_VER
-#pragma warning(pop)
-#endif
- }
-
- template
- static inline char* align_for(char* ptr)
- {
- const std::size_t alignment = std::alignment_of::value;
- return ptr + (alignment - (reinterpret_cast(ptr) % alignment)) % alignment;
- }
-
- template
- static inline T ceil_to_pow_2(T x)
- {
- static_assert(std::is_integral::value && !std::numeric_limits::is_signed, "ceil_to_pow_2 is intended to be used only with unsigned integer types");
-
- // Adapted from http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
- --x;
- x |= x >> 1;
- x |= x >> 2;
- x |= x >> 4;
- for (std::size_t i = 1; i < sizeof(T); i <<= 1) {
- x |= x >> (i << 3);
- }
- ++x;
- return x;
- }
-
- template
- static inline void swap_relaxed(std::atomic& left, std::atomic& right)
- {
- T temp = std::move(left.load(std::memory_order_relaxed));
- left.store(std::move(right.load(std::memory_order_relaxed)), std::memory_order_relaxed);
- right.store(std::move(temp), std::memory_order_relaxed);
- }
-
- template
- static inline T const& nomove(T const& x)
- {
- return x;
- }
-
- template
- struct nomove_if
- {
- template
- static inline T const& eval(T const& x)
- {
- return x;
- }
- };
-
- template<>
- struct nomove_if
- {
- template
- static inline auto eval(U&& x)
- -> decltype(std::forward(x))
- {
- return std::forward(x);
- }
- };
-
- template
- static inline auto deref_noexcept(It& it) MOODYCAMEL_NOEXCEPT -> decltype(*it)
- {
- return *it;
- }
-
-#if defined(__clang__) || !defined(__GNUC__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)
- template struct is_trivially_destructible : std::is_trivially_destructible { };
-#else
- template struct is_trivially_destructible : std::has_trivial_destructor { };
-#endif
-
-#ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED
-#ifdef MCDBGQ_USE_RELACY
- typedef RelacyThreadExitListener ThreadExitListener;
- typedef RelacyThreadExitNotifier ThreadExitNotifier;
-#else
- struct ThreadExitListener
- {
- typedef void (*callback_t)(void*);
- callback_t callback;
- void* userData;
-
- ThreadExitListener* next; // reserved for use by the ThreadExitNotifier
- };
-
-
- class ThreadExitNotifier
- {
- public:
- static void subscribe(ThreadExitListener* listener)
- {
- auto& tlsInst = instance();
- listener->next = tlsInst.tail;
- tlsInst.tail = listener;
- }
-
- static void unsubscribe(ThreadExitListener* listener)
- {
- auto& tlsInst = instance();
- ThreadExitListener** prev = &tlsInst.tail;
- for (auto ptr = tlsInst.tail; ptr != nullptr; ptr = ptr->next) {
- if (ptr == listener) {
- *prev = ptr->next;
- break;
- }
- prev = &ptr->next;
- }
- }
-
- private:
- ThreadExitNotifier() : tail(nullptr) { }
- ThreadExitNotifier(ThreadExitNotifier const&) MOODYCAMEL_DELETE_FUNCTION;
- ThreadExitNotifier& operator=(ThreadExitNotifier const&) MOODYCAMEL_DELETE_FUNCTION;
-
- ~ThreadExitNotifier()
- {
- // This thread is about to exit, let everyone know!
- assert(this == &instance() && "If this assert fails, you likely have a buggy compiler! Change the preprocessor conditions such that MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED is no longer defined.");
- for (auto ptr = tail; ptr != nullptr; ptr = ptr->next) {
- ptr->callback(ptr->userData);
- }
- }
-
- // Thread-local
- static inline ThreadExitNotifier& instance()
- {
- static thread_local ThreadExitNotifier notifier;
- return notifier;
- }
-
- private:
- ThreadExitListener* tail;
- };
-#endif
-#endif
-
- template struct static_is_lock_free_num { enum { value = 0 }; };
- template<> struct static_is_lock_free_num { enum { value = ATOMIC_CHAR_LOCK_FREE }; };
- template<> struct static_is_lock_free_num { enum { value = ATOMIC_SHORT_LOCK_FREE }; };
- template<> struct static_is_lock_free_num { enum { value = ATOMIC_INT_LOCK_FREE }; };
- template<> struct static_is_lock_free_num { enum { value = ATOMIC_LONG_LOCK_FREE }; };
- template<> struct static_is_lock_free_num { enum { value = ATOMIC_LLONG_LOCK_FREE }; };
- template struct static_is_lock_free : static_is_lock_free_num::type> { };
- template<> struct static_is_lock_free { enum { value = ATOMIC_BOOL_LOCK_FREE }; };
- template struct static_is_lock_free { enum { value = ATOMIC_POINTER_LOCK_FREE }; };
-}
-
-
-struct ProducerToken
-{
- template
- explicit ProducerToken(ConcurrentQueue& queue);
-
- template
- explicit ProducerToken(BlockingConcurrentQueue& queue);
-
- ProducerToken(ProducerToken&& other) MOODYCAMEL_NOEXCEPT
- : producer(other.producer)
- {
- other.producer = nullptr;
- if (producer != nullptr) {
- producer->token = this;
- }
- }
-
- inline ProducerToken& operator=(ProducerToken&& other) MOODYCAMEL_NOEXCEPT
- {
- swap(other);
- return *this;
- }
-
- void swap(ProducerToken& other) MOODYCAMEL_NOEXCEPT
- {
- std::swap(producer, other.producer);
- if (producer != nullptr) {
- producer->token = this;
- }
- if (other.producer != nullptr) {
- other.producer->token = &other;
- }
- }
-
- // A token is always valid unless:
- // 1) Memory allocation failed during construction
- // 2) It was moved via the move constructor
- // (Note: assignment does a swap, leaving both potentially valid)
- // 3) The associated queue was destroyed
- // Note that if valid() returns true, that only indicates
- // that the token is valid for use with a specific queue,
- // but not which one; that's up to the user to track.
- inline bool valid() const { return producer != nullptr; }
-
- ~ProducerToken()
- {
- if (producer != nullptr) {
- producer->token = nullptr;
- producer->inactive.store(true, std::memory_order_release);
- }
- }
-
- // Disable copying and assignment
- ProducerToken(ProducerToken const&) MOODYCAMEL_DELETE_FUNCTION;
- ProducerToken& operator=(ProducerToken const&) MOODYCAMEL_DELETE_FUNCTION;
-
-private:
- template friend class ConcurrentQueue;
- friend class ConcurrentQueueTests;
-
-protected:
- details::ConcurrentQueueProducerTypelessBase* producer;
-};
-
-
-struct ConsumerToken
-{
- template
- explicit ConsumerToken(ConcurrentQueue& q);
-
- template
- explicit ConsumerToken(BlockingConcurrentQueue& q);
-
- ConsumerToken(ConsumerToken&& other) MOODYCAMEL_NOEXCEPT
- : initialOffset(other.initialOffset), lastKnownGlobalOffset(other.lastKnownGlobalOffset), itemsConsumedFromCurrent(other.itemsConsumedFromCurrent), currentProducer(other.currentProducer), desiredProducer(other.desiredProducer)
- {
- }
-
- inline ConsumerToken& operator=(ConsumerToken&& other) MOODYCAMEL_NOEXCEPT
- {
- swap(other);
- return *this;
- }
-
- void swap(ConsumerToken& other) MOODYCAMEL_NOEXCEPT
- {
- std::swap(initialOffset, other.initialOffset);
- std::swap(lastKnownGlobalOffset, other.lastKnownGlobalOffset);
- std::swap(itemsConsumedFromCurrent, other.itemsConsumedFromCurrent);
- std::swap(currentProducer, other.currentProducer);
- std::swap(desiredProducer, other.desiredProducer);
- }
-
- // Disable copying and assignment
- ConsumerToken(ConsumerToken const&) MOODYCAMEL_DELETE_FUNCTION;
- ConsumerToken& operator=(ConsumerToken const&) MOODYCAMEL_DELETE_FUNCTION;
-
-private:
- template friend class ConcurrentQueue;
- friend class ConcurrentQueueTests;
-
-private: // but shared with ConcurrentQueue
- std::uint32_t initialOffset;
- std::uint32_t lastKnownGlobalOffset;
- std::uint32_t itemsConsumedFromCurrent;
- details::ConcurrentQueueProducerTypelessBase* currentProducer;
- details::ConcurrentQueueProducerTypelessBase* desiredProducer;
-};
-
-// Need to forward-declare this swap because it's in a namespace.
-// See http://stackoverflow.com/questions/4492062/why-does-a-c-friend-class-need-a-forward-declaration-only-in-other-namespaces
-template
-inline void swap(typename ConcurrentQueue::ImplicitProducerKVP& a, typename ConcurrentQueue::ImplicitProducerKVP& b) MOODYCAMEL_NOEXCEPT;
-
-
-template
-class ConcurrentQueue
-{
-public:
- typedef ::moodycamel::ProducerToken producer_token_t;
- typedef ::moodycamel::ConsumerToken consumer_token_t;
-
- typedef typename Traits::index_t index_t;
- typedef typename Traits::size_t size_t;
-
- static const size_t BLOCK_SIZE = static_cast(Traits::BLOCK_SIZE);
- static const size_t EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD = static_cast(Traits::EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD);
- static const size_t EXPLICIT_INITIAL_INDEX_SIZE = static_cast(Traits::EXPLICIT_INITIAL_INDEX_SIZE);
- static const size_t IMPLICIT_INITIAL_INDEX_SIZE = static_cast(Traits::IMPLICIT_INITIAL_INDEX_SIZE);
- static const size_t INITIAL_IMPLICIT_PRODUCER_HASH_SIZE = static_cast(Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE);
- static const std::uint32_t EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE = static_cast(Traits::EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE);
-#ifdef _MSC_VER
-#pragma warning(push)
-#pragma warning(disable: 4307) // + integral constant overflow (that's what the ternary expression is for!)
-#pragma warning(disable: 4309) // static_cast: Truncation of constant value
-#endif
- static const size_t MAX_SUBQUEUE_SIZE = (details::const_numeric_max::value - static_cast(Traits::MAX_SUBQUEUE_SIZE) < BLOCK_SIZE) ? details::const_numeric_max::value : ((static_cast(Traits::MAX_SUBQUEUE_SIZE) + (BLOCK_SIZE - 1)) / BLOCK_SIZE * BLOCK_SIZE);
-#ifdef _MSC_VER
-#pragma warning(pop)
-#endif
-
- static_assert(!std::numeric_limits::is_signed && std::is_integral::value, "Traits::size_t must be an unsigned integral type");
- static_assert(!std::numeric_limits::is_signed && std::is_integral::value, "Traits::index_t must be an unsigned integral type");
- static_assert(sizeof(index_t) >= sizeof(size_t), "Traits::index_t must be at least as wide as Traits::size_t");
- static_assert((BLOCK_SIZE > 1) && !(BLOCK_SIZE & (BLOCK_SIZE - 1)), "Traits::BLOCK_SIZE must be a power of 2 (and at least 2)");
- static_assert((EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD > 1) && !(EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD & (EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD - 1)), "Traits::EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD must be a power of 2 (and greater than 1)");
- static_assert((EXPLICIT_INITIAL_INDEX_SIZE > 1) && !(EXPLICIT_INITIAL_INDEX_SIZE & (EXPLICIT_INITIAL_INDEX_SIZE - 1)), "Traits::EXPLICIT_INITIAL_INDEX_SIZE must be a power of 2 (and greater than 1)");
- static_assert((IMPLICIT_INITIAL_INDEX_SIZE > 1) && !(IMPLICIT_INITIAL_INDEX_SIZE & (IMPLICIT_INITIAL_INDEX_SIZE - 1)), "Traits::IMPLICIT_INITIAL_INDEX_SIZE must be a power of 2 (and greater than 1)");
- static_assert((INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) || !(INITIAL_IMPLICIT_PRODUCER_HASH_SIZE & (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE - 1)), "Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE must be a power of 2");
- static_assert(INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0 || INITIAL_IMPLICIT_PRODUCER_HASH_SIZE >= 1, "Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE must be at least 1 (or 0 to disable implicit enqueueing)");
-
-public:
- // Creates a queue with at least `capacity` element slots; note that the
- // actual number of elements that can be inserted without additional memory
- // allocation depends on the number of producers and the block size (e.g. if
- // the block size is equal to `capacity`, only a single block will be allocated
- // up-front, which means only a single producer will be able to enqueue elements
- // without an extra allocation -- blocks aren't shared between producers).
- // This method is not thread safe -- it is up to the user to ensure that the
- // queue is fully constructed before it starts being used by other threads (this
- // includes making the memory effects of construction visible, possibly with a
- // memory barrier).
- explicit ConcurrentQueue(size_t capacity = 6 * BLOCK_SIZE)
- : producerListTail(nullptr),
- producerCount(0),
- initialBlockPoolIndex(0),
- nextExplicitConsumerId(0),
- globalExplicitConsumerOffset(0)
- {
- implicitProducerHashResizeInProgress.clear(std::memory_order_relaxed);
- populate_initial_implicit_producer_hash();
- populate_initial_block_list(capacity / BLOCK_SIZE + ((capacity & (BLOCK_SIZE - 1)) == 0 ? 0 : 1));
-
-#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG
- // Track all the producers using a fully-resolved typed list for
- // each kind; this makes it possible to debug them starting from
- // the root queue object (otherwise wacky casts are needed that
- // don't compile in the debugger's expression evaluator).
- explicitProducers.store(nullptr, std::memory_order_relaxed);
- implicitProducers.store(nullptr, std::memory_order_relaxed);
-#endif
- }
-
- // Computes the correct amount of pre-allocated blocks for you based
- // on the minimum number of elements you want available at any given
- // time, and the maximum concurrent number of each type of producer.
- ConcurrentQueue(size_t minCapacity, size_t maxExplicitProducers, size_t maxImplicitProducers)
- : producerListTail(nullptr),
- producerCount(0),
- initialBlockPoolIndex(0),
- nextExplicitConsumerId(0),
- globalExplicitConsumerOffset(0)
- {
- implicitProducerHashResizeInProgress.clear(std::memory_order_relaxed);
- populate_initial_implicit_producer_hash();
- size_t blocks = (((minCapacity + BLOCK_SIZE - 1) / BLOCK_SIZE) - 1) * (maxExplicitProducers + 1) + 2 * (maxExplicitProducers + maxImplicitProducers);
- populate_initial_block_list(blocks);
-
-#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG
- explicitProducers.store(nullptr, std::memory_order_relaxed);
- implicitProducers.store(nullptr, std::memory_order_relaxed);
-#endif
- }
-
- // Note: The queue should not be accessed concurrently while it's
- // being deleted. It's up to the user to synchronize this.
- // This method is not thread safe.
- ~ConcurrentQueue()
- {
- // Destroy producers
- auto ptr = producerListTail.load(std::memory_order_relaxed);
- while (ptr != nullptr) {
- auto next = ptr->next_prod();
- if (ptr->token != nullptr) {
- ptr->token->producer = nullptr;
- }
- destroy(ptr);
- ptr = next;
- }
-
- // Destroy implicit producer hash tables
- if (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE != 0) {
- auto hash = implicitProducerHash.load(std::memory_order_relaxed);
- while (hash != nullptr) {
- auto prev = hash->prev;
- if (prev != nullptr) { // The last hash is part of this object and was not allocated dynamically
- for (size_t i = 0; i != hash->capacity; ++i) {
- hash->entries[i].~ImplicitProducerKVP();
- }
- hash->~ImplicitProducerHash();
- (Traits::free)(hash);
- }
- hash = prev;
- }
- }
-
- // Destroy global free list
- auto block = freeList.head_unsafe();
- while (block != nullptr) {
- auto next = block->freeListNext.load(std::memory_order_relaxed);
- if (block->dynamicallyAllocated) {
- destroy(block);
- }
- block = next;
- }
-
- // Destroy initial free list
- destroy_array(initialBlockPool, initialBlockPoolSize);
- }
-
- // Disable copying and copy assignment
- ConcurrentQueue(ConcurrentQueue const&) MOODYCAMEL_DELETE_FUNCTION;
- ConcurrentQueue& operator=(ConcurrentQueue const&) MOODYCAMEL_DELETE_FUNCTION;
-
- // Moving is supported, but note that it is *not* a thread-safe operation.
- // Nobody can use the queue while it's being moved, and the memory effects
- // of that move must be propagated to other threads before they can use it.
- // Note: When a queue is moved, its tokens are still valid but can only be
- // used with the destination queue (i.e. semantically they are moved along
- // with the queue itself).
- ConcurrentQueue(ConcurrentQueue&& other) MOODYCAMEL_NOEXCEPT
- : producerListTail(other.producerListTail.load(std::memory_order_relaxed)),
- producerCount(other.producerCount.load(std::memory_order_relaxed)),
- initialBlockPoolIndex(other.initialBlockPoolIndex.load(std::memory_order_relaxed)),
- initialBlockPool(other.initialBlockPool),
- initialBlockPoolSize(other.initialBlockPoolSize),
- freeList(std::move(other.freeList)),
- nextExplicitConsumerId(other.nextExplicitConsumerId.load(std::memory_order_relaxed)),
- globalExplicitConsumerOffset(other.globalExplicitConsumerOffset.load(std::memory_order_relaxed))
- {
- // Move the other one into this, and leave the other one as an empty queue
- implicitProducerHashResizeInProgress.clear(std::memory_order_relaxed);
- populate_initial_implicit_producer_hash();
- swap_implicit_producer_hashes(other);
-
- other.producerListTail.store(nullptr, std::memory_order_relaxed);
- other.producerCount.store(0, std::memory_order_relaxed);
- other.nextExplicitConsumerId.store(0, std::memory_order_relaxed);
- other.globalExplicitConsumerOffset.store(0, std::memory_order_relaxed);
-
-#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG
- explicitProducers.store(other.explicitProducers.load(std::memory_order_relaxed), std::memory_order_relaxed);
- other.explicitProducers.store(nullptr, std::memory_order_relaxed);
- implicitProducers.store(other.implicitProducers.load(std::memory_order_relaxed), std::memory_order_relaxed);
- other.implicitProducers.store(nullptr, std::memory_order_relaxed);
-#endif
-
- other.initialBlockPoolIndex.store(0, std::memory_order_relaxed);
- other.initialBlockPoolSize = 0;
- other.initialBlockPool = nullptr;
-
- reown_producers();
- }
-
- inline ConcurrentQueue& operator=(ConcurrentQueue&& other) MOODYCAMEL_NOEXCEPT
- {
- return swap_internal(other);
- }
-
- // Swaps this queue's state with the other's. Not thread-safe.
- // Swapping two queues does not invalidate their tokens, however
- // the tokens that were created for one queue must be used with
- // only the swapped queue (i.e. the tokens are tied to the
- // queue's movable state, not the object itself).
- inline void swap(ConcurrentQueue& other) MOODYCAMEL_NOEXCEPT
- {
- swap_internal(other);
- }
-
-private:
- ConcurrentQueue& swap_internal(ConcurrentQueue& other)
- {
- if (this == &other) {
- return *this;
- }
-
- details::swap_relaxed(producerListTail, other.producerListTail);
- details::swap_relaxed(producerCount, other.producerCount);
- details::swap_relaxed(initialBlockPoolIndex, other.initialBlockPoolIndex);
- std::swap(initialBlockPool, other.initialBlockPool);
- std::swap(initialBlockPoolSize, other.initialBlockPoolSize);
- freeList.swap(other.freeList);
- details::swap_relaxed(nextExplicitConsumerId, other.nextExplicitConsumerId);
- details::swap_relaxed(globalExplicitConsumerOffset, other.globalExplicitConsumerOffset);
-
- swap_implicit_producer_hashes(other);
-
- reown_producers();
- other.reown_producers();
-
-#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG
- details::swap_relaxed(explicitProducers, other.explicitProducers);
- details::swap_relaxed(implicitProducers, other.implicitProducers);
-#endif
-
- return *this;
- }
-
-public:
- // Enqueues a single item (by copying it).
- // Allocates memory if required. Only fails if memory allocation fails (or implicit
- // production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0,
- // or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed).
- // Thread-safe.
- inline bool enqueue(T const& item)
- {
- if (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false;
- return inner_enqueue(item);
- }
-
- // Enqueues a single item (by moving it, if possible).
- // Allocates memory if required. Only fails if memory allocation fails (or implicit
- // production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0,
- // or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed).
- // Thread-safe.
- inline bool enqueue(T&& item)
- {
- if (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false;
- return inner_enqueue(std::move(item));
- }
-
- // Enqueues a single item (by copying it) using an explicit producer token.
- // Allocates memory if required. Only fails if memory allocation fails (or
- // Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed).
- // Thread-safe.
- inline bool enqueue(producer_token_t const& token, T const& item)
- {
- return inner_enqueue(token, item);
- }
-
- // Enqueues a single item (by moving it, if possible) using an explicit producer token.
- // Allocates memory if required. Only fails if memory allocation fails (or
- // Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed).
- // Thread-safe.
- inline bool enqueue(producer_token_t const& token, T&& item)
- {
- return inner_enqueue(token, std::move(item));
- }
-
- // Enqueues several items.
- // Allocates memory if required. Only fails if memory allocation fails (or
- // implicit production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE
- // is 0, or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed).
- // Note: Use std::make_move_iterator if the elements should be moved instead of copied.
- // Thread-safe.
- template
- bool enqueue_bulk(It itemFirst, size_t count)
- {
- if (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false;
- return inner_enqueue_bulk(itemFirst, count);
- }
-
- // Enqueues several items using an explicit producer token.
- // Allocates memory if required. Only fails if memory allocation fails
- // (or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed).
- // Note: Use std::make_move_iterator if the elements should be moved
- // instead of copied.
- // Thread-safe.
- template
- bool enqueue_bulk(producer_token_t const& token, It itemFirst, size_t count)
- {
- return inner_enqueue_bulk(token, itemFirst, count);
- }
-
- // Enqueues a single item (by copying it).
- // Does not allocate memory. Fails if not enough room to enqueue (or implicit
- // production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE
- // is 0).
- // Thread-safe.
- inline bool try_enqueue(T const& item)
- {
- if (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false;
- return inner_enqueue(item);
- }
-
- // Enqueues a single item (by moving it, if possible).
- // Does not allocate memory (except for one-time implicit producer).
- // Fails if not enough room to enqueue (or implicit production is
- // disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0).
- // Thread-safe.
- inline bool try_enqueue(T&& item)
- {
- if (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false;
- return inner_enqueue(std::move(item));
- }
-
- // Enqueues a single item (by copying it) using an explicit producer token.
- // Does not allocate memory. Fails if not enough room to enqueue.
- // Thread-safe.
- inline bool try_enqueue(producer_token_t const& token, T const& item)
- {
- return inner_enqueue(token, item);
- }
-
- // Enqueues a single item (by moving it, if possible) using an explicit producer token.
- // Does not allocate memory. Fails if not enough room to enqueue.
- // Thread-safe.
- inline bool try_enqueue(producer_token_t const& token, T&& item)
- {
- return inner_enqueue(token, std::move(item));
- }
-
- // Enqueues several items.
- // Does not allocate memory (except for one-time implicit producer).
- // Fails if not enough room to enqueue (or implicit production is
- // disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0).
- // Note: Use std::make_move_iterator if the elements should be moved
- // instead of copied.
- // Thread-safe.
- template
- bool try_enqueue_bulk(It itemFirst, size_t count)
- {
- if (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false;
- return inner_enqueue_bulk(itemFirst, count);
- }
-
- // Enqueues several items using an explicit producer token.
- // Does not allocate memory. Fails if not enough room to enqueue.
- // Note: Use std::make_move_iterator if the elements should be moved
- // instead of copied.
- // Thread-safe.
- template
- bool try_enqueue_bulk(producer_token_t const& token, It itemFirst, size_t count)
- {
- return inner_enqueue_bulk(token, itemFirst, count);
- }
-
-
-
- // Attempts to dequeue from the queue.
- // Returns false if all producer streams appeared empty at the time they
- // were checked (so, the queue is likely but not guaranteed to be empty).
- // Never allocates. Thread-safe.
- template
- bool try_dequeue(U& item)
- {
- // Instead of simply trying each producer in turn (which could cause needless contention on the first
- // producer), we score them heuristically.
- size_t nonEmptyCount = 0;
- ProducerBase* best = nullptr;
- size_t bestSize = 0;
- for (auto ptr = producerListTail.load(std::memory_order_acquire); nonEmptyCount < 3 && ptr != nullptr; ptr = ptr->next_prod()) {
- auto size = ptr->size_approx();
- if (size > 0) {
- if (size > bestSize) {
- bestSize = size;
- best = ptr;
- }
- ++nonEmptyCount;
- }
- }
-
- // If there was at least one non-empty queue but it appears empty at the time
- // we try to dequeue from it, we need to make sure every queue's been tried
- if (nonEmptyCount > 0) {
- if (details::likely(best->dequeue(item))) {
- return true;
- }
- for (auto ptr = producerListTail.load(std::memory_order_acquire); ptr != nullptr; ptr = ptr->next_prod()) {
- if (ptr != best && ptr->dequeue(item)) {
- return true;
- }
- }
- }
- return false;
- }
-
- // Attempts to dequeue from the queue.
- // Returns false if all producer streams appeared empty at the time they
- // were checked (so, the queue is likely but not guaranteed to be empty).
- // This differs from the try_dequeue(item) method in that this one does
- // not attempt to reduce contention by interleaving the order that producer
- // streams are dequeued from. So, using this method can reduce overall throughput
- // under contention, but will give more predictable results in single-threaded
- // consumer scenarios. This is mostly only useful for internal unit tests.
- // Never allocates. Thread-safe.
- template
- bool try_dequeue_non_interleaved(U& item)
- {
- for (auto ptr = producerListTail.load(std::memory_order_acquire); ptr != nullptr; ptr = ptr->next_prod()) {
- if (ptr->dequeue(item)) {
- return true;
- }
- }
- return false;
- }
-
- // Attempts to dequeue from the queue using an explicit consumer token.
- // Returns false if all producer streams appeared empty at the time they
- // were checked (so, the queue is likely but not guaranteed to be empty).
- // Never allocates. Thread-safe.
- template
- bool try_dequeue(consumer_token_t& token, U& item)
- {
- // The idea is roughly as follows:
- // Every 256 items from one producer, make everyone rotate (increase the global offset) -> this means the highest efficiency consumer dictates the rotation speed of everyone else, more or less
- // If you see that the global offset has changed, you must reset your consumption counter and move to your designated place
- // If there's no items where you're supposed to be, keep moving until you find a producer with some items
- // If the global offset has not changed but you've run out of items to consume, move over from your current position until you find an producer with something in it
-
- if (token.desiredProducer == nullptr || token.lastKnownGlobalOffset != globalExplicitConsumerOffset.load(std::memory_order_relaxed)) {
- if (!update_current_producer_after_rotation(token)) {
- return false;
- }
- }
-
- // If there was at least one non-empty queue but it appears empty at the time
- // we try to dequeue from it, we need to make sure every queue's been tried
- if (static_cast(token.currentProducer)->dequeue(item)) {
- if (++token.itemsConsumedFromCurrent == EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE) {
- globalExplicitConsumerOffset.fetch_add(1, std::memory_order_relaxed);
- }
- return true;
- }
-
- auto tail = producerListTail.load(std::memory_order_acquire);
- auto ptr = static_cast(token.currentProducer)->next_prod();
- if (ptr == nullptr) {
- ptr = tail;
- }
- while (ptr != static_cast(token.currentProducer)) {
- if (ptr->dequeue(item)) {
- token.currentProducer = ptr;
- token.itemsConsumedFromCurrent = 1;
- return true;
- }
- ptr = ptr->next_prod();
- if (ptr == nullptr) {
- ptr = tail;
- }
- }
- return false;
- }
-
- // Attempts to dequeue several elements from the queue.
- // Returns the number of items actually dequeued.
- // Returns 0 if all producer streams appeared empty at the time they
- // were checked (so, the queue is likely but not guaranteed to be empty).
- // Never allocates. Thread-safe.
- template
- size_t try_dequeue_bulk(It itemFirst, size_t max)
- {
- size_t count = 0;
- for (auto ptr = producerListTail.load(std::memory_order_acquire); ptr != nullptr; ptr = ptr->next_prod()) {
- count += ptr->dequeue_bulk(itemFirst, max - count);
- if (count == max) {
- break;
- }
- }
- return count;
- }
-
- // Attempts to dequeue several elements from the queue using an explicit consumer token.
- // Returns the number of items actually dequeued.
- // Returns 0 if all producer streams appeared empty at the time they
- // were checked (so, the queue is likely but not guaranteed to be empty).
- // Never allocates. Thread-safe.
- template
- size_t try_dequeue_bulk(consumer_token_t& token, It itemFirst, size_t max)
- {
- if (token.desiredProducer == nullptr || token.lastKnownGlobalOffset != globalExplicitConsumerOffset.load(std::memory_order_relaxed)) {
- if (!update_current_producer_after_rotation(token)) {
- return 0;
- }
- }
-
- size_t count = static_cast(token.currentProducer)->dequeue_bulk(itemFirst, max);
- if (count == max) {
- if ((token.itemsConsumedFromCurrent += static_cast(max)) >= EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE) {
- globalExplicitConsumerOffset.fetch_add(1, std::memory_order_relaxed);
- }
- return max;
- }
- token.itemsConsumedFromCurrent += static_cast(count);
- max -= count;
-
- auto tail = producerListTail.load(std::memory_order_acquire);
- auto ptr = static_cast(token.currentProducer)->next_prod();
- if (ptr == nullptr) {
- ptr = tail;
- }
- while (ptr != static_cast(token.currentProducer)) {
- auto dequeued = ptr->dequeue_bulk(itemFirst, max);
- count += dequeued;
- if (dequeued != 0) {
- token.currentProducer = ptr;
- token.itemsConsumedFromCurrent = static_cast(dequeued);
- }
- if (dequeued == max) {
- break;
- }
- max -= dequeued;
- ptr = ptr->next_prod();
- if (ptr == nullptr) {
- ptr = tail;
- }
- }
- return count;
- }
-
-
-
- // Attempts to dequeue from a specific producer's inner queue.
- // If you happen to know which producer you want to dequeue from, this
- // is significantly faster than using the general-case try_dequeue methods.
- // Returns false if the producer's queue appeared empty at the time it
- // was checked (so, the queue is likely but not guaranteed to be empty).
- // Never allocates. Thread-safe.
- template
- inline bool try_dequeue_from_producer(producer_token_t const& producer, U& item)
- {
- return static_cast(producer.producer)->dequeue(item);
- }
-
- // Attempts to dequeue several elements from a specific producer's inner queue.
- // Returns the number of items actually dequeued.
- // If you happen to know which producer you want to dequeue from, this
- // is significantly faster than using the general-case try_dequeue methods.
- // Returns 0 if the producer's queue appeared empty at the time it
- // was checked (so, the queue is likely but not guaranteed to be empty).
- // Never allocates. Thread-safe.
- template
- inline size_t try_dequeue_bulk_from_producer(producer_token_t const& producer, It itemFirst, size_t max)
- {
- return static_cast(producer.producer)->dequeue_bulk(itemFirst, max);
- }
-
-
- // Returns an estimate of the total number of elements currently in the queue. This
- // estimate is only accurate if the queue has completely stabilized before it is called
- // (i.e. all enqueue and dequeue operations have completed and their memory effects are
- // visible on the calling thread, and no further operations start while this method is
- // being called).
- // Thread-safe.
- size_t size_approx() const
- {
- size_t size = 0;
- for (auto ptr = producerListTail.load(std::memory_order_acquire); ptr != nullptr; ptr = ptr->next_prod()) {
- size += ptr->size_approx();
- }
- return size;
- }
-
-
- // Returns true if the underlying atomic variables used by
- // the queue are lock-free (they should be on most platforms).
- // Thread-safe.
- static bool is_lock_free()
- {
- return
- details::static_is_lock_free::value == 2 &&
- details::static_is_lock_free::value == 2 &&
- details::static_is_lock_free::value == 2 &&
- details::static_is_lock_free::value == 2 &&
- details::static_is_lock_free::value == 2 &&
- details::static_is_lock_free::thread_id_numeric_size_t>::value == 2;
- }
-
-
-private:
- friend struct ProducerToken;
- friend struct ConsumerToken;
- friend struct ExplicitProducer;
- friend class ConcurrentQueueTests;
-
- enum AllocationMode { CanAlloc, CannotAlloc };
-
-
- ///////////////////////////////
- // Queue methods
- ///////////////////////////////
-
- template
- inline bool inner_enqueue(producer_token_t const& token, U&& element)
- {
- return static_cast(token.producer)->ConcurrentQueue::ExplicitProducer::template enqueue(std::forward(element));
- }
-
- template
- inline bool inner_enqueue(U&& element)
- {
- auto producer = get_or_add_implicit_producer();
- return producer == nullptr ? false : producer->ConcurrentQueue::ImplicitProducer::template enqueue(std::forward(element));
- }
-
- template
- inline bool inner_enqueue_bulk(producer_token_t const& token, It itemFirst, size_t count)
- {
- return static_cast(token.producer)->ConcurrentQueue::ExplicitProducer::template enqueue_bulk(itemFirst, count);
- }
-
- template
- inline bool inner_enqueue_bulk(It itemFirst, size_t count)
- {
- auto producer = get_or_add_implicit_producer();
- return producer == nullptr ? false : producer->ConcurrentQueue::ImplicitProducer::template enqueue_bulk(itemFirst, count);
- }
-
- inline bool update_current_producer_after_rotation(consumer_token_t& token)
- {
- // Ah, there's been a rotation, figure out where we should be!
- auto tail = producerListTail.load(std::memory_order_acquire);
- if (token.desiredProducer == nullptr && tail == nullptr) {
- return false;
- }
- auto prodCount = producerCount.load(std::memory_order_relaxed);
- auto globalOffset = globalExplicitConsumerOffset.load(std::memory_order_relaxed);
- if (details::unlikely(token.desiredProducer == nullptr)) {
- // Aha, first time we're dequeueing anything.
- // Figure out our local position
- // Note: offset is from start, not end, but we're traversing from end -- subtract from count first
- std::uint32_t offset = prodCount - 1 - (token.initialOffset % prodCount);
- token.desiredProducer = tail;
- for (std::uint32_t i = 0; i != offset; ++i) {
- token.desiredProducer = static_cast(token.desiredProducer)->next_prod();
- if (token.desiredProducer == nullptr) {
- token.desiredProducer = tail;
- }
- }
- }
-
- std::uint32_t delta = globalOffset - token.lastKnownGlobalOffset;
- if (delta >= prodCount) {
- delta = delta % prodCount;
- }
- for (std::uint32_t i = 0; i != delta; ++i) {
- token.desiredProducer = static_cast(token.desiredProducer)->next_prod();
- if (token.desiredProducer == nullptr) {
- token.desiredProducer = tail;
- }
- }
-
- token.lastKnownGlobalOffset = globalOffset;
- token.currentProducer = token.desiredProducer;
- token.itemsConsumedFromCurrent = 0;
- return true;
- }
-
-
- ///////////////////////////
- // Free list
- ///////////////////////////
-
- template
- struct FreeListNode
- {
- FreeListNode() : freeListRefs(0), freeListNext(nullptr) { }
-
- std::atomic freeListRefs;
- std::atomic freeListNext;
- };
-
- // A simple CAS-based lock-free free list. Not the fastest thing in the world under heavy contention, but
- // simple and correct (assuming nodes are never freed until after the free list is destroyed), and fairly
- // speedy under low contention.
- template