Internal Interface Wrap
API reference
Loading...
Searching...
No Matches
HfSpscRing.h
Go to the documentation of this file.
1
21#pragma once
22
23#include <atomic>
24#include <cstddef>
25#include <cstdint>
26#include <memory>
27#include <new>
28
29template <typename T>
31public:
33 static constexpr uint32_t kMaxCapacity = 1U << 16;
34
35 HfSpscRing() noexcept = default;
36 ~HfSpscRing() noexcept = default;
37
38 HfSpscRing(const HfSpscRing&) = delete;
39 HfSpscRing& operator=(const HfSpscRing&) = delete;
41 HfSpscRing& operator=(HfSpscRing&&) = delete;
42
49 bool Init(uint32_t min_capacity) noexcept {
50 Release();
51 if (min_capacity == 0U || min_capacity > kMaxCapacity) {
52 return false;
53 }
54 uint32_t cap = 1U;
55 while (cap < min_capacity) {
56 cap <<= 1U;
57 }
58 slots_.reset(new (std::nothrow) T[cap]);
59 if (!slots_) {
60 return false;
61 }
62 capacity_ = cap;
63 mask_ = cap - 1U;
64 return true;
65 }
66
68 void Release() noexcept {
69 slots_.reset();
70 capacity_ = 0U;
71 mask_ = 0U;
72 head_.store(0U, std::memory_order_relaxed);
73 tail_.store(0U, std::memory_order_relaxed);
74 overruns_.store(0U, std::memory_order_relaxed);
75 peak_.store(0U, std::memory_order_relaxed);
76 }
77
79 bool IsReady() const noexcept {
80 return capacity_ != 0U;
81 }
82
84 uint32_t Capacity() const noexcept {
85 return capacity_;
86 }
87
93 bool TryPush(const T& item) noexcept {
94 if (capacity_ == 0U) {
95 return false;
96 }
97 const uint32_t head = head_.load(std::memory_order_relaxed);
98 const uint32_t tail = tail_.load(std::memory_order_acquire);
99 const uint32_t used = head - tail;
100 if (used >= capacity_) {
101 overruns_.fetch_add(1U, std::memory_order_relaxed);
102 return false;
103 }
104 slots_[head & mask_] = item;
105 head_.store(head + 1U, std::memory_order_release);
106 const uint32_t now_used = used + 1U;
107 if (now_used > peak_.load(std::memory_order_relaxed)) {
108 peak_.store(now_used, std::memory_order_relaxed);
109 }
110 return true;
111 }
112
117 bool TryPop(T& out) noexcept {
118 if (capacity_ == 0U) {
119 return false;
120 }
121 const uint32_t tail = tail_.load(std::memory_order_relaxed);
122 const uint32_t head = head_.load(std::memory_order_acquire);
123 if (head == tail) {
124 return false;
125 }
126 out = slots_[tail & mask_];
127 tail_.store(tail + 1U, std::memory_order_release);
128 return true;
129 }
130
134 void Clear() noexcept {
135 tail_.store(head_.load(std::memory_order_acquire), std::memory_order_release);
136 }
137
139 uint32_t Size() const noexcept {
140 return head_.load(std::memory_order_acquire) - tail_.load(std::memory_order_acquire);
141 }
142
144 bool Empty() const noexcept {
145 return Size() == 0U;
146 }
147
149 uint32_t Overruns() const noexcept {
150 return overruns_.load(std::memory_order_relaxed);
151 }
152
154 uint32_t Peak() const noexcept {
155 return peak_.load(std::memory_order_relaxed);
156 }
157
159 void ResetCounters() noexcept {
160 overruns_.store(0U, std::memory_order_relaxed);
161 peak_.store(0U, std::memory_order_relaxed);
162 }
163
164private:
165 std::unique_ptr<T[]> slots_{};
166 uint32_t capacity_{0U};
167 uint32_t mask_{0U};
168 std::atomic<uint32_t> head_{0U};
169 std::atomic<uint32_t> tail_{0U};
170 std::atomic<uint32_t> overruns_{0U};
171 std::atomic<uint32_t> peak_{0U};
172};
Definition HfSpscRing.h:30
bool Empty() const noexcept
True when nothing is queued (snapshot).
Definition HfSpscRing.h:144
bool TryPush(const T &item) noexcept
Producer side: append a copy of item.
Definition HfSpscRing.h:93
void Release() noexcept
Free the storage. Not thread-safe.
Definition HfSpscRing.h:68
std::atomic< uint32_t > peak_
highest fill level
Definition HfSpscRing.h:171
uint32_t capacity_
Definition HfSpscRing.h:166
std::atomic< uint32_t > head_
next slot to write (producer owned)
Definition HfSpscRing.h:168
uint32_t mask_
Definition HfSpscRing.h:167
uint32_t Peak() const noexcept
Highest fill level seen, since Init() or ResetCounters().
Definition HfSpscRing.h:154
bool Init(uint32_t min_capacity) noexcept
Allocate storage for at least min_capacity elements.
Definition HfSpscRing.h:49
uint32_t Size() const noexcept
Elements queued now (a snapshot when the other side is running).
Definition HfSpscRing.h:139
std::unique_ptr< T[]> slots_
Definition HfSpscRing.h:165
void ResetCounters() noexcept
Zero the overrun and peak counters (the queued elements stay).
Definition HfSpscRing.h:159
uint32_t Overruns() const noexcept
Pushes refused because the ring was full, since Init() or ResetCounters().
Definition HfSpscRing.h:149
bool TryPop(T &out) noexcept
Consumer side: remove the oldest element into out.
Definition HfSpscRing.h:117
void Clear() noexcept
Consumer side: drop every queued element.
Definition HfSpscRing.h:134
static constexpr uint32_t kMaxCapacity
Largest capacity Init() accepts.
Definition HfSpscRing.h:33
std::atomic< uint32_t > tail_
next slot to read (consumer owned)
Definition HfSpscRing.h:169
uint32_t Capacity() const noexcept
Number of elements the ring can hold (a power of two, 0 before Init()).
Definition HfSpscRing.h:84
std::atomic< uint32_t > overruns_
refused pushes
Definition HfSpscRing.h:170
HfSpscRing() noexcept=default
bool IsReady() const noexcept
True after a successful Init().
Definition HfSpscRing.h:79