Program Listing for File ringbuffer.hpp
↰ Return to documentation for file (include/libcaercpp/ringbuffer.hpp
)
#ifndef LIBCAER_RINGBUFFER_HPP_
#define LIBCAER_RINGBUFFER_HPP_
#include <atomic>
#include <cstdint>
#include <stdexcept>
#include <vector>
// Alignment specification support (with defines for cache line alignment).
#if !defined(CACHELINE_SIZE)
# define CACHELINE_SIZE 128 // Default (big enough for most processors), must be power of two!
#endif
namespace libcaer {
namespace ringbuffer {
template<typename T>
class RingBuffer {
private:
alignas(CACHELINE_SIZE) size_t putPos;
alignas(CACHELINE_SIZE) size_t getPos;
alignas(CACHELINE_SIZE) std::vector<std::atomic<T>> elements;
const size_t sizeAdj;
const T placeholder;
public:
RingBuffer(size_t sz) : putPos(0), getPos(0), elements(sz), sizeAdj(sz - 1), placeholder() {
// Force multiple of two size for performance.
if ((sz == 0) || ((sz & sizeAdj) != 0)) {
throw std::invalid_argument("Size must be a power of two.");
}
std::atomic_thread_fence(std::memory_order_release);
}
bool operator==(const RingBuffer &rhs) const noexcept {
return (this == &rhs);
}
bool operator!=(const RingBuffer &rhs) const noexcept {
return (!operator==(rhs));
}
void put(const T &elem) {
if (elem == placeholder) {
// Default constructed elements are disallowed (used as place-holders).
throw std::invalid_argument("Default constructed elements are not allowed in the ringbuffer.");
}
const T curr = elements[putPos].load(std::memory_order_acquire);
// If the place where we want to put the new element is NULL, it's still
// free and we can use it.
if (curr == placeholder) {
elements[putPos].store(elem, std::memory_order_release);
// Increase local put pointer.
putPos = ((putPos + 1) & sizeAdj);
return;
}
// Else, buffer is full.
throw std::out_of_range("Ringbuffer full.");
}
bool full() const noexcept {
const T curr = elements[putPos].load(std::memory_order_acquire);
// If the place where we want to put the new element is NULL, it's still
// free and thus the buffer still has available space.
if (curr == placeholder) {
return (false);
}
// Else, buffer is full.
return (true);
}
T get() {
T curr = elements[getPos].load(std::memory_order_acquire);
// If the place where we want to get an element from is not NULL, there
// is valid content there, which we return, and reset the place to NULL.
if (curr != placeholder) {
elements[getPos].store(placeholder, std::memory_order_release);
// Increase local get pointer.
getPos = ((getPos + 1) & sizeAdj);
return (curr);
}
// Else, buffer is empty.
throw std::out_of_range("Ringbuffer empty.");
}
T look() const {
T curr = elements[getPos].load(std::memory_order_acquire);
// If the place where we want to get an element from is not NULL, there
// is valid content there, which we return, without removing it from the
// ring buffer.
if (curr != placeholder) {
return (curr);
}
// Else, buffer is empty.
throw std::out_of_range("Ringbuffer empty.");
}
bool empty() const noexcept {
const T curr = elements[getPos].load(std::memory_order_acquire);
// If the place where we want to get an element from is not NULL, there
// is valid content there, which we return, without removing it from the
// ring buffer.
if (curr != placeholder) {
return (false);
}
// Else, buffer is empty.
return (true);
}
};
} // namespace ringbuffer
} // namespace libcaer
#endif /* LIBCAER_RINGBUFFER_HPP_ */