Created
March 1, 2018 23:25
-
-
Save bexp/85a62c6f890a28204704ddc6fc0b07ea to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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