Skip to content

Instantly share code, notes, and snippets.

@bexp
Created March 1, 2018 23:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bexp/85a62c6f890a28204704ddc6fc0b07ea to your computer and use it in GitHub Desktop.
Save bexp/85a62c6f890a28204704ddc6fc0b07ea to your computer and use it in GitHub Desktop.
/*
* Simple lockfree implementation of single producer/consumer circular FIFO buffer
*/
#ifndef RINGBUFFER_H
#define RINGBUFFER_H
#include <algorithm>
#include <memory.h>
#include <atomic>
namespace Utility
{
template<typename T> class ringbuffer {
public:
//do not allow default constructor
ringbuffer() = delete;
/**
* create a ringbuffer with space for up to size elements.
*/
explicit ringbuffer(size_t size)
: size(size + 1)
, m_begin(0)
, m_end(0) {
buffer = new T[size];
}
/**
* copy constructor
*/
ringbuffer(const ringbuffer<T> & rb) {
this(rb.size);
m_begin = rb.m_begin;
m_end = rb.m_end;
memcpy(buffer, rb.buffer, sizeof(T) * size);
}
~ringbuffer() {
delete[] buffer;
}
size_t write(const T * data, size_t n) noexcept
{
if (getFree() < n || n == 0) {
return 0;
}
auto end = m_end.load(std::memory_order_relaxed);
const size_t first_chunk = std::min(n, size - end);
memcpy(buffer + end, data, first_chunk * sizeof(T));
end = (end + first_chunk) % size;
m_end.store(end, std::memory_order_release);
if (first_chunk < n) {
const size_t second_chunk = n - first_chunk;
memcpy(buffer + end, data + first_chunk, second_chunk * sizeof(T));
end = (end + second_chunk) % size;
m_end.store(end, std::memory_order_release);
}
return n;
}
size_t read(T * dest, size_t n) noexcept {
if (n > getOccupied() || n == 0) {
return 0;
}
auto begin = m_begin.load(std::memory_order_relaxed);
const size_t first_chunk = std::min(n, size - begin);
memcpy(dest, buffer + begin, first_chunk * sizeof(T));
begin = (begin + first_chunk) % size;
m_begin.store(begin, std::memory_order_release);
if (first_chunk < n) {
const size_t second_chunk = n - first_chunk;
memcpy(dest + first_chunk, buffer + begin, second_chunk * sizeof(T));
begin = (begin + second_chunk) % size;
m_begin.store(begin, std::memory_order_release);
}
return n;
}
size_t capacity() const noexcept {
return size - 1;
}
size_t getOccupied() const noexcept {
auto end = m_end.load();
auto begin = m_begin.load();
if (end == begin) {
return 0;
} else if (end > begin) {
return end - begin;
} else {
return size + end - begin;
}
}
size_t getFree() const {
assert(size - getOccupied() - 1 >= 0);
return size - getOccupied() - 1;
}
private:
T * buffer;
const size_t size;
std::atomic<size_t> m_begin;
std::atomic<size_t> m_end;
};
}
#endif // RINGBUFFER_H
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment