Skip to content

Commit 4f9db4d

Browse files
committed
Add robust SPSC ring buffer header for logging infrastructure (C++17 safe)
1 parent 603858a commit 4f9db4d

File tree

1 file changed

+57
-0
lines changed

1 file changed

+57
-0
lines changed

include/spsc_ring_buffer.hpp

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
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

Comments
 (0)