comparison base/RingBuffer.h @ 1827:6e218407f0cf audio-source-refactor

Make RingBuffer copyable, and simplify a bit
author Chris Cannam
date Thu, 19 Mar 2020 16:09:04 +0000
parents 66c1988fc906
children 51fd27fbce9a
comparison
equal deleted inserted replaced
1826:a4dce53b3353 1827:6e218407f0cf
24 #include <sys/types.h> 24 #include <sys/types.h>
25 25
26 #include "system/System.h" 26 #include "system/System.h"
27 27
28 #include <bqvec/Barrier.h> 28 #include <bqvec/Barrier.h>
29 29 #include <bqvec/VectorOps.h>
30 #include <cstring> // memcpy, memset &c 30
31 #include <vector>
31 32
32 //#define DEBUG_RINGBUFFER 1 33 //#define DEBUG_RINGBUFFER 1
33 34
34 #ifdef DEBUG_RINGBUFFER 35 #ifdef DEBUG_RINGBUFFER
35 #include <iostream> 36 #include <iostream>
36 #endif 37 #endif
37 38
38 /** 39 /**
39 * RingBuffer implements a lock-free ring buffer for one writer and N 40 * RingBuffer implements a lock-free ring buffer for one writer and N
40 * readers, that is to be used to store a sample type T. 41 * readers, that is to be used to store a sample type T.
41 *
42 * For efficiency, RingBuffer frequently initialises samples by
43 * writing zeroes into their memory space, so T should normally be a
44 * simple type that can safely be set to zero using memset.
45 */ 42 */
46 43
47 template <typename T, int N = 1> 44 template <typename T, int N = 1>
48 class RingBuffer 45 class RingBuffer
49 { 46 {
66 * (This is the argument n passed to the constructor.) 63 * (This is the argument n passed to the constructor.)
67 */ 64 */
68 int getSize() const; 65 int getSize() const;
69 66
70 /** 67 /**
71 * Return a new ring buffer (allocated with "new" -- caller must 68 * Return a new ring buffer of the given size, containing the same
72 * delete when no longer needed) of the given size, containing the 69 * data as this one as perceived by reader 0 of this buffer. If
73 * same data as this one as perceived by reader 0 of this buffer. 70 * another thread reads from or writes to this buffer during the
74 * If another thread reads from or writes to this buffer during 71 * call, the contents of the new buffer may be incomplete or
75 * the call, the contents of the new buffer may be incomplete or
76 * inconsistent. If this buffer's data will not fit in the new 72 * inconsistent. If this buffer's data will not fit in the new
77 * size, the contents are undefined. 73 * size, the contents are undetermined.
78 */ 74 */
79 RingBuffer<T, N> *resized(int newSize) const; 75 RingBuffer<T, N> resized(int newSize) const;
80
81 /**
82 * Lock the ring buffer into physical memory. Returns true
83 * for success.
84 */
85 bool mlock();
86 76
87 /** 77 /**
88 * Reset read and write pointers, thus emptying the buffer. 78 * Reset read and write pointers, thus emptying the buffer.
89 * Should be called from the write thread. 79 * Should be called from the write thread.
90 */ 80 */
162 * space is available, not all zeros may actually be written. 152 * space is available, not all zeros may actually be written.
163 * Returns the number of zeroes actually written. 153 * Returns the number of zeroes actually written.
164 */ 154 */
165 int zero(int n); 155 int zero(int n);
166 156
157 RingBuffer(const RingBuffer &) =default;
158 RingBuffer &operator=(const RingBuffer &) =default;
159
167 protected: 160 protected:
168 T *m_buffer; 161 std::vector<T> m_buffer;
169 bool m_mlocked; 162 int m_writer;
170 int m_writer; 163 std::vector<int> m_readers;
171 int *m_readers; 164 int m_size;
172 int m_size;
173 int m_spare;
174
175 private:
176 RingBuffer(const RingBuffer &); // not provided
177 RingBuffer &operator=(const RingBuffer &); // not provided
178 }; 165 };
179 166
180 template <typename T, int N> 167 template <typename T, int N>
181 RingBuffer<T, N>::RingBuffer(int n) : 168 RingBuffer<T, N>::RingBuffer(int n) :
182 m_buffer(new T[n + 1]), 169 m_buffer(n + 1, T()),
183 m_mlocked(false),
184 m_writer(0), 170 m_writer(0),
185 m_readers(new int[N]), 171 m_readers(N, 0),
186 m_size(n + 1) 172 m_size(n + 1)
187 { 173 {
188 #ifdef DEBUG_RINGBUFFER 174 #ifdef DEBUG_RINGBUFFER
189 std::cerr << "RingBuffer<T," << N << ">[" << this << "]::RingBuffer(" << n << ")" << std::endl; 175 std::cerr << "RingBuffer<T," << N << ">[" << this << "]::RingBuffer(" << n << ")" << std::endl;
190 #endif 176 #endif
191 /*
192 std::cerr << "note: sizeof(RingBuffer<T,N> = " << sizeof(RingBuffer<T,N>) << ")" << std::endl;
193
194 std::cerr << "this = " << this << std::endl;
195 std::cerr << "&m_buffer = " << &m_buffer << std::endl;
196 std::cerr << "&m_mlocked = " << &m_mlocked << std::endl;
197 std::cerr << "&m_writer = " << &m_writer << std::endl;
198 std::cerr << "&m_readers = " << &m_readers << std::endl;
199 std::cerr << "&m_size = " << &m_size << std::endl;
200 */
201
202 for (int i = 0; i < N; ++i) m_readers[i] = 0;
203 } 177 }
204 178
205 template <typename T, int N> 179 template <typename T, int N>
206 RingBuffer<T, N>::~RingBuffer() 180 RingBuffer<T, N>::~RingBuffer()
207 { 181 {
208 #ifdef DEBUG_RINGBUFFER 182 #ifdef DEBUG_RINGBUFFER
209 std::cerr << "RingBuffer<T," << N << ">[" << this << "]::~RingBuffer" << std::endl; 183 std::cerr << "RingBuffer<T," << N << ">[" << this << "]::~RingBuffer" << std::endl;
210 #endif 184 #endif
211
212 delete[] m_readers;
213
214 if (m_mlocked) {
215 MUNLOCK((void *)m_buffer, m_size * sizeof(T));
216 }
217 delete[] m_buffer;
218 } 185 }
219 186
220 template <typename T, int N> 187 template <typename T, int N>
221 int 188 int
222 RingBuffer<T, N>::getSize() const 189 RingBuffer<T, N>::getSize() const
227 194
228 return m_size - 1; 195 return m_size - 1;
229 } 196 }
230 197
231 template <typename T, int N> 198 template <typename T, int N>
232 RingBuffer<T, N> * 199 RingBuffer<T, N>
233 RingBuffer<T, N>::resized(int newSize) const 200 RingBuffer<T, N>::resized(int newSize) const
234 { 201 {
235 #ifdef DEBUG_RINGBUFFER 202 #ifdef DEBUG_RINGBUFFER
236 std::cerr << "RingBuffer<T," << N << ">[" << this << "]::resized(" << newSize << ")" << std::endl; 203 std::cerr << "RingBuffer<T," << N << ">[" << this << "]::resized(" << newSize << ")" << std::endl;
237 #endif 204 #endif
238 205
239 RingBuffer<T, N> *newBuffer = new RingBuffer<T, N>(newSize); 206 RingBuffer<T, N> newBuffer(newSize);
240 207
241 int w = m_writer; 208 int w = m_writer;
242 int r = m_readers[0]; 209 int r = m_readers[0];
243 210
244 while (r != w) { 211 while (r != w) {
249 216
250 return newBuffer; 217 return newBuffer;
251 } 218 }
252 219
253 template <typename T, int N> 220 template <typename T, int N>
254 bool
255 RingBuffer<T, N>::mlock()
256 {
257 if (MLOCK((void *)m_buffer, m_size * sizeof(T))) return false;
258 m_mlocked = true;
259 return true;
260 }
261
262 template <typename T, int N>
263 void 221 void
264 RingBuffer<T, N>::reset() 222 RingBuffer<T, N>::reset()
265 { 223 {
266 #ifdef DEBUG_RINGBUFFER 224 #ifdef DEBUG_RINGBUFFER
267 std::cerr << "RingBuffer<T," << N << ">[" << this << "]::reset" << std::endl; 225 std::cerr << "RingBuffer<T," << N << ">[" << this << "]::reset" << std::endl;
268 #endif 226 #endif
269 227
270 m_writer = 0; 228 m_writer = 0;
271 for (int i = 0; i < N; ++i) m_readers[i] = 0; 229 for (int i = 0; i < N; ++i) {
230 m_readers[i] = 0;
231 }
272 } 232 }
273 233
274 template <typename T, int N> 234 template <typename T, int N>
275 int 235 int
276 RingBuffer<T, N>::getReadSpace(int R) const 236 RingBuffer<T, N>::getReadSpace(int R) const
326 if (n > available) { 286 if (n > available) {
327 #ifdef DEBUG_RINGBUFFER 287 #ifdef DEBUG_RINGBUFFER
328 std::cerr << "WARNING: Only " << available << " samples available" 288 std::cerr << "WARNING: Only " << available << " samples available"
329 << std::endl; 289 << std::endl;
330 #endif 290 #endif
331 memset(destination + available, 0, (n - available) * sizeof(T)); 291 breakfastquay::v_zero(destination + available, n - available);
332 n = available; 292 n = available;
333 } 293 }
334 if (n == 0) return n; 294 if (n == 0) return n;
335 295
336 int here = m_size - m_readers[R]; 296 int here = m_size - m_readers[R];
337 if (here >= n) { 297 if (here >= n) {
338 memcpy(destination, m_buffer + m_readers[R], n * sizeof(T)); 298 breakfastquay::v_copy(destination, m_buffer.data() + m_readers[R], n);
339 } else { 299 } else {
340 memcpy(destination, m_buffer + m_readers[R], here * sizeof(T)); 300 breakfastquay::v_copy(destination, m_buffer.data() + m_readers[R], here);
341 memcpy(destination + here, m_buffer, (n - here) * sizeof(T)); 301 breakfastquay::v_copy(destination + here, m_buffer.data(), n - here);
342 } 302 }
343 303
344 BQ_MBARRIER(); 304 BQ_MBARRIER();
345 m_readers[R] = (m_readers[R] + n) % m_size; 305 m_readers[R] = (m_readers[R] + n) % m_size;
346 306
371 331
372 int here = m_size - m_readers[R]; 332 int here = m_size - m_readers[R];
373 333
374 if (here >= n) { 334 if (here >= n) {
375 for (int i = 0; i < n; ++i) { 335 for (int i = 0; i < n; ++i) {
376 destination[i] += (m_buffer + m_readers[R])[i]; 336 destination[i] += (m_buffer.data() + m_readers[R])[i];
377 } 337 }
378 } else { 338 } else {
379 for (int i = 0; i < here; ++i) { 339 for (int i = 0; i < here; ++i) {
380 destination[i] += (m_buffer + m_readers[R])[i]; 340 destination[i] += (m_buffer.data() + m_readers[R])[i];
381 } 341 }
382 for (int i = 0; i < (n - here); ++i) { 342 for (int i = 0; i < (n - here); ++i) {
383 destination[i + here] += m_buffer[i]; 343 destination[i + here] += m_buffer[i];
384 } 344 }
385 } 345 }
400 if (m_writer == m_readers[R]) { 360 if (m_writer == m_readers[R]) {
401 #ifdef DEBUG_RINGBUFFER 361 #ifdef DEBUG_RINGBUFFER
402 std::cerr << "WARNING: No sample available" 362 std::cerr << "WARNING: No sample available"
403 << std::endl; 363 << std::endl;
404 #endif 364 #endif
405 T t; 365 return T();
406 memset(&t, 0, sizeof(T));
407 return t;
408 } 366 }
409 T value = m_buffer[m_readers[R]]; 367 T value = m_buffer[m_readers[R]];
410 BQ_MBARRIER(); 368 BQ_MBARRIER();
411 if (++m_readers[R] == m_size) m_readers[R] = 0; 369 if (++m_readers[R] == m_size) m_readers[R] = 0;
412 return value; 370 return value;
424 if (n > available) { 382 if (n > available) {
425 #ifdef DEBUG_RINGBUFFER 383 #ifdef DEBUG_RINGBUFFER
426 std::cerr << "WARNING: Only " << available << " samples available" 384 std::cerr << "WARNING: Only " << available << " samples available"
427 << std::endl; 385 << std::endl;
428 #endif 386 #endif
429 memset(destination + available, 0, (n - available) * sizeof(T)); 387 breakfastquay::v_zero(destination + available, n - available);
430 n = available; 388 n = available;
431 } 389 }
432 if (n == 0) return n; 390 if (n == 0) return n;
433 391
434 int here = m_size - m_readers[R]; 392 int here = m_size - m_readers[R];
435 if (here >= n) { 393 if (here >= n) {
436 memcpy(destination, m_buffer + m_readers[R], n * sizeof(T)); 394 breakfastquay::v_copy(destination, m_buffer.data() + m_readers[R], n);
437 } else { 395 } else {
438 memcpy(destination, m_buffer + m_readers[R], here * sizeof(T)); 396 breakfastquay::v_copy(destination, m_buffer.data() + m_readers[R], here);
439 memcpy(destination + here, m_buffer, (n - here) * sizeof(T)); 397 breakfastquay::v_copy(destination + here, m_buffer.data(), n - here);
440 } 398 }
441 399
442 #ifdef DEBUG_RINGBUFFER 400 #ifdef DEBUG_RINGBUFFER
443 std::cerr << "RingBuffer<T," << N << ">[" << this << "]::peek: read " << n << std::endl; 401 std::cerr << "RingBuffer<T," << N << ">[" << this << "]::peek: read " << n << std::endl;
444 #endif 402 #endif
457 if (m_writer == m_readers[R]) { 415 if (m_writer == m_readers[R]) {
458 #ifdef DEBUG_RINGBUFFER 416 #ifdef DEBUG_RINGBUFFER
459 std::cerr << "WARNING: No sample available" 417 std::cerr << "WARNING: No sample available"
460 << std::endl; 418 << std::endl;
461 #endif 419 #endif
462 T t; 420 return T();
463 memset(&t, 0, sizeof(T));
464 return t;
465 } 421 }
466 T value = m_buffer[m_readers[R]]; 422 T value = m_buffer[m_readers[R]];
467 return value; 423 return value;
468 } 424 }
469 425
506 } 462 }
507 if (n == 0) return n; 463 if (n == 0) return n;
508 464
509 int here = m_size - m_writer; 465 int here = m_size - m_writer;
510 if (here >= n) { 466 if (here >= n) {
511 memcpy(m_buffer + m_writer, source, n * sizeof(T)); 467 breakfastquay::v_copy(m_buffer.data() + m_writer, source, n);
512 } else { 468 } else {
513 memcpy(m_buffer + m_writer, source, here * sizeof(T)); 469 breakfastquay::v_copy(m_buffer.data() + m_writer, source, here);
514 memcpy(m_buffer, source + here, (n - here) * sizeof(T)); 470 breakfastquay::v_copy(m_buffer.data(), source + here, n - here);
515 } 471 }
516 472
517 BQ_MBARRIER(); 473 BQ_MBARRIER();
518 m_writer = (m_writer + n) % m_size; 474 m_writer = (m_writer + n) % m_size;
519 475
542 } 498 }
543 if (n == 0) return n; 499 if (n == 0) return n;
544 500
545 int here = m_size - m_writer; 501 int here = m_size - m_writer;
546 if (here >= n) { 502 if (here >= n) {
547 memset(m_buffer + m_writer, 0, n * sizeof(T)); 503 breakfastquay::v_zero(m_buffer.data() + m_writer, n);
548 } else { 504 } else {
549 memset(m_buffer + m_writer, 0, here * sizeof(T)); 505 breakfastquay::v_zero(m_buffer.data() + m_writer, here);
550 memset(m_buffer, 0, (n - here) * sizeof(T)); 506 breakfastquay::v_zero(m_buffer.data(), n - here);
551 } 507 }
552 508
553 BQ_MBARRIER(); 509 BQ_MBARRIER();
554 m_writer = (m_writer + n) % m_size; 510 m_writer = (m_writer + n) % m_size;
555 return n; 511 return n;