16 #if __cplusplus > 199711L || _MSC_VER >= 1700 // C++11 or VS2012 34 #ifndef MOODYCAMEL_CACHE_LINE_SIZE 35 #define MOODYCAMEL_CACHE_LINE_SIZE 64 38 #ifndef MOODYCAMEL_EXCEPTIONS_ENABLED 39 #if (defined(_MSC_VER) && defined(_CPPUNWIND)) || (defined(__GNUC__) && defined(__EXCEPTIONS)) || (!defined(_MSC_VER) && !defined(__GNUC__)) 40 #define MOODYCAMEL_EXCEPTIONS_ENABLED 44 #ifndef MOODYCAMEL_HAS_EMPLACE 45 #if !defined(_MSC_VER) || _MSC_VER >= 1800 // variadic templates: either a non-MS compiler or VS >= 2013 46 #define MOODYCAMEL_HAS_EMPLACE 1 52 #pragma warning(disable: 4324) // structure was padded due to __declspec(align()) 53 #pragma warning(disable: 4820) // padding was added 54 #pragma warning(disable: 4127) // conditional expression is constant 59 template<
typename T,
size_t MAX_BLOCK_SIZE = 512>
96 assert(MAX_BLOCK_SIZE ==
ceilToPow2(MAX_BLOCK_SIZE) &&
"MAX_BLOCK_SIZE must be a power of 2");
97 assert(MAX_BLOCK_SIZE >= 2 &&
"MAX_BLOCK_SIZE must be at least 2");
99 Block* firstBlock =
nullptr;
108 size_t initialBlockCount = (maxSize + MAX_BLOCK_SIZE * 2 - 3) / (MAX_BLOCK_SIZE - 1);
110 Block* lastBlock =
nullptr;
111 for (
size_t i = 0; i != initialBlockCount; ++i) {
113 if (block ==
nullptr) {
114 #ifdef MOODYCAMEL_EXCEPTIONS_ENABLED 115 throw std::bad_alloc();
120 if (firstBlock ==
nullptr) {
124 lastBlock->
next = block;
127 block->
next = firstBlock;
132 if (firstBlock ==
nullptr) {
133 #ifdef MOODYCAMEL_EXCEPTIONS_ENABLED 134 throw std::bad_alloc();
139 firstBlock->
next = firstBlock;
159 other.largestBlockSize = 32;
160 Block* b = other.make_block(other.largestBlockSize);
162 #ifdef MOODYCAMEL_EXCEPTIONS_ENABLED 163 throw std::bad_alloc();
169 other.frontBlock = b;
179 other.frontBlock = b;
196 Block* block = frontBlock_;
199 size_t blockFront = block->
front;
200 size_t blockTail = block->
tail;
202 for (
size_t i = blockFront; i != blockTail; i = (i + 1) & block->
sizeMask) {
203 auto element =
reinterpret_cast<T*
>(block->
data + i *
sizeof(T));
208 auto rawBlock = block->
rawThis;
212 }
while (block != frontBlock_);
221 return inner_enqueue<CannotAlloc>(element);
229 return inner_enqueue<CannotAlloc>(std::forward<T>(element));
232 #if MOODYCAMEL_HAS_EMPLACE 234 template<
typename... Args>
237 return inner_enqueue<CannotAlloc>(std::forward<Args>(
args)...);
246 return inner_enqueue<CanAlloc>(element);
254 return inner_enqueue<CanAlloc>(std::forward<T>(element));
257 #if MOODYCAMEL_HAS_EMPLACE 259 template<
typename... Args>
262 return inner_enqueue<CanAlloc>(std::forward<Args>(
args)...);
294 size_t blockTail = frontBlock_->
localTail;
295 size_t blockFront = frontBlock_->
front.
load();
297 if (blockFront != blockTail || blockFront != (frontBlock_->
localTail = frontBlock_->
tail.
load())) {
300 non_empty_front_block:
302 auto element =
reinterpret_cast<T*
>(frontBlock_->
data + blockFront *
sizeof(T));
303 result = std::move(*element);
306 blockFront = (blockFront + 1) & frontBlock_->
sizeMask;
309 frontBlock_->
front = blockFront;
311 else if (frontBlock_ !=
tailBlock.load()) {
319 if (blockFront != blockTail) {
321 goto non_empty_front_block;
330 size_t nextBlockFront = nextBlock->
front.
load();
336 assert(nextBlockFront != nextBlockTail);
345 auto element =
reinterpret_cast<T*
>(frontBlock_->
data + nextBlockFront *
sizeof(T));
347 result = std::move(*element);
350 nextBlockFront = (nextBlockFront + 1) & frontBlock_->
sizeMask;
353 frontBlock_->
front = nextBlockFront;
377 size_t blockTail = frontBlock_->
localTail;
378 size_t blockFront = frontBlock_->
front.
load();
380 if (blockFront != blockTail || blockFront != (frontBlock_->
localTail = frontBlock_->
tail.
load())) {
382 non_empty_front_block:
383 return reinterpret_cast<T*
>(frontBlock_->
data + blockFront *
sizeof(T));
385 else if (frontBlock_ !=
tailBlock.load()) {
392 if (blockFront != blockTail) {
393 goto non_empty_front_block;
398 size_t nextBlockFront = nextBlock->
front.
load();
401 assert(nextBlockFront != nextBlock->
tail.
load());
402 return reinterpret_cast<T*
>(nextBlock->
data + nextBlockFront *
sizeof(T));
419 size_t blockTail = frontBlock_->
localTail;
420 size_t blockFront = frontBlock_->
front.
load();
422 if (blockFront != blockTail || blockFront != (frontBlock_->
localTail = frontBlock_->
tail.
load())) {
425 non_empty_front_block:
426 auto element =
reinterpret_cast<T*
>(frontBlock_->
data + blockFront *
sizeof(T));
429 blockFront = (blockFront + 1) & frontBlock_->
sizeMask;
432 frontBlock_->
front = blockFront;
434 else if (frontBlock_ !=
tailBlock.load()) {
441 if (blockFront != blockTail) {
442 goto non_empty_front_block;
448 size_t nextBlockFront = nextBlock->
front.
load();
452 assert(nextBlockFront != nextBlockTail);
460 auto element =
reinterpret_cast<T*
>(frontBlock_->
data + nextBlockFront *
sizeof(T));
463 nextBlockFront = (nextBlockFront + 1) & frontBlock_->
sizeMask;
466 frontBlock_->
front = nextBlockFront;
482 Block* block = frontBlock_;
486 size_t blockTail = block->
tail.
load();
487 result += (blockTail - blockFront) & block->
sizeMask;
488 block = block->
next.load();
489 }
while (block != frontBlock_);
497 #if MOODYCAMEL_HAS_EMPLACE 501 template<AllocationMode canAlloc,
typename U>
518 size_t blockTail = tailBlock_->
tail.
load();
520 size_t nextBlockTail = (blockTail + 1) & tailBlock_->
sizeMask;
521 if (nextBlockTail != blockFront || nextBlockTail != (tailBlock_->
localFront = tailBlock_->
front.
load())) {
524 char* location = tailBlock_->
data + blockTail *
sizeof(T);
525 #if MOODYCAMEL_HAS_EMPLACE 526 new (location) T(std::forward<Args>(
args)...);
528 new (location) T(std::forward<U>(element));
532 tailBlock_->
tail = nextBlockTail;
545 Block* tailBlockNext = tailBlock_->
next.load();
547 nextBlockTail = tailBlockNext->
tail.
load();
552 assert(nextBlockFront == nextBlockTail);
555 char* location = tailBlockNext->
data + nextBlockTail *
sizeof(T);
556 #if MOODYCAMEL_HAS_EMPLACE 557 new (location) T(std::forward<Args>(
args)...);
559 new (location) T(std::forward<U>(element));
562 tailBlockNext->
tail = (nextBlockTail + 1) & tailBlockNext->
sizeMask;
571 if (newBlock ==
nullptr) {
577 #if MOODYCAMEL_HAS_EMPLACE 578 new (newBlock->data) T(std::forward<Args>(
args)...);
580 new (newBlock->data) T(std::forward<U>(element));
582 assert(newBlock->front == 0);
583 newBlock->tail = newBlock->localTail = 1;
585 newBlock->next = tailBlock_->
next.load();
586 tailBlock_->
next = newBlock;
597 else if (canAlloc == CannotAlloc) {
602 assert(
false &&
"Should be unreachable code");
626 for (
size_t i = 1; i <
sizeof(size_t); i <<= 1) {
636 const std::size_t alignment = std::alignment_of<U>::value;
637 return ptr + (alignment - (
reinterpret_cast<std::uintptr_t
>(ptr) % alignment)) % alignment;
646 assert(!
inSection &&
"Concurrent (or re-entrant) enqueue or dequeue operation detected (only one thread at a time may hold the producer or consumer role)");
680 : front(0), localTail(0), tail(0), localFront(0), next(nullptr), data(_data), sizeMask(_size - 1), rawThis(_rawThis)
696 auto size =
sizeof(
Block) + std::alignment_of<Block>::value - 1;
697 size +=
sizeof(T) * capacity + std::alignment_of<T>::value - 1;
698 auto newBlockRaw =
static_cast<char*
>(std::malloc(size));
699 if (newBlockRaw ==
nullptr) {
703 auto newBlockAligned = align_for<Block>(newBlockRaw);
704 auto newBlockData = align_for<T>(newBlockAligned +
sizeof(
Block));
705 return new (newBlockAligned)
Block(capacity, newBlockRaw, newBlockData);
723 template<
typename T,
size_t MAX_BLOCK_SIZE = 512>
731 : inner(maxSize), sema(new spsc_sema::LightweightSemaphore())
735 : inner(
std::move(other.inner)), sema(
std::move(other.sema))
740 std::swap(sema, other.sema);
741 std::swap(inner, other.inner);
751 if (inner.try_enqueue(element)) {
763 if (inner.try_enqueue(std::forward<T>(element))) {
776 if (inner.enqueue(element)) {
788 if (inner.enqueue(std::forward<T>(element))) {
802 if (sema->tryWait()) {
803 bool success = inner.try_dequeue(result);
818 bool success = inner.try_dequeue(result);
834 if (!sema->wait(timeout_usecs)) {
837 bool success = inner.try_dequeue(result);
845 #if __cplusplus > 199711L || _MSC_VER >= 1700 852 template<
typename U,
typename Rep,
typename Period>
853 inline bool wait_dequeue_timed(U& result, std::chrono::duration<Rep, Period>
const& timeout)
AE_NO_TSAN 855 return wait_dequeue_timed(result, std::chrono::duration_cast<std::chrono::microseconds>(timeout).count());
875 if (sema->tryWait()) {
876 bool result = inner.pop();
888 return sema->availableApprox();
899 std::unique_ptr<spsc_sema::LightweightSemaphore>
sema;
weak_atomic< Block * > next
char cachelineFiller[MOODYCAMEL_CACHE_LINE_SIZE-sizeof(weak_atomic< Block * >)]
AE_FORCEINLINE bool try_enqueue(T const &element) AE_NO_TSAN
AE_FORCEINLINE bool try_enqueue(T &&element) AE_NO_TSAN
::moodycamel::ReaderWriterQueue< T, MAX_BLOCK_SIZE > ReaderWriterQueue
weak_atomic< size_t > tail
weak_atomic< Block * > tailBlock
AE_NO_TSAN ReaderWriterQueue(size_t maxSize=15)
bool try_dequeue(U &result) AE_NO_TSAN
AE_FORCEINLINE bool pop() AE_NO_TSAN
#define MOODYCAMEL_CACHE_LINE_SIZE
AE_NO_TSAN ~ReaderWriterQueue()
BlockingReaderWriterQueue(BlockingReaderWriterQueue const &)
bool wait_dequeue_timed(U &result, std::int64_t timeout_usecs) AE_NO_TSAN
AE_NO_TSAN Block(size_t const &_size, char *_rawThis, char *_data)
static AE_FORCEINLINE size_t ceilToPow2(size_t x)
AE_FORCEINLINE bool try_enqueue(T const &element) AE_NO_TSAN
AE_FORCEINLINE bool enqueue(T const &element) AE_NO_TSAN
AE_FORCEINLINE bool emplace(Args &&...args) AE_NO_TSAN
weak_atomic< size_t > front
AE_FORCEINLINE void fence(memory_order order) AE_NO_TSAN
ReaderWriterQueue(ReaderWriterQueue const &)
AE_FORCEINLINE T load() const AE_NO_TSAN
BlockingReaderWriterQueue & operator=(BlockingReaderWriterQueue &&other) AE_NO_TSAN
AE_FORCEINLINE bool try_emplace(Args &&...args) AE_NO_TSAN
bool try_dequeue(U &result) AE_NO_TSAN
std::unique_ptr< spsc_sema::LightweightSemaphore > sema
static Block * make_block(size_t capacity) AE_NO_TSAN
AE_FORCEINLINE size_t size_approx() const AE_NO_TSAN
AE_FORCEINLINE bool try_enqueue(T &&element) AE_NO_TSAN
BlockingReaderWriterQueue(BlockingReaderWriterQueue &&other) AE_NO_TSAN
AE_FORCEINLINE bool enqueue(T const &element) AE_NO_TSAN
ReentrantGuard & operator=(ReentrantGuard const &)
AE_NO_TSAN ReaderWriterQueue(ReaderWriterQueue &&other)
AE_NO_TSAN ReentrantGuard(bool &_inSection)
size_t size_approx() const AE_NO_TSAN
bool inner_enqueue(Args &&...args) AE_NO_TSAN
BlockingReaderWriterQueue & operator=(BlockingReaderWriterQueue const &)
AE_NO_TSAN ~ReentrantGuard()
void wait_dequeue(U &result) AE_NO_TSAN
AE_FORCEINLINE bool enqueue(T &&element) AE_NO_TSAN
AE_FORCEINLINE void compiler_fence(memory_order order) AE_NO_TSAN
AE_FORCEINLINE bool enqueue(T &&element) AE_NO_TSAN
ReaderWriterQueue & operator=(ReaderWriterQueue const &)
ReaderWriterQueue & operator=(ReaderWriterQueue &&other) AE_NO_TSAN
BlockingReaderWriterQueue(size_t maxSize=15) AE_NO_TSAN
AE_FORCEINLINE T * peek() AE_NO_TSAN
weak_atomic< Block * > frontBlock
static AE_FORCEINLINE char * align_for(char *ptr) AE_NO_TSAN