9#include "libxr_def.hpp"
10#include "libxr_mem.hpp"
38 : element_size_(element_size),
40 payload_alloc_align_(alignof(std::max_align_t)),
41 payload_stride_(ComputeStride(element_size, alignof(std::byte))),
56 SPSCQueueBase(
size_t element_size,
size_t element_align,
size_t capacity)
57 : element_size_(element_size),
59 payload_alloc_align_(alignof(std::max_align_t)),
60 payload_stride_(ComputeStride(element_size, element_align)),
73 static_assert(
alignof(std::max_align_t) > 0 &&
74 (
alignof(std::max_align_t) & (
alignof(std::max_align_t) - 1)) == 0);
75 ASSERT(element_size_ > 0);
76 ASSERT(capacity_ > 0);
77 ASSERT(capacity_ <= std::numeric_limits<size_t>::max() - 1);
79 const size_t payload_bytes = MultiplyChecked(payload_stride_, RingCapacity());
80 payloads_ =
static_cast<std::byte*
>(
81 ::operator
new[](payload_bytes, std::align_val_t(payload_alloc_align_)));
90 ::operator
delete[](payloads_, std::align_val_t(payload_alloc_align_));
103 if (value ==
nullptr)
108 const auto current_tail = tail_.load(std::memory_order_relaxed);
109 const auto next_tail = Increment(current_tail);
111 if (next_tail == head_.load(std::memory_order_acquire))
117 tail_.store(next_tail, std::memory_order_release);
132 const auto current_head = head_.load(std::memory_order_relaxed);
134 if (current_head == tail_.load(std::memory_order_acquire))
139 if (value !=
nullptr)
144 head_.store(Increment(current_head), std::memory_order_release);
159 if (value ==
nullptr)
164 const auto current_head = head_.load(std::memory_order_relaxed);
165 if (current_head == tail_.load(std::memory_order_acquire))
195 const auto current_tail = tail_.load(std::memory_order_relaxed);
196 const auto current_head = head_.load(std::memory_order_acquire);
197 const size_t capacity = RingCapacity();
198 const size_t free_space = (current_tail >= current_head)
199 ? (capacity - (current_tail - current_head) - 1)
200 : (current_head - current_tail - 1);
202 if (free_space < count)
207 const auto* src =
static_cast<const std::byte*
>(data);
208 for (
size_t index = 0; index < count; ++index)
211 src + index * element_size_, element_size_);
214 tail_.store((current_tail + count) % capacity, std::memory_order_release);
233 template <
typename Writer>
241 const auto current_tail = tail_.load(std::memory_order_relaxed);
242 const auto current_head = head_.load(std::memory_order_acquire);
243 const size_t capacity = RingCapacity();
244 const size_t free_space = (current_tail >= current_head)
245 ? (capacity - (current_tail - current_head) - 1)
246 : (current_head - current_tail - 1);
248 if (free_space < count)
253 const size_t first_chunk = std::min(count, capacity - current_tail);
254 Writer& writer_ref = writer;
255 const ErrorCode first_ec = writer_ref(PayloadPtr(current_tail), first_chunk);
261 if (count > first_chunk)
263 const ErrorCode second_ec = writer_ref(PayloadPtr(0), count - first_chunk);
270 tail_.store((current_tail + count) % capacity, std::memory_order_release);
289 template <
typename Writer>
297 const auto current_tail = tail_.load(std::memory_order_relaxed);
298 const auto current_head = head_.load(std::memory_order_acquire);
299 const size_t capacity = RingCapacity();
300 const size_t free_space = (current_tail >= current_head)
301 ? (capacity - (current_tail - current_head) - 1U)
302 : (current_head - current_tail - 1U);
303 const size_t offered = std::min(limit, free_space);
309 const size_t first_count = std::min(offered, capacity - current_tail);
310 const size_t second_count = offered - first_count;
311 void*
const second = second_count == 0U ? nullptr : PayloadPtr(0U);
312 const size_t produced =
313 writer(PayloadPtr(current_tail), first_count, second, second_count);
314 ASSERT(produced <= offered);
317 tail_.store((current_tail + produced) % capacity, std::memory_order_release);
338 const auto current_head = head_.load(std::memory_order_relaxed);
339 const auto current_tail = tail_.load(std::memory_order_acquire);
340 const size_t capacity = RingCapacity();
341 const size_t available = (current_tail >= current_head)
342 ? (current_tail - current_head)
343 : (capacity - current_head + current_tail);
345 if (available < count)
350 auto* dst =
static_cast<std::byte*
>(data);
353 for (
size_t index = 0; index < count; ++index)
356 PayloadPtr((current_head + index) % capacity),
361 head_.store((current_head + count) % capacity, std::memory_order_release);
381 template <
typename Reader>
389 const auto current_head = head_.load(std::memory_order_relaxed);
390 const auto current_tail = tail_.load(std::memory_order_acquire);
391 const size_t capacity = RingCapacity();
392 const size_t available = (current_tail >= current_head)
393 ? (current_tail - current_head)
394 : (capacity - current_head + current_tail);
396 if (available < count)
401 const size_t first_chunk = std::min(count, capacity - current_head);
402 Reader& reader_ref = reader;
403 const ErrorCode first_ec = reader_ref(PayloadPtr(current_head), first_chunk);
409 if (count > first_chunk)
411 const ErrorCode second_ec = reader_ref(PayloadPtr(0), count - first_chunk);
418 head_.store((current_head + count) % capacity, std::memory_order_release);
437 template <
typename Reader>
445 const auto current_head = head_.load(std::memory_order_relaxed);
446 const auto current_tail = tail_.load(std::memory_order_acquire);
447 const size_t capacity = RingCapacity();
448 const size_t available = (current_tail >= current_head)
449 ? (current_tail - current_head)
450 : (capacity - current_head + current_tail);
451 const size_t offered = std::min(limit, available);
457 const size_t first_count = std::min(offered, capacity - current_head);
458 const size_t second_count = offered - first_count;
459 const void*
const second = second_count == 0U ? nullptr : PayloadPtr(0U);
460 const size_t accepted =
461 reader(PayloadPtr(current_head), first_count, second, second_count);
462 ASSERT(accepted <= offered);
465 head_.store((current_head + accepted) % capacity, std::memory_order_release);
492 const auto current_head = head_.load(std::memory_order_relaxed);
493 const auto current_tail = tail_.load(std::memory_order_acquire);
494 const size_t capacity = RingCapacity();
495 const size_t available = (current_tail >= current_head)
496 ? (current_tail - current_head)
497 : (capacity - current_head + current_tail);
499 if (available < count)
504 auto* dst =
static_cast<std::byte*
>(data);
505 for (
size_t index = 0; index < count; ++index)
508 PayloadPtr((current_head + index) % capacity),
528 const auto current_tail = tail_.load(std::memory_order_acquire);
529 head_.store(current_tail, std::memory_order_release);
538 const auto current_head = head_.load(std::memory_order_acquire);
539 const auto current_tail = tail_.load(std::memory_order_acquire);
540 return (current_tail >= current_head)
541 ? (current_tail - current_head)
542 : (RingCapacity() - current_head + current_tail);
564 std::byte* PayloadPtr(
IndexType index) {
return payloads_ + index * payload_stride_; }
573 const std::byte* PayloadPtr(
IndexType index)
const
575 return payloads_ + index * payload_stride_;
583 size_t RingCapacity()
const {
return capacity_ + 1; }
607 static size_t AlignUpChecked(
size_t size,
size_t align)
609 DEV_ASSERT(align > 0);
610 DEV_ASSERT((align & (align - 1)) == 0);
611 ASSERT(size <= std::numeric_limits<size_t>::max() - (align - 1));
612 return ((size + align - 1) / align) * align;
615 static size_t ComputeStride(
size_t element_size,
size_t element_align)
617 ASSERT(element_size > 0);
618 ASSERT(element_align > 0);
619 ASSERT((element_align & (element_align - 1)) == 0);
620 return AlignUpChecked(element_size, element_align);
629 static size_t MultiplyChecked(
size_t lhs,
size_t rhs)
631 if (lhs == 0 || rhs == 0)
636 ASSERT(lhs <= std::numeric_limits<size_t>::max() / rhs);
640 const size_t element_size_;
641 const size_t capacity_;
642 const size_t payload_alloc_align_;
644 const size_t payload_stride_;
646 std::byte* payloads_;
static void FastCopy(void *dst, const void *src, size_t size)
快速内存拷贝 / Fast memory copy
单生产者单消费者字节队列内核 / Single-producer single-consumer byte-queue core
size_t MaxSize() const
获取队列最大容量 / Get the maximum queue capacity
void Reset()
从 consumer 侧丢弃当前可见的全部 payload / Discard all currently available payloads from the consumer side
~SPSCQueueBase()
析构 SPSC 字节队列内核 / Destroy the SPSC byte-queue core
SPSCQueueBase(size_t element_size, size_t element_align, size_t capacity)
构造 SPSC 字节队列内核并指定元素对齐 / Construct the SPSC byte-queue core with explicit element alignment
size_t IndexType
环形缓冲区索引类型 / Ring-buffer index type.
ErrorCode PeekBytes(void *value)
按字节查看一个队头 payload 但不出队 / Peek one front payload by bytes without dequeuing it
ErrorCode PeekBatchBytes(void *data, size_t count)
按字节批量查看多个 payload 但不出队 / Peek multiple payloads by bytes without dequeuing them
size_t Size() const
获取当前已用元素数 / Get the current element count
SPSCQueueBase(size_t element_size, size_t capacity)
构造 SPSC 字节队列内核 / Construct the SPSC byte-queue core
ErrorCode PushBytes(const void *value)
按字节入队一个 payload / Enqueue one payload by bytes
size_t ProduceWithWriter(size_t limit, Writer &&writer)
通过一次双 span 回调生产 payload 前缀 / Produce a payload prefix through one two-span writer callback.
size_t EmptySize() const
获取剩余空槽数 / Get the current free-slot count
ErrorCode PopBatchBytes(void *data, size_t count)
按字节批量出队多个 payload / Dequeue multiple payloads by bytes
ErrorCode PushBatchBytes(const void *data, size_t count)
按字节批量入队多个 payload / Enqueue multiple payloads by bytes
ErrorCode PopBytesWithReader(size_t count, Reader &&reader)
通过读取器回调批量出队 payload / Dequeue payloads through a reader callback
ErrorCode PopBytes(void *value=nullptr)
按字节出队一个 payload;传空指针时仅丢弃队头元素 / Dequeue one payload by bytes; pass null to discard the front item only
size_t ConsumeWithReader(size_t limit, Reader &&reader)
通过一次双 span 回调消费 payload 前缀 / Consume a payload prefix through one two-span reader callback.
ErrorCode PushBytesWithWriter(size_t count, Writer &&writer)
通过写入器回调批量入队 payload / Enqueue payloads through a writer callback
LibXR 命名空间 / LibXR namespace.
ErrorCode
定义错误码枚举 / Defines an enumeration for error codes
@ PTR_NULL
空指针 / Null pointer
@ OK
操作成功 / Operation successful
constexpr size_t CONCURRENCY_ALIGNMENT
并发结构对齐粒度(用于降低多核伪共享) / Alignment policy used by concurrency-oriented structures