#pragma once
|
|
|
|
#include "spinlock.h"
|
|
#include <thread>
|
|
|
|
namespace vlc
|
|
{
|
|
template<typename T, size_t max_capacity = 8> 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<T>(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;
|
|
}
|
|
};
|
|
}
|