Common System 0.2.0
Common interfaces and patterns for system integration
Loading...
Searching...
No Matches
kcenon::common::utils::SPSCCircularBuffer< T, Capacity > Class Template Reference

Lock-free circular buffer for single-producer single-consumer (SPSC). More...

#include <circular_buffer.h>

Collaboration diagram for kcenon::common::utils::SPSCCircularBuffer< T, Capacity >:
Collaboration graph

Public Member Functions

 SPSCCircularBuffer ()=default
 
bool push (const T &value)
 Push a value (producer thread only).
 
bool push (T &&value)
 
std::optional< T > pop ()
 Pop a value (consumer thread only).
 
bool empty () const noexcept
 
bool full () const noexcept
 
std::size_t size () const noexcept
 
constexpr std::size_t capacity () const noexcept
 

Static Private Member Functions

static std::size_t advance (std::size_t index) noexcept
 

Private Attributes

std::array< T, Capacity+1 > buffer_ {}
 
std::atomic< std::size_t > head_ {0}
 
std::atomic< std::size_t > tail_ {0}
 

Detailed Description

template<typename T, std::size_t Capacity>
class kcenon::common::utils::SPSCCircularBuffer< T, Capacity >

Lock-free circular buffer for single-producer single-consumer (SPSC).

Uses std::atomic head/tail with acquire/release ordering. No mutex is needed because only the producer modifies tail_ and only the consumer modifies head_. Capacity must be > 0. One slot is reserved to distinguish full from empty, so the usable capacity is exactly Capacity elements.

Examples
utility_containers_example.cpp.

Definition at line 118 of file circular_buffer.h.

Constructor & Destructor Documentation

◆ SPSCCircularBuffer()

template<typename T , std::size_t Capacity>
kcenon::common::utils::SPSCCircularBuffer< T, Capacity >::SPSCCircularBuffer ( )
default

Member Function Documentation

◆ advance()

template<typename T , std::size_t Capacity>
static std::size_t kcenon::common::utils::SPSCCircularBuffer< T, Capacity >::advance ( std::size_t index)
inlinestaticprivatenoexcept

◆ capacity()

template<typename T , std::size_t Capacity>
std::size_t kcenon::common::utils::SPSCCircularBuffer< T, Capacity >::capacity ( ) const
inlinenodiscardconstexprnoexcept

Definition at line 183 of file circular_buffer.h.

183 {
184 return Capacity;
185 }

◆ empty()

template<typename T , std::size_t Capacity>
bool kcenon::common::utils::SPSCCircularBuffer< T, Capacity >::empty ( ) const
inlinenodiscardnoexcept

Definition at line 164 of file circular_buffer.h.

164 {
165 return head_.load(std::memory_order_acquire)
166 == tail_.load(std::memory_order_acquire);
167 }

References kcenon::common::utils::SPSCCircularBuffer< T, Capacity >::head_, and kcenon::common::utils::SPSCCircularBuffer< T, Capacity >::tail_.

◆ full()

template<typename T , std::size_t Capacity>
bool kcenon::common::utils::SPSCCircularBuffer< T, Capacity >::full ( ) const
inlinenodiscardnoexcept

Definition at line 169 of file circular_buffer.h.

169 {
170 return advance(tail_.load(std::memory_order_acquire))
171 == head_.load(std::memory_order_acquire);
172 }
static std::size_t advance(std::size_t index) noexcept

References kcenon::common::utils::SPSCCircularBuffer< T, Capacity >::advance(), kcenon::common::utils::SPSCCircularBuffer< T, Capacity >::head_, and kcenon::common::utils::SPSCCircularBuffer< T, Capacity >::tail_.

Here is the call graph for this function:

◆ pop()

template<typename T , std::size_t Capacity>
std::optional< T > kcenon::common::utils::SPSCCircularBuffer< T, Capacity >::pop ( )
inlinenodiscard

Pop a value (consumer thread only).

Returns
The value if available, std::nullopt if empty.
Examples
utility_containers_example.cpp.

Definition at line 154 of file circular_buffer.h.

154 {
155 const auto head = head_.load(std::memory_order_relaxed);
156 if (head == tail_.load(std::memory_order_acquire)) {
157 return std::nullopt; // empty
158 }
159 auto value = std::move(buffer_[head]);
160 head_.store(advance(head), std::memory_order_release);
161 return value;
162 }

References kcenon::common::utils::SPSCCircularBuffer< T, Capacity >::advance(), kcenon::common::utils::SPSCCircularBuffer< T, Capacity >::buffer_, kcenon::common::utils::SPSCCircularBuffer< T, Capacity >::head_, and kcenon::common::utils::SPSCCircularBuffer< T, Capacity >::tail_.

Referenced by main().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ push() [1/2]

template<typename T , std::size_t Capacity>
bool kcenon::common::utils::SPSCCircularBuffer< T, Capacity >::push ( const T & value)
inline

Push a value (producer thread only).

Returns
true if pushed, false if full.
Examples
utility_containers_example.cpp.

Definition at line 128 of file circular_buffer.h.

128 {
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)) {
132 return false; // full
133 }
134 buffer_[tail] = value;
135 tail_.store(next_tail, std::memory_order_release);
136 return true;
137 }

References kcenon::common::utils::SPSCCircularBuffer< T, Capacity >::advance(), kcenon::common::utils::SPSCCircularBuffer< T, Capacity >::buffer_, kcenon::common::utils::SPSCCircularBuffer< T, Capacity >::head_, and kcenon::common::utils::SPSCCircularBuffer< T, Capacity >::tail_.

Referenced by main().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ push() [2/2]

template<typename T , std::size_t Capacity>
bool kcenon::common::utils::SPSCCircularBuffer< T, Capacity >::push ( T && value)
inline

Definition at line 139 of file circular_buffer.h.

139 {
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)) {
143 return false; // full
144 }
145 buffer_[tail] = std::move(value);
146 tail_.store(next_tail, std::memory_order_release);
147 return true;
148 }

References kcenon::common::utils::SPSCCircularBuffer< T, Capacity >::advance(), kcenon::common::utils::SPSCCircularBuffer< T, Capacity >::buffer_, kcenon::common::utils::SPSCCircularBuffer< T, Capacity >::head_, and kcenon::common::utils::SPSCCircularBuffer< T, Capacity >::tail_.

Here is the call graph for this function:

◆ size()

template<typename T , std::size_t Capacity>
std::size_t kcenon::common::utils::SPSCCircularBuffer< T, Capacity >::size ( ) const
inlinenodiscardnoexcept
Examples
utility_containers_example.cpp.

Definition at line 174 of file circular_buffer.h.

174 {
175 const auto head = head_.load(std::memory_order_acquire);
176 const auto tail = tail_.load(std::memory_order_acquire);
177 if (tail >= head) {
178 return tail - head;
179 }
180 return (Capacity + 1) - head + tail;
181 }

References kcenon::common::utils::SPSCCircularBuffer< T, Capacity >::head_, and kcenon::common::utils::SPSCCircularBuffer< T, Capacity >::tail_.

Referenced by main().

Here is the caller graph for this function:

Member Data Documentation

◆ buffer_

template<typename T , std::size_t Capacity>
std::array<T, Capacity + 1> kcenon::common::utils::SPSCCircularBuffer< T, Capacity >::buffer_ {}
private

◆ head_

◆ tail_


The documentation for this class was generated from the following file: