/usr/include/raul/SRMWQueue.hpp is in libraul-dev 0.8.0+dfsg0-0.1+b1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 | /* This file is part of Raul.
* Copyright (C) 2007-2009 David Robillard <http://drobilla.net>
*
* Raul is free software; you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* Raul is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef RAUL_SRMW_QUEUE_HPP
#define RAUL_SRMW_QUEUE_HPP
#include <cassert>
#include <cstdlib>
#include <cmath>
#include <boost/utility.hpp>
#include "raul/AtomicInt.hpp"
namespace Raul {
/** Realtime-safe single-reader multi-writer queue (aka lock-free ringbuffer)
*
* Implemented as a dequeue in a fixed array. Both push and pop are realtime
* safe, but only push is threadsafe. In other words, multiple threads can push
* data into this queue for a single thread to consume.
*
* The interface is intentionally as similar to std::queue as possible, but
* note the additional thread restrictions imposed (e.g. empty() is only
* legal to call in the read thread).
*
* Obey the threading restrictions documented here, or horrible nasty (possibly
* undetected) errors will occur.
*
* If you only need a single writer, use SRSWQueue. This is slightly more
* computationally expensive, and allocates an additional size words of memory (ie
* if you're using this for ints or pointers etc, SRMWQueue will be twice the size
* of SRSWQueue for the same queue size. Additionally, the size of this queue must
* be a power of 2 (SRSWQueue does not have this limitation).
*
* \ingroup raul
*/
template <typename T>
class SRMWQueue : boost::noncopyable
{
public:
explicit SRMWQueue(size_t size);
~SRMWQueue();
// Any thread:
inline size_t capacity() const { return _size-1; }
// Write thread(s):
inline bool full() const;
inline bool push(const T& obj);
// Read thread:
inline bool empty() const;
inline T& front() const;
inline void pop();
private:
// Note that _front doesn't need to be an AtomicInt since it's only accessed
// by the (single) reader thread
unsigned _front; ///< Circular index of element at front of queue (READER ONLY)
AtomicInt _back; ///< Circular index 1 past element at back of queue (WRITERS ONLY)
AtomicInt _write_space; ///< Remaining free space for new elements (all threads)
const unsigned _size; ///< Size of @ref _objects (you can store _size-1 objects)
T* const _objects; ///< Fixed array containing queued elements
AtomicInt* const _valid; ///< Parallel array to _objects, whether loc is written or not
};
template<typename T>
SRMWQueue<T>::SRMWQueue(size_t size)
: _front(0)
, _back(0)
, _write_space(size)
, _size(size+1)
, _objects((T*)calloc(_size, sizeof(T)))
, _valid((AtomicInt*)calloc(_size, sizeof(AtomicInt)))
{
assert(log2(size) - (int)log2(size) == 0);
assert(size > 1);
assert(_size-1 == (unsigned)_write_space.get());
for (unsigned i=0; i < _size; ++i) {
assert(_valid[i].get() == 0);
}
}
template <typename T>
SRMWQueue<T>::~SRMWQueue()
{
free(_objects);
}
/** Return whether the queue is full.
*
* Write thread(s) only.
*/
template <typename T>
inline bool
SRMWQueue<T>::full() const
{
return (_write_space.get() <= 0);
}
/** Push an item onto the back of the SRMWQueue - realtime-safe, not thread-safe.
*
* Write thread(s) only.
*
* @returns true if @a elem was successfully pushed onto the queue,
* false otherwise (queue is full).
*/
template <typename T>
inline bool
SRMWQueue<T>::push(const T& elem)
{
const int old_write_space = _write_space.exchange_and_add(-1);
const bool already_full = ( old_write_space <= 0 );
/* Technically right here pop could be called in the reader thread and
* make space available, but no harm in failing anyway - this queue
* really isn't designed to be filled... */
if (already_full) {
/* if multiple threads simultaneously get here, _write_space may be 0
* or negative. The next call to pop() will set _write_space back to
* a sane value. Note that _write_space is not exposed, so this is okay
* (... assuming this code is correct) */
return false;
} else {
// Note: _size must be a power of 2 for this to not explode when _back overflows
const unsigned write_index = (unsigned)_back.exchange_and_add(1) % _size;
assert(_valid[write_index] == 0);
_objects[write_index] = elem;
++(_valid[write_index]);
return true;
}
}
/** Return whether the queue is empty.
*
* Read thread only.
*/
template <typename T>
inline bool
SRMWQueue<T>::empty() const
{
return (_valid[_front].get() == 0);
}
/** Return the element at the front of the queue without removing it.
*
* It is a fatal error to call front() when the queue is empty.
* Read thread only.
*/
template <typename T>
inline T&
SRMWQueue<T>::front() const
{
return _objects[_front];
}
/** Pop an item off the front of the queue - realtime-safe, NOT thread-safe.
*
* It is a fatal error to call pop() if the queue is empty.
* Read thread only.
*
* @return true if queue is now empty, otherwise false.
*/
template <typename T>
inline void
SRMWQueue<T>::pop()
{
assert(_valid[_front] == 1);
--(_valid[_front]);
_front = (_front + 1) % (_size);
if (_write_space.get() < 0)
_write_space = 1;
else
++_write_space;
}
} // namespace Raul
#endif // RAUL_SRMW_QUEUE_HPP
|