From 1cde2dc22bb41116853a69a88cbb73671ffba5b2 Mon Sep 17 00:00:00 2001 From: Stephen Booth Date: Fri, 10 Jul 2026 08:32:25 -0500 Subject: [PATCH 1/5] Use relaxed loads --- .../CXXMessageQueue/include/mpsc/MessageQueue.hpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/Sources/CXXMessageQueue/include/mpsc/MessageQueue.hpp b/Sources/CXXMessageQueue/include/mpsc/MessageQueue.hpp index 00fba77..20fd2b6 100644 --- a/Sources/CXXMessageQueue/include/mpsc/MessageQueue.hpp +++ b/Sources/CXXMessageQueue/include/mpsc/MessageQueue.hpp @@ -90,24 +90,22 @@ class MessageQueue final { // MARK: Buffer Usage /// Returns the number of empty slots in the message queue. - /// @note The result of this method is only valid when called from a producer. /// @note The returned value is a transient snapshot and may become stale immediately after return. /// @return The number of empty slots available for messages. [[nodiscard]] SizeType emptySlots() const noexcept [[clang::nonblocking]]; /// Returns true if the message queue is full. - /// @note The result of this method is only valid when called from a producer. /// @note The returned value is a transient snapshot and may become stale immediately after return. /// @return true if the all slots in the queue are occupied. [[nodiscard]] bool isFull() const noexcept [[clang::nonblocking]]; /// Returns the number of occupied slots in the message queue. - /// @note The result of this method is only accurate when called from the consumer. + /// @note The returned value is a transient snapshot and may become stale immediately after return. /// @return The number of occupied slots containing messages. [[nodiscard]] SizeType occupiedSlots() const noexcept [[clang::nonblocking]]; /// Returns true if the message queue is empty. - /// @note The result of this method is only accurate when called from the consumer. + /// @note The returned value is a transient snapshot and may become stale immediately after return. /// @return true if all slots in the queue are empty. [[nodiscard]] bool isEmpty() const noexcept [[clang::nonblocking]]; @@ -267,7 +265,7 @@ template requires ValidPowerOfTwo && ValidPowerOfTwo inline auto MessageQueue::emptySlots() const noexcept -> SizeType { const auto writePos = writePosition_.load(std::memory_order_relaxed); - const auto readPos = readPosition_.load(std::memory_order_acquire); + const auto readPos = readPosition_.load(std::memory_order_relaxed); return N - (writePos - readPos); } @@ -275,14 +273,14 @@ template requires ValidPowerOfTwo && ValidPowerOfTwo inline bool MessageQueue::isFull() const noexcept { const auto writePos = writePosition_.load(std::memory_order_relaxed); - const auto readPos = readPosition_.load(std::memory_order_acquire); + const auto readPos = readPosition_.load(std::memory_order_relaxed); return (writePos - readPos) == N; } template requires ValidPowerOfTwo && ValidPowerOfTwo inline auto MessageQueue::occupiedSlots() const noexcept -> SizeType { - const auto writePos = writePosition_.load(std::memory_order_acquire); + const auto writePos = writePosition_.load(std::memory_order_relaxed); const auto readPos = readPosition_.load(std::memory_order_relaxed); return writePos - readPos; } @@ -290,7 +288,7 @@ inline auto MessageQueue::occupiedSlots() const noexcept -> SizeType { template requires ValidPowerOfTwo && ValidPowerOfTwo inline bool MessageQueue::isEmpty() const noexcept { - const auto writePos = writePosition_.load(std::memory_order_acquire); + const auto writePos = writePosition_.load(std::memory_order_relaxed); const auto readPos = readPosition_.load(std::memory_order_relaxed); return writePos == readPos; } From 748025969dbb7dcd481f37d139008da6a6ccaa43 Mon Sep 17 00:00:00 2001 From: Stephen Booth Date: Fri, 10 Jul 2026 09:13:11 -0500 Subject: [PATCH 2/5] Handle lack of defined load ordering --- Sources/CXXMessageQueue/include/mpsc/MessageQueue.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Sources/CXXMessageQueue/include/mpsc/MessageQueue.hpp b/Sources/CXXMessageQueue/include/mpsc/MessageQueue.hpp index 20fd2b6..84d1ce8 100644 --- a/Sources/CXXMessageQueue/include/mpsc/MessageQueue.hpp +++ b/Sources/CXXMessageQueue/include/mpsc/MessageQueue.hpp @@ -266,6 +266,7 @@ template inline auto MessageQueue::emptySlots() const noexcept -> SizeType { const auto writePos = writePosition_.load(std::memory_order_relaxed); const auto readPos = readPosition_.load(std::memory_order_relaxed); + const auto occupied = std::min(writePos - readPos, N); return N - (writePos - readPos); } @@ -282,7 +283,7 @@ template inline auto MessageQueue::occupiedSlots() const noexcept -> SizeType { const auto writePos = writePosition_.load(std::memory_order_relaxed); const auto readPos = readPosition_.load(std::memory_order_relaxed); - return writePos - readPos; + return std::min(writePos - readPos, N); } template From 29de0d64c8faf9d68d21d3398c52d2d21932cbab Mon Sep 17 00:00:00 2001 From: Stephen Booth Date: Fri, 10 Jul 2026 09:18:33 -0500 Subject: [PATCH 3/5] Fix error --- Sources/CXXMessageQueue/include/mpsc/MessageQueue.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/CXXMessageQueue/include/mpsc/MessageQueue.hpp b/Sources/CXXMessageQueue/include/mpsc/MessageQueue.hpp index 84d1ce8..017bdc6 100644 --- a/Sources/CXXMessageQueue/include/mpsc/MessageQueue.hpp +++ b/Sources/CXXMessageQueue/include/mpsc/MessageQueue.hpp @@ -267,7 +267,7 @@ inline auto MessageQueue::emptySlots() const noexcept -> SizeType { const auto writePos = writePosition_.load(std::memory_order_relaxed); const auto readPos = readPosition_.load(std::memory_order_relaxed); const auto occupied = std::min(writePos - readPos, N); - return N - (writePos - readPos); + return N - occupied; } template From 9a9fa0e0abf9f454a7eb10285820f9c614497737 Mon Sep 17 00:00:00 2001 From: Stephen Booth Date: Fri, 10 Jul 2026 09:49:11 -0500 Subject: [PATCH 4/5] Improve documentation --- .../include/mpsc/MessageQueue.hpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Sources/CXXMessageQueue/include/mpsc/MessageQueue.hpp b/Sources/CXXMessageQueue/include/mpsc/MessageQueue.hpp index 017bdc6..47c6cdb 100644 --- a/Sources/CXXMessageQueue/include/mpsc/MessageQueue.hpp +++ b/Sources/CXXMessageQueue/include/mpsc/MessageQueue.hpp @@ -89,24 +89,24 @@ class MessageQueue final { // MARK: Buffer Usage - /// Returns the number of empty slots in the message queue. + /// Returns an estimate of the number of empty or unclaimed slots in the message queue. /// @note The returned value is a transient snapshot and may become stale immediately after return. - /// @return The number of empty slots available for messages. + /// @return The estimated number of empty or unclaimed slots. [[nodiscard]] SizeType emptySlots() const noexcept [[clang::nonblocking]]; - /// Returns true if the message queue is full. + /// Returns true if the message queue appears full. /// @note The returned value is a transient snapshot and may become stale immediately after return. - /// @return true if the all slots in the queue are occupied. + /// @return true if all slots in the queue appear to be occupied or claimed. [[nodiscard]] bool isFull() const noexcept [[clang::nonblocking]]; - /// Returns the number of occupied slots in the message queue. + /// Returns an estimate of the number of occupied or claimed slots in the message queue. /// @note The returned value is a transient snapshot and may become stale immediately after return. - /// @return The number of occupied slots containing messages. + /// @return The estimated number of occupied or claimed slots. [[nodiscard]] SizeType occupiedSlots() const noexcept [[clang::nonblocking]]; - /// Returns true if the message queue is empty. + /// Returns true if the message queue appears empty. /// @note The returned value is a transient snapshot and may become stale immediately after return. - /// @return true if all slots in the queue are empty. + /// @return true if no slots in the queue appear to be occupied or claimed. [[nodiscard]] bool isEmpty() const noexcept [[clang::nonblocking]]; // MARK: Enqueuing Messages From 712a09a16f6d0a0b184d24aaca88f105be59a4a4 Mon Sep 17 00:00:00 2001 From: Stephen Booth Date: Fri, 10 Jul 2026 09:52:14 -0500 Subject: [PATCH 5/5] Reorder --- .../include/mpsc/MessageQueue.hpp | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/Sources/CXXMessageQueue/include/mpsc/MessageQueue.hpp b/Sources/CXXMessageQueue/include/mpsc/MessageQueue.hpp index 47c6cdb..ac716b7 100644 --- a/Sources/CXXMessageQueue/include/mpsc/MessageQueue.hpp +++ b/Sources/CXXMessageQueue/include/mpsc/MessageQueue.hpp @@ -79,7 +79,7 @@ class MessageQueue final { /// Destroys the message queue and releases all associated resources. ~MessageQueue() noexcept = default; - // MARK: Buffer Information + // MARK: Slot Count and Capacity /// The number of slots in the message queue. static constexpr auto slotCount = N; @@ -87,28 +87,28 @@ class MessageQueue final { /// The capacity of a single slot in the message queue in bytes. static constexpr auto slotCapacity = C; - // MARK: Buffer Usage + // MARK: Statistics - /// Returns an estimate of the number of empty or unclaimed slots in the message queue. + /// Returns true if the message queue appears empty. /// @note The returned value is a transient snapshot and may become stale immediately after return. - /// @return The estimated number of empty or unclaimed slots. - [[nodiscard]] SizeType emptySlots() const noexcept [[clang::nonblocking]]; + /// @return true if no slots in the queue appear to be occupied or claimed. + [[nodiscard]] bool isEmpty() const noexcept [[clang::nonblocking]]; /// Returns true if the message queue appears full. /// @note The returned value is a transient snapshot and may become stale immediately after return. /// @return true if all slots in the queue appear to be occupied or claimed. [[nodiscard]] bool isFull() const noexcept [[clang::nonblocking]]; + /// Returns an estimate of the number of empty or unclaimed slots in the message queue. + /// @note The returned value is a transient snapshot and may become stale immediately after return. + /// @return The estimated number of empty or unclaimed slots. + [[nodiscard]] SizeType emptySlots() const noexcept [[clang::nonblocking]]; + /// Returns an estimate of the number of occupied or claimed slots in the message queue. /// @note The returned value is a transient snapshot and may become stale immediately after return. /// @return The estimated number of occupied or claimed slots. [[nodiscard]] SizeType occupiedSlots() const noexcept [[clang::nonblocking]]; - /// Returns true if the message queue appears empty. - /// @note The returned value is a transient snapshot and may become stale immediately after return. - /// @return true if no slots in the queue appear to be occupied or claimed. - [[nodiscard]] bool isEmpty() const noexcept [[clang::nonblocking]]; - // MARK: Enqueuing Messages /// Enqueues a message in the next available slot and advances the write position. @@ -259,15 +259,14 @@ constexpr MessageQueue::MessageQueue() noexcept { } } -// MARK: Buffer Usage +// MARK: Statistics template requires ValidPowerOfTwo && ValidPowerOfTwo -inline auto MessageQueue::emptySlots() const noexcept -> SizeType { +inline bool MessageQueue::isEmpty() const noexcept { const auto writePos = writePosition_.load(std::memory_order_relaxed); const auto readPos = readPosition_.load(std::memory_order_relaxed); - const auto occupied = std::min(writePos - readPos, N); - return N - occupied; + return writePos == readPos; } template @@ -280,18 +279,19 @@ inline bool MessageQueue::isFull() const noexcept { template requires ValidPowerOfTwo && ValidPowerOfTwo -inline auto MessageQueue::occupiedSlots() const noexcept -> SizeType { +inline auto MessageQueue::emptySlots() const noexcept -> SizeType { const auto writePos = writePosition_.load(std::memory_order_relaxed); const auto readPos = readPosition_.load(std::memory_order_relaxed); - return std::min(writePos - readPos, N); + const auto occupied = std::min(writePos - readPos, N); + return N - occupied; } template requires ValidPowerOfTwo && ValidPowerOfTwo -inline bool MessageQueue::isEmpty() const noexcept { +inline auto MessageQueue::occupiedSlots() const noexcept -> SizeType { const auto writePos = writePosition_.load(std::memory_order_relaxed); const auto readPos = readPosition_.load(std::memory_order_relaxed); - return writePos == readPos; + return std::min(writePos - readPos, N); } // MARK: Enqueuing Messages