29 #ifndef MOODYCAMEL_CACHE_LINE_SIZE
30 #define MOODYCAMEL_CACHE_LINE_SIZE 64
33 #ifndef MOODYCAMEL_EXCEPTIONS_ENABLED
34 #if (defined(_MSC_VER) && defined(_CPPUNWIND)) || (defined(__GNUC__) && defined(__EXCEPTIONS)) || (!defined(_MSC_VER) && !defined(__GNUC__))
35 #define MOODYCAMEL_EXCEPTIONS_ENABLED
41 #pragma warning(disable: 4324) // structure was padded due to __declspec(align())
42 #pragma warning(disable: 4820) // padding was added
43 #pragma warning(disable: 4127) // conditional expression is constant
46 namespace moodycamel {
48 template<
typename T,
size_t MAX_BLOCK_SIZE = 512>
83 assert(MAX_BLOCK_SIZE == ceilToPow2(MAX_BLOCK_SIZE) &&
"MAX_BLOCK_SIZE must be a power of 2");
84 assert(MAX_BLOCK_SIZE >= 2 &&
"MAX_BLOCK_SIZE must be at least 2");
86 Block* firstBlock =
nullptr;
88 largestBlockSize = ceilToPow2(maxSize + 1);
89 if (largestBlockSize > MAX_BLOCK_SIZE * 2) {
95 size_t initialBlockCount = (maxSize + MAX_BLOCK_SIZE * 2 - 3) / (MAX_BLOCK_SIZE - 1);
96 largestBlockSize = MAX_BLOCK_SIZE;
97 Block* lastBlock =
nullptr;
98 for (
size_t i = 0; i != initialBlockCount; ++i) {
99 auto block = make_block(largestBlockSize);
100 if (block ==
nullptr) {
101 #ifdef MOODYCAMEL_EXCEPTIONS_ENABLED
102 throw std::bad_alloc();
107 if (firstBlock ==
nullptr) {
111 lastBlock->next = block;
114 block->next = firstBlock;
118 firstBlock = make_block(largestBlockSize);
119 if (firstBlock ==
nullptr) {
120 #ifdef MOODYCAMEL_EXCEPTIONS_ENABLED
121 throw std::bad_alloc();
126 firstBlock->next = firstBlock;
128 frontBlock = firstBlock;
129 tailBlock = firstBlock;
132 fence(memory_order_sync);
140 fence(memory_order_sync);
143 Block* frontBlock_ = frontBlock;
144 Block* block = frontBlock_;
146 Block* nextBlock = block->next;
147 size_t blockFront = block->front;
148 size_t blockTail = block->tail;
150 for (
size_t i = blockFront; i != blockTail; i = (i + 1) & block->sizeMask) {
151 auto element =
reinterpret_cast<T*
>(block->data + i *
sizeof(T));
156 auto rawBlock = block->rawThis;
160 }
while (block != frontBlock_);
167 AE_FORCEINLINE
bool try_enqueue(T
const& element)
169 return inner_enqueue<CannotAlloc>(element);
175 AE_FORCEINLINE
bool try_enqueue(T&& element)
177 return inner_enqueue<CannotAlloc>(std::forward<T>(element));
184 AE_FORCEINLINE
bool enqueue(T
const& element)
186 return inner_enqueue<CanAlloc>(element);
192 AE_FORCEINLINE
bool enqueue(T&& element)
194 return inner_enqueue<CanAlloc>(std::forward<T>(element));
202 bool try_dequeue(U& result)
205 ReentrantGuard guard(this->dequeuing);
225 Block* frontBlock_ = frontBlock.load();
226 size_t blockTail = frontBlock_->localTail;
227 size_t blockFront = frontBlock_->front.load();
229 if (blockFront != blockTail || blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) {
230 fence(memory_order_acquire);
232 non_empty_front_block:
234 auto element =
reinterpret_cast<T*
>(frontBlock_->data + blockFront *
sizeof(T));
235 result = std::move(*element);
238 blockFront = (blockFront + 1) & frontBlock_->sizeMask;
240 fence(memory_order_release);
241 frontBlock_->front = blockFront;
243 else if (frontBlock_ != tailBlock.load()) {
244 fence(memory_order_acquire);
246 frontBlock_ = frontBlock.load();
247 blockTail = frontBlock_->localTail = frontBlock_->tail.load();
248 blockFront = frontBlock_->front.load();
249 fence(memory_order_acquire);
251 if (blockFront != blockTail) {
253 goto non_empty_front_block;
257 Block* nextBlock = frontBlock_->next;
262 size_t nextBlockFront = nextBlock->front.load();
263 size_t nextBlockTail = nextBlock->localTail = nextBlock->tail.load();
264 fence(memory_order_acquire);
268 assert(nextBlockFront != nextBlockTail);
269 AE_UNUSED(nextBlockTail);
272 fence(memory_order_release);
273 frontBlock = frontBlock_ = nextBlock;
275 compiler_fence(memory_order_release);
277 auto element =
reinterpret_cast<T*
>(frontBlock_->data + nextBlockFront *
sizeof(T));
279 result = std::move(*element);
282 nextBlockFront = (nextBlockFront + 1) & frontBlock_->sizeMask;
284 fence(memory_order_release);
285 frontBlock_->front = nextBlockFront;
304 ReentrantGuard guard(this->dequeuing);
308 Block* frontBlock_ = frontBlock.load();
309 size_t blockTail = frontBlock_->localTail;
310 size_t blockFront = frontBlock_->front.load();
312 if (blockFront != blockTail || blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) {
313 fence(memory_order_acquire);
314 non_empty_front_block:
315 return reinterpret_cast<T*
>(frontBlock_->data + blockFront *
sizeof(T));
317 else if (frontBlock_ != tailBlock.load()) {
318 fence(memory_order_acquire);
319 frontBlock_ = frontBlock.load();
320 blockTail = frontBlock_->localTail = frontBlock_->tail.load();
321 blockFront = frontBlock_->front.load();
322 fence(memory_order_acquire);
324 if (blockFront != blockTail) {
325 goto non_empty_front_block;
328 Block* nextBlock = frontBlock_->next;
330 size_t nextBlockFront = nextBlock->front.load();
331 fence(memory_order_acquire);
333 assert(nextBlockFront != nextBlock->tail.load());
334 return reinterpret_cast<T*
>(nextBlock->data + nextBlockFront *
sizeof(T));
346 ReentrantGuard guard(this->dequeuing);
350 Block* frontBlock_ = frontBlock.load();
351 size_t blockTail = frontBlock_->localTail;
352 size_t blockFront = frontBlock_->front.load();
354 if (blockFront != blockTail || blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) {
355 fence(memory_order_acquire);
357 non_empty_front_block:
358 auto element =
reinterpret_cast<T*
>(frontBlock_->data + blockFront *
sizeof(T));
361 blockFront = (blockFront + 1) & frontBlock_->sizeMask;
363 fence(memory_order_release);
364 frontBlock_->front = blockFront;
366 else if (frontBlock_ != tailBlock.load()) {
367 fence(memory_order_acquire);
368 frontBlock_ = frontBlock.load();
369 blockTail = frontBlock_->localTail = frontBlock_->tail.load();
370 blockFront = frontBlock_->front.load();
371 fence(memory_order_acquire);
373 if (blockFront != blockTail) {
374 goto non_empty_front_block;
378 Block* nextBlock = frontBlock_->next;
380 size_t nextBlockFront = nextBlock->front.load();
381 size_t nextBlockTail = nextBlock->localTail = nextBlock->tail.load();
382 fence(memory_order_acquire);
384 assert(nextBlockFront != nextBlockTail);
385 AE_UNUSED(nextBlockTail);
387 fence(memory_order_release);
388 frontBlock = frontBlock_ = nextBlock;
390 compiler_fence(memory_order_release);
392 auto element =
reinterpret_cast<T*
>(frontBlock_->data + nextBlockFront *
sizeof(T));
395 nextBlockFront = (nextBlockFront + 1) & frontBlock_->sizeMask;
397 fence(memory_order_release);
398 frontBlock_->front = nextBlockFront;
410 inline size_t size_approx()
const
413 Block* frontBlock_ = frontBlock.load();
414 Block* block = frontBlock_;
416 fence(memory_order_acquire);
417 size_t blockFront = block->front.load();
418 size_t blockTail = block->tail.load();
419 result += (blockTail - blockFront) & block->sizeMask;
420 block = block->next.load();
421 }
while (block != frontBlock_);
427 enum AllocationMode { CanAlloc, CannotAlloc };
429 template<AllocationMode canAlloc,
typename U>
430 bool inner_enqueue(U&& element)
433 ReentrantGuard guard(this->enqueuing);
443 Block* tailBlock_ = tailBlock.load();
444 size_t blockFront = tailBlock_->localFront;
445 size_t blockTail = tailBlock_->tail.load();
447 size_t nextBlockTail = (blockTail + 1) & tailBlock_->sizeMask;
448 if (nextBlockTail != blockFront || nextBlockTail != (tailBlock_->localFront = tailBlock_->front.load())) {
449 fence(memory_order_acquire);
451 char* location = tailBlock_->data + blockTail *
sizeof(T);
452 new (location) T(std::forward<U>(element));
454 fence(memory_order_release);
455 tailBlock_->tail = nextBlockTail;
458 fence(memory_order_acquire);
459 if (tailBlock_->next.load() != frontBlock) {
465 fence(memory_order_acquire);
468 Block* tailBlockNext = tailBlock_->next.load();
469 size_t nextBlockFront = tailBlockNext->localFront = tailBlockNext->front.load();
470 nextBlockTail = tailBlockNext->tail.load();
471 fence(memory_order_acquire);
475 assert(nextBlockFront == nextBlockTail);
476 tailBlockNext->localFront = nextBlockFront;
478 char* location = tailBlockNext->data + nextBlockTail *
sizeof(T);
479 new (location) T(std::forward<U>(element));
481 tailBlockNext->tail = (nextBlockTail + 1) & tailBlockNext->sizeMask;
483 fence(memory_order_release);
484 tailBlock = tailBlockNext;
486 else if (canAlloc == CanAlloc) {
488 auto newBlockSize = largestBlockSize >= MAX_BLOCK_SIZE ? largestBlockSize : largestBlockSize * 2;
489 auto newBlock = make_block(newBlockSize);
490 if (newBlock ==
nullptr) {
494 largestBlockSize = newBlockSize;
496 new (newBlock->data) T(std::forward<U>(element));
498 assert(newBlock->front == 0);
499 newBlock->tail = newBlock->localTail = 1;
501 newBlock->next = tailBlock_->next.load();
502 tailBlock_->next = newBlock;
510 fence(memory_order_release);
511 tailBlock = newBlock;
513 else if (canAlloc == CannotAlloc) {
518 assert(
false &&
"Should be unreachable code");
535 AE_FORCEINLINE
static size_t ceilToPow2(
size_t x)
542 for (
size_t i = 1; i <
sizeof(size_t); i <<= 1) {
550 static AE_FORCEINLINE
char* align_for(
char* ptr)
552 const std::size_t alignment = std::alignment_of<U>::value;
553 return ptr + (alignment - (
reinterpret_cast<std::uintptr_t
>(ptr) % alignment)) % alignment;
557 struct ReentrantGuard
559 ReentrantGuard(
bool& _inSection)
560 : inSection(_inSection)
562 assert(!inSection &&
"ReaderWriterQueue does not support enqueuing or dequeuing elements from other elements' ctors and dtors");
566 ~ReentrantGuard() { inSection =
false; }
569 ReentrantGuard& operator=(ReentrantGuard
const&);
582 char cachelineFiller0[MOODYCAMEL_CACHE_LINE_SIZE -
sizeof(
weak_atomic<size_t>) -
sizeof(
size_t)];
586 char cachelineFiller1[MOODYCAMEL_CACHE_LINE_SIZE -
sizeof(
weak_atomic<size_t>) -
sizeof(
size_t)];
591 const size_t sizeMask;
595 Block(
size_t const& _size,
char* _rawThis,
char* _data)
596 : front(0), localTail(0), tail(0), localFront(0), next(
nullptr), data(_data), sizeMask(_size - 1), rawThis(_rawThis)
602 Block& operator=(Block
const&);
609 static Block* make_block(
size_t capacity)
612 auto size =
sizeof(Block) + std::alignment_of<Block>::value - 1;
613 size +=
sizeof(T) * capacity + std::alignment_of<T>::value - 1;
614 auto newBlockRaw =
static_cast<char*
>(std::malloc(size));
615 if (newBlockRaw ==
nullptr) {
619 auto newBlockAligned = align_for<Block>(newBlockRaw);
620 auto newBlockData = align_for<T>(newBlockAligned +
sizeof(Block));
621 return new (newBlockAligned) Block(capacity, newBlockRaw, newBlockData);
630 size_t largestBlockSize;
639 template<
typename T,
size_t MAX_BLOCK_SIZE = 512>
654 AE_FORCEINLINE
bool try_enqueue(T
const& element)
656 if (inner.try_enqueue(element)) {
666 AE_FORCEINLINE
bool try_enqueue(T&& element)
668 if (inner.try_enqueue(std::forward<T>(element))) {
679 AE_FORCEINLINE
bool enqueue(T
const& element)
681 if (inner.enqueue(element)) {
691 AE_FORCEINLINE
bool enqueue(T&& element)
693 if (inner.enqueue(std::forward<T>(element))) {
705 bool try_dequeue(U& result)
707 if (sema.tryWait()) {
708 bool success = inner.try_dequeue(result);
720 void wait_dequeue(U& result)
723 bool success = inner.try_dequeue(result);
735 AE_FORCEINLINE T* peek()
743 AE_FORCEINLINE
bool pop()
745 if (sema.tryWait()) {
746 bool result = inner.pop();
756 AE_FORCEINLINE
size_t size_approx()
const
758 return sema.availableApprox();
Definition: readerwriterqueue.h:49
Definition: atomicops.h:498
Definition: readerwriterqueue.h:640