24template<
typename T, std::
size_t Capacity>
26 static_assert(Capacity > 0,
"CircularBuffer capacity must be greater than zero");
31 bool push(
const T& value,
bool overwrite =
false) {
32 std::lock_guard<std::mutex> lock(
mutex_);
45 bool push(T&& value,
bool overwrite =
false) {
46 std::lock_guard<std::mutex> lock(
mutex_);
59 [[nodiscard]] std::optional<T>
pop() {
60 std::lock_guard<std::mutex> lock(
mutex_);
64 [[nodiscard]]
bool empty()
const {
65 std::lock_guard<std::mutex> lock(
mutex_);
69 [[nodiscard]]
bool full()
const {
70 std::lock_guard<std::mutex> lock(
mutex_);
71 return size_ == Capacity;
74 [[nodiscard]] std::size_t
size()
const {
75 std::lock_guard<std::mutex> lock(
mutex_);
79 [[nodiscard]]
constexpr std::size_t
capacity()
const {
84 void advance(std::size_t& index)
noexcept {
85 index = (index + 1) % Capacity;
89 return size_ == Capacity;
117template<
typename T, std::
size_t Capacity>
119 static_assert(Capacity > 0,
"SPSCCircularBuffer capacity must be greater than zero");
129 const auto tail =
tail_.load(std::memory_order_relaxed);
130 const auto next_tail =
advance(tail);
131 if (next_tail ==
head_.load(std::memory_order_acquire)) {
135 tail_.store(next_tail, std::memory_order_release);
140 const auto tail =
tail_.load(std::memory_order_relaxed);
141 const auto next_tail =
advance(tail);
142 if (next_tail ==
head_.load(std::memory_order_acquire)) {
145 buffer_[tail] = std::move(value);
146 tail_.store(next_tail, std::memory_order_release);
154 [[nodiscard]] std::optional<T>
pop() {
155 const auto head =
head_.load(std::memory_order_relaxed);
156 if (head ==
tail_.load(std::memory_order_acquire)) {
159 auto value = std::move(
buffer_[head]);
164 [[nodiscard]]
bool empty() const noexcept {
165 return head_.load(std::memory_order_acquire)
166 ==
tail_.load(std::memory_order_acquire);
169 [[nodiscard]]
bool full() const noexcept {
171 ==
head_.load(std::memory_order_acquire);
174 [[nodiscard]] std::size_t
size() const noexcept {
175 const auto head =
head_.load(std::memory_order_acquire);
176 const auto tail =
tail_.load(std::memory_order_acquire);
180 return (Capacity + 1) - head + tail;
183 [[nodiscard]]
constexpr std::size_t
capacity() const noexcept {
188 static std::size_t
advance(std::size_t index)
noexcept {
189 return (index + 1) % (Capacity + 1);
194 alignas(64) std::atomic<std::size_t>
head_{0};
195 alignas(64) std::atomic<std::size_t>
tail_{0};
constexpr std::size_t capacity() const
bool push(T &&value, bool overwrite=false)
bool push(const T &value, bool overwrite=false)
void advance(std::size_t &index) noexcept
std::optional< T > pop_locked()
bool is_full_locked() const noexcept
std::array< T, Capacity > buffer_
Lock-free circular buffer for single-producer single-consumer (SPSC).
bool push(const T &value)
Push a value (producer thread only).
SPSCCircularBuffer()=default
bool full() const noexcept
std::size_t size() const noexcept
bool empty() const noexcept
std::atomic< std::size_t > tail_
constexpr std::size_t capacity() const noexcept
std::atomic< std::size_t > head_
std::optional< T > pop()
Pop a value (consumer thread only).
std::array< T, Capacity+1 > buffer_
static std::size_t advance(std::size_t index) noexcept