#include using namespace std; constexpr size_t kCacheLineSize = 64; template class LockFreeRingBuffer { static_assert((Capacity & (Capacity - 1)) == 0, "Capacity must be a power of 2"); static_assert(std::is_trivially_copyable_v || std::is_move_constructible_v, "T should be trivially copyable or movable for trading perf"); public: bool push(const T& item) noexcept { const size_t head = head_.load(std::memory_order_relaxed); const size_t next_head = (head + 1) & mask_; // Acquire the remote tail to see latest consumer progress if (next_head == tail_.load(std::memory_order_acquire)) { return false; // full – in HFT you can overwrite oldest or drop } buffer_[head] = item; // write data (or std::move if rvalue) head_.store(next_head, std::memory_order_release); return true; } bool pop(T& item) noexcept { const size_t tail = tail_.load(std::memory_order_relaxed); // Acquire the remote head to see latest producer data if (tail == head_.load(std::memory_order_acquire)) { return false; // empty } item = std::move(buffer_[tail]); // move for efficiency (or copy) tail_.store((tail + 1) & mask_, std::memory_order_release); return true; } // Optional: non-blocking peek, size estimate, etc. size_t size_approx() const noexcept { const size_t h = head_.load(std::memory_order_relaxed); const size_t t = tail_.load(std::memory_order_relaxed); return (h - t) & mask_; // approximate only } tuple stats() { const size_t head = head_.load(std::memory_order_relaxed); const size_t tail = tail_.load(std::memory_order_relaxed); return tuple(head, tail); } private: // Buffer on its own cache line (producer writes here) alignas(kCacheLineSize) std::array buffer_{}; // Producer index – on its own cache line alignas(kCacheLineSize) std::atomic head_{0}; // Consumer index – on its own cache line alignas(kCacheLineSize) std::atomic tail_{0}; static constexpr size_t mask_ = Capacity - 1; }; int main(void) { vector>> testCases = { { {1,2,3,4,5} }, }; LockFreeRingBuffer buffer; for (auto& tc: testCases) { vector input = get<0>(tc); cout << "push 1,2,3,4,5\n"; for (auto& i: input) { buffer.push(i); tuple data = buffer.stats(); cout << format(" stats: head: {} tail: {}\n", get<0>(data), get<1>(data)); } cout << format("buffer_size: {}\n" , buffer.size_approx()); for ([[maybe_unused]] auto& i: {1,2,3,4,5}) { int val; buffer.pop(val); cout << format("pop: {}\n" , val); tuple data = buffer.stats(); cout << format(" stats: head: {} tail: {}\n", get<0>(data), get<1>(data)); } buffer.push(6); cout << "push(6)\n"; tuple data = buffer.stats(); cout << format(" stats: head: {} tail: {}\n", get<0>(data), get<1>(data)); int val; buffer.pop(val); cout << format("pop: {}\n" , val); tuple data1 = buffer.stats(); cout << format(" stats: head: {} tail: {}\n", get<0>(data1), get<1>(data1)); } return 0; }