You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

156 lines
2.4 KiB

#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;
}
};
}