From 0a5aee43dbed3517573d8dd4aab103ed9866afad Mon Sep 17 00:00:00 2001 From: Alexander Date: Sat, 15 Aug 2026 15:42:56 +0300 Subject: [PATCH 1/2] fix: move release fence in inner_enqueue() Move fence(memory_order_release) before both writes that publish a newly allocated block (tailBlock_->next and tailBlock), not just the second one. size_approx() reaches blocks via the next-chain and never reads tailBlock, so it wasn't covered by the existing fence placement. Fixes #171 --- readerwriterqueue.h | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/readerwriterqueue.h b/readerwriterqueue.h index 78c8e43..e11f098 100644 --- a/readerwriterqueue.h +++ b/readerwriterqueue.h @@ -1,4 +1,4 @@ -// ©2013-2020 Cameron Desrochers. +// ©2013-2020 Cameron Desrochers. // Distributed under the simplified BSD license (see the license file that // should have come with this header). @@ -619,6 +619,13 @@ class MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE ReaderWriterQueue newBlock->tail = newBlock->localTail = 1; newBlock->next = tailBlock_->next.load(); + + // Publish all writes to *newBlock before it becomes reachable via either + // tailBlock_->next (walked by size_approx() and other chain traversals) + // or tailBlock itself (used by try_dequeue). Without this fence, on + // weakly-ordered architectures (e.g. AArch64) a reader could observe the + // updated `next` pointer before seeing newBlock's initialized fields. + fence(memory_order_release); tailBlock_->next = newBlock; // Might be possible for the dequeue thread to see the new tailBlock->next @@ -627,7 +634,6 @@ class MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE ReaderWriterQueue // case where it could try to read the next is if it's already at the tailBlock, // and it won't advance past tailBlock in any circumstance). - fence(memory_order_release); tailBlock = newBlock; } else if (canAlloc == CannotAlloc) { From ba8c6623d7609e1225e7dc35eeaec713b6285ae8 Mon Sep 17 00:00:00 2001 From: Alexander Date: Tue, 25 Aug 2026 00:32:18 +0300 Subject: [PATCH 2/2] Fence and Comments Added second fence and changed comments --- readerwriterqueue.h | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/readerwriterqueue.h b/readerwriterqueue.h index e11f098..e4e45f1 100644 --- a/readerwriterqueue.h +++ b/readerwriterqueue.h @@ -620,20 +620,14 @@ class MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE ReaderWriterQueue newBlock->next = tailBlock_->next.load(); - // Publish all writes to *newBlock before it becomes reachable via either - // tailBlock_->next (walked by size_approx() and other chain traversals) - // or tailBlock itself (used by try_dequeue). Without this fence, on - // weakly-ordered architectures (e.g. AArch64) a reader could observe the - // updated `next` pointer before seeing newBlock's initialized fields. + // Publish all writes to *newBlock before it becomes reachable via + // tailBlock_->next. fence(memory_order_release); tailBlock_->next = newBlock; - // Might be possible for the dequeue thread to see the new tailBlock->next - // *without* seeing the new tailBlock value, but this is OK since it can't - // advance to the next block until tailBlock is set anyway (because the only - // case where it could try to read the next is if it's already at the tailBlock, - // and it won't advance past tailBlock in any circumstance). - + // Ensure that readers observing the new tailBlock also observe the + // preceding publication of tailBlock_->next. + fence(memory_order_release); tailBlock = newBlock; } else if (canAlloc == CannotAlloc) {