ringbuffer: added force() and use lockless methods

this adds force() which can be used for drivers wanting consumers to
get the latest data when the buffer overflows
This commit is contained in:
px4dev 2013-09-09 17:36:07 +10:00 committed by Lorenz Meier
parent 778cfaa0b9
commit f9f41f5770
1 changed files with 120 additions and 16 deletions

View File

@ -55,13 +55,29 @@ public:
bool put(T &val);
/**
* Put an item into the buffer.
* Put an item into the buffer if there is space.
*
* @param val Item to put
* @return true if the item was put, false if the buffer is full
*/
bool put(const T &val);
/**
* Force an item into the buffer, discarding an older item if there is not space.
*
* @param val Item to put
* @return true if an item was discarded to make space
*/
bool force(T &val);
/**
* Force an item into the buffer, discarding an older item if there is not space.
*
* @param val Item to put
* @return true if an item was discarded to make space
*/
bool force(const T &val);
/**
* Get an item from the buffer.
*
@ -73,8 +89,8 @@ public:
/**
* Get an item from the buffer (scalars only).
*
* @return The value that was fetched, or zero if the buffer was
* empty.
* @return The value that was fetched. If the buffer is empty,
* returns zero.
*/
T get(void);
@ -97,23 +113,23 @@ public:
/*
* Returns true if the buffer is empty.
*/
bool empty() { return _tail == _head; }
bool empty();
/*
* Returns true if the buffer is full.
*/
bool full() { return _next(_head) == _tail; }
bool full();
/*
* Returns the capacity of the buffer, or zero if the buffer could
* not be allocated.
*/
unsigned size() { return (_buf != nullptr) ? _size : 0; }
unsigned size();
/*
* Empties the buffer.
*/
void flush() { _head = _tail = _size; }
void flush();
private:
T *const _buf;
@ -139,6 +155,38 @@ RingBuffer<T>::~RingBuffer()
delete[] _buf;
}
template <typename T>
bool RingBuffer<T>::empty()
{
return _tail == _head;
}
template <typename T>
bool RingBuffer<T>::full()
{
return _next(_head) == _tail;
}
template <typename T>
unsigned RingBuffer<T>::size()
{
return (_buf != nullptr) ? _size : 0;
}
template <typename T>
void RingBuffer<T>::flush()
{
T junk;
while (!empty())
get(junk);
}
template <typename T>
unsigned RingBuffer<T>::_next(unsigned index)
{
return (0 == index) ? _size : (index - 1);
}
template <typename T>
bool RingBuffer<T>::put(T &val)
{
@ -165,12 +213,55 @@ bool RingBuffer<T>::put(const T &val)
}
}
template <typename T>
bool RingBuffer<T>::force(T &val)
{
bool overwrote = false;
for (;;) {
if (put(val))
break;
T junk;
get(junk);
overwrote = true;
}
return overwrote;
}
template <typename T>
bool RingBuffer<T>::force(const T &val)
{
bool overwrote = false;
for (;;) {
if (put(val))
break;
T junk;
get(junk);
overwrote = true;
}
return overwrote;
}
template <typename T>
bool RingBuffer<T>::get(T &val)
{
if (_tail != _head) {
val = _buf[_tail];
_tail = _next(_tail);
unsigned candidate;
unsigned next;
do {
/* decide which element we think we're going to read */
candidate = _tail;
/* and what the corresponding next index will be */
next = _next(candidate);
/* go ahead and read from this index */
val = _buf[candidate];
/* if the tail pointer didn't change, we got our item */
} while (!__sync_bool_compare_and_swap(&_tail, candidate, next));
return true;
} else {
return false;
@ -187,17 +278,30 @@ T RingBuffer<T>::get(void)
template <typename T>
unsigned RingBuffer<T>::space(void)
{
return (_tail >= _head) ? (_size - (_tail - _head)) : (_head - _tail - 1);
unsigned tail, head;
/*
* Make a copy of the head/tail pointers in a fashion that
* may err on the side of under-estimating the free space
* in the buffer in the case that the buffer is being updated
* asynchronously with our check.
* If the head pointer changes (reducing space) while copying,
* re-try the copy.
*/
do {
head = _head;
tail = _tail;
} while (head != _head);
return (tail >= head) ? (_size - (tail - head)) : (head - tail - 1);
}
template <typename T>
unsigned RingBuffer<T>::count(void)
{
/*
* Note that due to the conservative nature of space(), this may
* over-estimate the number of items in the buffer.
*/
return _size - space();
}
template <typename T>
unsigned RingBuffer<T>::_next(unsigned index)
{
return (0 == index) ? _size : (index - 1);
}