#pragma once
#include <atomic>
#include <memory>
template <typename T> class RingBuffer {
public:
explicit RingBuffer(std::size_t capactiy) : _buffer_capacity(capactiy){
std::unique_ptr <T[], RingBufferFree> buffer_ptr(new T[capactiy]);
_ring_buffer_array = std::move(buffer_ptr);
}
~RingBuffer() {
}
void Push(T val) {
while (!TryPush(val));
}
T Pop() {
T val;
while (!TryPop(&val));
return val;
}
private:
//Private Struct to ensure allocated memory is freed.
struct RingBufferFree{
void operator()(T * ring_buffer_memory) { delete[] ring_buffer_memory; }
};
//Private Member Functions
bool TryPush(T & val) {
const std::size_t current_write = write_position.load(std::memory_order_acquire);
const std::size_t current_read = read_position.load(std::memory_order_acquire);
const std::size_t next_write = increment_index(current_write);
if (next_write == current_read) { return false; }
_ring_buffer_array[current_write] = val;
write_position.store(next_write, std::memory_order_release);
return true;
}
bool TryPop(T * val) {
const std::size_t current_write = write_position.load(std::memory_order_acquire);
const std::size_t current_read = read_position.load(std::memory_order_acquire);
if (current_read == current_write) { return false; }
*val = _ring_buffer_array[current_read];
const std::size_t next_read = increment_index(current_read);
read_position.store(next_read, std::memory_order_release);
return true;
}
std::size_t increment_index(std::size_t index) {
return (index + 1) % _buffer_capacity;
}
//Private Member Variables
std::atomic<std::size_t> read_position = 0;
std::atomic<std::size_t> write_position = 0;
std::size_t _buffer_capacity;
std::unique_ptr<T[], RingBufferFree> _ring_buffer_array;
};
I have tested my code and it appears to work, but I would like some second opinions on what I did. This first time I have tried lock free programming with atomic variables and so I would appreciate it if I had more experienced programmers provide feed back.