21a17620e3
Previously it just limited the returned write space, irrespective of how much had already been written. The buffer could still be filled up by doing multiple writes. Now the size is limited by adjusting the read pointer by the real vs limited difference when calculating the writable space.
95 lines
3.0 KiB
C++
95 lines
3.0 KiB
C++
#ifndef RINGBUFFER_H
|
|
#define RINGBUFFER_H
|
|
|
|
#include <stddef.h>
|
|
|
|
#include <atomic>
|
|
#include <memory>
|
|
#include <utility>
|
|
|
|
#include "almalloc.h"
|
|
|
|
|
|
/* NOTE: This lockless ringbuffer implementation is copied from JACK, extended
|
|
* to include an element size. Consequently, parameters and return values for a
|
|
* size or count is in 'elements', not bytes. Additionally, it only supports
|
|
* single-consumer/single-provider operation.
|
|
*/
|
|
|
|
struct ll_ringbuffer_data {
|
|
char *buf;
|
|
size_t len;
|
|
};
|
|
using ll_ringbuffer_data_pair = std::pair<ll_ringbuffer_data,ll_ringbuffer_data>;
|
|
|
|
|
|
struct RingBuffer {
|
|
std::atomic<size_t> mWritePtr{0u};
|
|
std::atomic<size_t> mReadPtr{0u};
|
|
size_t mWriteSize{0u};
|
|
size_t mSizeMask{0u};
|
|
size_t mElemSize{0u};
|
|
|
|
alignas(16) char mBuffer[];
|
|
|
|
/** Reset the read and write pointers to zero. This is not thread safe. */
|
|
void reset() noexcept;
|
|
|
|
/**
|
|
* The non-copying data reader. Returns two ringbuffer data pointers that
|
|
* hold the current readable data. If the readable data is in one segment
|
|
* the second segment has zero length.
|
|
*/
|
|
ll_ringbuffer_data_pair getReadVector() const noexcept;
|
|
/**
|
|
* The non-copying data writer. Returns two ringbuffer data pointers that
|
|
* hold the current writeable data. If the writeable data is in one segment
|
|
* the second segment has zero length.
|
|
*/
|
|
ll_ringbuffer_data_pair getWriteVector() const noexcept;
|
|
|
|
/**
|
|
* Return the number of elements available for reading. This is the number
|
|
* of elements in front of the read pointer and behind the write pointer.
|
|
*/
|
|
size_t readSpace() const noexcept;
|
|
/**
|
|
* The copying data reader. Copy at most `cnt' elements into `dest'.
|
|
* Returns the actual number of elements copied.
|
|
*/
|
|
size_t read(void *dest, size_t cnt) noexcept;
|
|
/**
|
|
* The copying data reader w/o read pointer advance. Copy at most `cnt'
|
|
* elements into `dest'. Returns the actual number of elements copied.
|
|
*/
|
|
size_t peek(void *dest, size_t cnt) const noexcept;
|
|
/** Advance the read pointer `cnt' places. */
|
|
void readAdvance(size_t cnt) noexcept;
|
|
|
|
/**
|
|
* Return the number of elements available for writing. This is the number
|
|
* of elements in front of the write pointer and behind the read pointer.
|
|
*/
|
|
size_t writeSpace() const noexcept;
|
|
/**
|
|
* The copying data writer. Copy at most `cnt' elements from `src'. Returns
|
|
* the actual number of elements copied.
|
|
*/
|
|
size_t write(const void *src, size_t cnt) noexcept;
|
|
/** Advance the write pointer `cnt' places. */
|
|
void writeAdvance(size_t cnt) noexcept;
|
|
|
|
DEF_PLACE_NEWDEL()
|
|
};
|
|
using RingBufferPtr = std::unique_ptr<RingBuffer>;
|
|
|
|
|
|
/**
|
|
* Create a new ringbuffer to hold at least `sz' elements of `elem_sz' bytes.
|
|
* The number of elements is rounded up to the next power of two (even if it is
|
|
* already a power of two, to ensure the requested amount can be written).
|
|
*/
|
|
RingBufferPtr CreateRingBuffer(size_t sz, size_t elem_sz, int limit_writes);
|
|
|
|
#endif /* RINGBUFFER_H */
|