33 #include "../common/TracyAlloc.hpp"
34 #include "../common/TracyForceInline.hpp"
35 #include "../common/TracySystem.hpp"
41 #pragma GCC diagnostic push
42 #pragma GCC diagnostic ignored "-Wconversion"
45 #if defined(__APPLE__)
46 #include "TargetConditionals.h"
54 #include <type_traits>
66 namespace moodycamel {
namespace details {
68 inline bool cqLikely(
bool x) {
return __builtin_expect((x),
true); }
69 inline bool cqUnlikely(
bool x) {
return __builtin_expect((x),
false); }
80 struct compile_time_condition
82 static const bool value =
false;
85 struct compile_time_condition<true>
87 static const bool value =
true;
91 namespace moodycamel {
96 static const T
value = std::numeric_limits<T>::is_signed
97 ? (
static_cast<T
>(1) << (
sizeof(T) * CHAR_BIT - 1)) -
static_cast<T
>(1)
101 #if defined(__GLIBCXX__)
171 #if defined(malloc) || defined(free)
174 static inline void* WORKAROUND_malloc(
size_t size) {
return malloc(size); }
175 static inline void WORKAROUND_free(
void* ptr) {
return free(ptr); }
176 static inline void* (
malloc)(
size_t size) {
return WORKAROUND_malloc(size); }
177 static inline void (
free)(
void* ptr) {
return WORKAROUND_free(ptr); }
192 struct ProducerToken;
193 struct ConsumerToken;
195 template<
typename T,
typename Traits>
class ConcurrentQueue;
217 #pragma warning(push)
218 #pragma warning(disable: 4554)
221 return static_cast<T
>(a -
b) > static_cast<T>(static_cast<T>(1) <<
static_cast<T
>(
sizeof(T) * CHAR_BIT - 1));
231 return ptr + (alignment - (
reinterpret_cast<std::uintptr_t>(ptr) % alignment)) % alignment;
244 for (std::size_t i = 1; i <
sizeof(T); i <<= 1) {
254 T temp = std::move(left.load(std::memory_order_relaxed));
255 left.store(std::move(right.load(std::memory_order_relaxed)), std::memory_order_relaxed);
256 right.store(std::move(temp), std::memory_order_relaxed);
265 template<
bool Enable>
269 static inline T
const&
eval(T
const&
x)
280 -> decltype(std::forward<U>(
x))
282 return std::forward<U>(
x);
286 template<
typename It>
292 #if defined(__clang__) || !defined(__GNUC__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)
312 template<
typename T,
typename Traits>
318 other.producer =
nullptr;
319 if (producer !=
nullptr) {
320 producer->
token =
this;
336 if (other.producer !=
nullptr) {
373 template<
typename T,
typename Traits>
412 template<
typename T,
typename Traits = ConcurrentQueueDefaultTraits>
429 #pragma warning(push)
430 #pragma warning(disable: 4307) // + integral constant overflow (that's what the ternary expression is for!)
431 #pragma warning(disable: 4309) // static_cast: Truncation of constant value
440 static_assert(
sizeof(
index_t) >=
sizeof(
size_t),
"Traits::index_t must be at least as wide as Traits::size_t");
441 static_assert((BLOCK_SIZE > 1) && !(BLOCK_SIZE & (BLOCK_SIZE - 1)),
"Traits::BLOCK_SIZE must be a power of 2 (and at least 2)");
476 size_t blocks = (((minCapacity + BLOCK_SIZE - 1) / BLOCK_SIZE) - 1) * (maxExplicitProducers + 1) + 2 * (maxExplicitProducers);
487 while (ptr !=
nullptr) {
488 auto next = ptr->next_prod();
489 if (ptr->token !=
nullptr) {
490 ptr->token->producer =
nullptr;
498 while (block !=
nullptr) {
499 auto next = block->
freeListNext.load(std::memory_order_relaxed);
500 if (block->dynamicallyAllocated) {
522 template<
class NotifyThread,
class ProcessData>
536 if (ptr ==
nullptr) {
542 auto dequeued = ptr->
dequeue_bulk(notifyThread, processData);
548 ptr = ptr->next_prod();
549 if (ptr ==
nullptr) {
573 for (
auto ptr =
producerListTail.load(std::memory_order_acquire); ptr !=
nullptr; ptr = ptr->next_prod()) {
574 size += ptr->size_approx();
611 auto prodCount =
producerCount.load(std::memory_order_relaxed);
628 if (delta >= prodCount) {
629 delta = delta % prodCount;
649 template <
typename N>
684 auto head =
freeListHead.load(std::memory_order_acquire);
685 while (head !=
nullptr) {
686 auto prevHead = head;
687 auto refs = head->freeListRefs.load(std::memory_order_relaxed);
688 if ((refs &
REFS_MASK) == 0 || !head->freeListRefs.compare_exchange_strong(refs, refs + 1, std::memory_order_acquire, std::memory_order_relaxed)) {
695 auto next = head->freeListNext.load(std::memory_order_relaxed);
696 if (
freeListHead.compare_exchange_strong(head, next, std::memory_order_acquire, std::memory_order_relaxed)) {
702 head->freeListRefs.fetch_sub(2, std::memory_order_release);
709 refs = prevHead->freeListRefs.fetch_sub(1, std::memory_order_acq_rel);
732 auto head =
freeListHead.load(std::memory_order_relaxed);
734 node->freeListNext.store(head, std::memory_order_relaxed);
735 node->freeListRefs.store(1, std::memory_order_release);
736 if (!
freeListHead.compare_exchange_strong(head, node, std::memory_order_release, std::memory_order_relaxed)) {
771 if (!
emptyFlags[i].load(std::memory_order_relaxed)) {
777 std::atomic_thread_fence(std::memory_order_acquire);
783 std::atomic_thread_fence(std::memory_order_acquire);
796 assert(!
emptyFlags[BLOCK_SIZE - 1 - static_cast<size_t>(i & static_cast<index_t>(BLOCK_SIZE - 1))].load(std::memory_order_relaxed));
797 emptyFlags[BLOCK_SIZE - 1 -
static_cast<size_t>(i &
static_cast<index_t>(BLOCK_SIZE - 1))].store(
true, std::memory_order_release);
803 assert(prevVal < BLOCK_SIZE);
804 return prevVal == BLOCK_SIZE - 1;
814 std::atomic_thread_fence(std::memory_order_release);
815 i = BLOCK_SIZE - 1 -
static_cast<size_t>(i &
static_cast<index_t>(BLOCK_SIZE - 1)) - count + 1;
816 for (
size_t j = 0; j !=
count; ++j) {
817 assert(!
emptyFlags[i + j].load(std::memory_order_relaxed));
818 emptyFlags[i + j].store(
true, std::memory_order_relaxed);
825 assert(prevVal + count <= BLOCK_SIZE);
835 emptyFlags[i].store(
true, std::memory_order_relaxed);
849 emptyFlags[i].store(
false, std::memory_order_relaxed);
859 inline T
const*
operator[](
index_t idx)
const noexcept {
return static_cast<T const*
>(
static_cast<void const*
>(
elements)) +
static_cast<size_t>(idx &
static_cast<index_t>(BLOCK_SIZE - 1)); }
907 template<
class NotifyThread,
class ProcessData>
908 inline size_t dequeue_bulk(NotifyThread notifyThread, ProcessData processData)
917 auto tail =
tailIndex.load(std::memory_order_relaxed);
918 auto head =
headIndex.load(std::memory_order_relaxed);
967 Block* halfDequeuedBlock =
nullptr;
968 if ((this->
headIndex.load(std::memory_order_relaxed) &
static_cast<index_t>(BLOCK_SIZE - 1)) != 0) {
983 if (block->ConcurrentQueue::Block::is_empty()) {
988 if (block == halfDequeuedBlock) {
989 i =
static_cast<size_t>(this->
headIndex.load(std::memory_order_relaxed) &
static_cast<index_t>(BLOCK_SIZE - 1));
993 auto lastValidIndex = (this->
tailIndex.load(std::memory_order_relaxed) &
static_cast<index_t>(BLOCK_SIZE - 1)) == 0 ? BLOCK_SIZE :
static_cast<size_t>(this->
tailIndex.load(std::memory_order_relaxed) &
static_cast<index_t>(BLOCK_SIZE - 1));
994 while (i != BLOCK_SIZE && (block != this->
tailBlock || i != lastValidIndex)) {
1004 auto nextBlock = block->
next;
1005 if (block->dynamicallyAllocated) {
1017 while (header !=
nullptr) {
1019 header->~BlockIndexHeader();
1020 (Traits::free)(header);
1031 this->
tailBlock->ConcurrentQueue::Block::reset_empty();
1049 auto newBlock = this->
parent->ConcurrentQueue::requisition_block();
1050 newBlock->ConcurrentQueue::Block::reset_empty();
1052 newBlock->next = newBlock;
1064 entry.base = currentTailIndex;
1072 currentTailIndex = this->
tailIndex.load(std::memory_order_relaxed);
1073 if (
details::cqUnlikely((currentTailIndex & static_cast<index_t>(BLOCK_SIZE - 1)) == 0)) {
1076 return (*this->
tailBlock)[currentTailIndex];
1084 template<
class NotifyThread,
class ProcessData>
1087 auto tail = this->
tailIndex.load(std::memory_order_relaxed);
1089 auto desiredCount =
static_cast<size_t>(tail - (this->
dequeueOptimisticCount.load(std::memory_order_relaxed) - overcommit));
1090 if (details::circular_less_than<size_t>(0, desiredCount)) {
1091 desiredCount = desiredCount < 8192 ? desiredCount : 8192;
1092 std::atomic_thread_fence(std::memory_order_acquire);
1095 assert(overcommit <= myDequeueCount);
1097 tail = this->
tailIndex.load(std::memory_order_acquire);
1098 auto actualCount =
static_cast<size_t>(tail - (myDequeueCount - overcommit));
1099 if (details::circular_less_than<size_t>(0, actualCount)) {
1100 actualCount = desiredCount < actualCount ? desiredCount : actualCount;
1101 if (actualCount < desiredCount) {
1102 this->
dequeueOvercommit.fetch_add(desiredCount - actualCount, std::memory_order_release);
1107 auto firstIndex = this->
headIndex.fetch_add(actualCount, std::memory_order_acq_rel);
1110 auto localBlockIndex =
blockIndex.load(std::memory_order_acquire);
1111 auto localBlockIndexHead = localBlockIndex->front.load(std::memory_order_acquire);
1113 auto headBase = localBlockIndex->entries[localBlockIndexHead].base;
1114 auto firstBlockBaseIndex = firstIndex & ~static_cast<
index_t>(BLOCK_SIZE - 1);
1116 auto indexIndex = (localBlockIndexHead +
offset) & (localBlockIndex->size - 1);
1121 auto index = firstIndex;
1123 auto firstIndexInBlock =
index;
1125 endIndex = details::circular_less_than<index_t>(firstIndex +
static_cast<index_t>(actualCount), endIndex) ? firstIndex +
static_cast<index_t>(actualCount) : endIndex;
1126 auto block = localBlockIndex->entries[indexIndex].block;
1128 const auto sz = endIndex -
index;
1129 processData( (*block)[index], sz );
1132 block->ConcurrentQueue::Block::set_many_empty(firstIndexInBlock, static_cast<size_t>(endIndex - firstIndexInBlock));
1133 indexIndex = (indexIndex + 1) & (localBlockIndex->size - 1);
1134 }
while (
index != firstIndex + actualCount);
1170 if (newRawPtr ==
nullptr) {
1171 pr_blockIndexSize >>= 1;
1183 i = (i + 1) & prevBlockSizeMask;
1190 header->front.store(numberOfFilledSlotsToExpose - 1, std::memory_order_relaxed);
1191 header->entries = newBlockIndexEntries;
1197 blockIndex.store(header, std::memory_order_release);
1259 while (block !=
nullptr) {
1260 auto next = block->
next;
1275 if (block !=
nullptr) {
1280 if (block !=
nullptr) {
1284 return create<Block>();
1301 for (
auto ptr =
producerListTail.load(std::memory_order_acquire); ptr !=
nullptr; ptr = ptr->next_prod()) {
1302 if (ptr->inactive.load(std::memory_order_relaxed)) {
1303 if( ptr->size_approx() == 0 )
1305 bool expected =
true;
1306 if (ptr->inactive.compare_exchange_strong(expected,
false, std::memory_order_acquire, std::memory_order_relaxed)) {
1316 return add_producer(static_cast<ProducerBase*>(create<ExplicitProducer>(
this)));
1322 if (producer ==
nullptr) {
1331 producer->
next = prevTail;
1332 }
while (!
producerListTail.compare_exchange_weak(prevTail, producer, std::memory_order_release, std::memory_order_relaxed));
1342 for (
auto ptr =
producerListTail.load(std::memory_order_relaxed); ptr !=
nullptr; ptr = ptr->next_prod()) {
1351 template<
typename U>
1355 return static_cast<U*
>((Traits::malloc)(
sizeof(U) *
count));
1358 template<
typename U>
1368 template<
typename U>
1371 auto p = (Traits::malloc)(
sizeof(U));
1375 template<
typename U,
typename A1>
1378 auto p = (Traits::malloc)(
sizeof(U));
1379 return new (
p) U(std::forward<A1>(a1));
1382 template<
typename U>
1406 template<
typename T,
typename Traits>
1408 : producer(queue.recycle_or_create_producer())
1416 template<
typename T,
typename Traits>
1418 : itemsConsumedFromCurrent(0), currentProducer(nullptr), desiredProducer(nullptr)
1424 template<
typename T,
typename Traits>
1444 #if defined(__GNUC__)
1445 #pragma GCC diagnostic pop