1+ #pragma once
2+
3+ #include < atomic>
4+ #include < vector>
5+ #include < optional>
6+ #include < cassert>
7+
8+ // Lock-Free Single-Producer-Single-Consumer (SPSC) Ring Buffer
9+ // Author: cLog contributors
10+
11+ namespace c_log {
12+
13+ // Generic Ring Buffer for SPSC context
14+ template <typename T>
15+ class SPSCRingBuffer {
16+ public:
17+ explicit SPSCRingBuffer (size_t capacity) : capacity_(capacity), buffer_(capacity), head_(0 ), tail_(0 ) {
18+ assert (capacity > 0 && " Capacity must be greater than zero" );
19+ }
20+
21+ // Produces an element. Returns true if successful, false if the buffer is full.
22+ bool push (const T& item) {
23+ size_t head = head_.load (std::memory_order_relaxed);
24+ size_t next_head = (head + 1 ) % capacity_;
25+
26+ // Check if the buffer is full.
27+ if (next_head == tail_.load (std::memory_order_acquire)) {
28+ return false ; // Buffer full.
29+ }
30+
31+ buffer_[head] = item;
32+ head_.store (next_head, std::memory_order_release);
33+ return true ;
34+ }
35+
36+ // Consumes an element. Returns std::optional<T>.
37+ std::optional<T> pop () {
38+ size_t tail = tail_.load (std::memory_order_relaxed);
39+
40+ // Check if the buffer is empty.
41+ if (tail == head_.load (std::memory_order_acquire)) {
42+ return std::nullopt ; // Buffer empty.
43+ }
44+
45+ T item = buffer_[tail];
46+ tail_.store ((tail + 1 ) % capacity_, std::memory_order_release);
47+ return item;
48+ }
49+
50+ private:
51+ const size_t capacity_;
52+ std::vector<T> buffer_;
53+ std::atomic<size_t > head_;
54+ std::atomic<size_t > tail_;
55+ };
56+
57+ } // namespace c_log
0 commit comments