#pragma once #include "spinlock.h" #include namespace vlc { template class FixedRingBuffer { static_assert((max_capacity & (max_capacity - 1)) == 0, "Capacity is not power of 2!"); Spinlock m_lock; volatile uint32_t m_write = 0; volatile uint32_t m_read = 0; T m_buffer[max_capacity]; static inline size_t index(uint32_t x) { return x & (max_capacity - 1); } public: FixedRingBuffer() = default; FixedRingBuffer(const FixedRingBuffer& src) : m_write(src.m_write), m_read(src.m_read) { memcpy(m_buffer, src.m_buffer, max_capacity * sizeof(T)); } FixedRingBuffer& operator = (const FixedRingBuffer& src) { *this = src; return *this; } FixedRingBuffer(FixedRingBuffer&& src) = delete; FixedRingBuffer& operator = (FixedRingBuffer&&) = delete; size_t size() const { return m_write - m_read; } size_t capacity() const { return max_capacity; } bool empty() const { return m_write == m_read; } bool full() const { return size() == capacity(); } void clear() { m_write = m_read = 0; } bool try_push(const T& value) { uint32_t target_idx; { SpinlockGuard guard(m_lock); if (full()) return false; target_idx = m_write; m_buffer[index(target_idx)] = value; std::atomic_thread_fence(std::memory_order_release); m_write = target_idx + 1; } return true; } bool try_push(T&& value) { uint32_t target_idx; { SpinlockGuard guard(m_lock); if (full()) return false; target_idx = m_write; m_buffer[index(target_idx)] = std::forward(value); std::atomic_thread_fence(std::memory_order_release); m_write = target_idx + 1; } return true; } bool try_pop(T& value) { uint32_t target_idx; { SpinlockGuard guard(m_lock); if (empty()) return false; target_idx = m_read; value = std::move(m_buffer[index(target_idx)]); m_read = target_idx + 1; assert(m_read <= m_write); } return true; } void push(const T& value) { while (!try_push(value)) { std::this_thread::yield(); } } void push(T& value) { while (!try_push(value)) { std::this_thread::yield(); } } T pop() { T result; while (!try_pop(result)) { std::this_thread::yield(); } return result; } }; }