Mercurial > hg > svcore
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; |