1 //========================================================= 2 // MusE 3 // Linux Music Editor 4 // 5 // lock_free_buffer.h 6 // (C) Copyright 1999-2002 Werner Schweer (ws@seh.de) 7 // (C) Copyright 2012, 2017 Tim E. Real (terminator356 on users dot sourceforge dot net) 8 // 9 // This program is free software; you can redistribute it and/or 10 // modify it under the terms of the GNU General Public License 11 // as published by the Free Software Foundation; version 2 of 12 // the License, or (at your option) any later version. 13 // 14 // This program is distributed in the hope that it will be useful, 15 // but WITHOUT ANY WARRANTY; without even the implied warranty of 16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 // GNU General Public License for more details. 18 // 19 // You should have received a copy of the GNU General Public License 20 // along with this program; if not, write to the Free Software 21 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 22 // 23 //========================================================= 24 25 #ifndef __LOCK_FREE_BUFFER_H__ 26 #define __LOCK_FREE_BUFFER_H__ 27 28 //#include <map> 29 #include <atomic> 30 31 namespace MusECore { 32 33 34 //--------------------------------------------------------- 35 // LockFreeBuffer 36 //--------------------------------------------------------- 37 38 template <class T> 39 40 class LockFreeBuffer 41 { 42 int _capacity; 43 int _id; // Optional ID value. 44 T *_fifo; 45 volatile int _size; 46 int _wIndex; 47 int _rIndex; 48 int _sizeSnapshot; 49 T _dummyRetValue; 50 51 public: 52 // Start simple with just 2, like a flipping buffer for example. 53 LockFreeBuffer(int capacity = 2, int id = 0) _capacity(capacity)54 : _capacity(capacity), _id(id) 55 { 56 _dummyRetValue = T(); 57 _fifo = new T[_capacity]; 58 clear(); 59 } 60 ~LockFreeBuffer()61 ~LockFreeBuffer() 62 { 63 if(_fifo) 64 delete[] _fifo; 65 } 66 id()67 int id() const { return _id; } 68 69 void setCapacity(int capacity = 2) 70 { 71 if(_fifo) 72 delete _fifo; 73 _fifo = 0; 74 _capacity = capacity; 75 _fifo = new T[_capacity]; 76 } 77 78 // This is only for the writer. 79 // Returns true on fifo overflow put(const T & item)80 bool put(const T& item) 81 { 82 if (_size < _capacity) 83 { 84 _fifo[_wIndex] = item; 85 _wIndex = (_wIndex + 1) % _capacity; 86 // q_atomic_increment(&_size); 87 ++_size; 88 return false; 89 } 90 return true; 91 } 92 93 // This is only for the reader. get()94 T get() 95 { 96 if(_size <= 0) 97 return _dummyRetValue; 98 T item(_fifo[_rIndex]); 99 _rIndex = (_rIndex + 1) % _capacity; 100 --_size; 101 return item; 102 } 103 104 // This is only for the reader. 105 const T& peek(int n = 0) 106 { 107 const int idx = (_rIndex + n) % _capacity; 108 return _fifo[idx]; 109 } 110 111 // // This is only for the reader. 112 // // A non-constant version of peek so that we can modify the items in-place. 113 // T& peekNonConst(int n = 0) 114 // { 115 // const int idx = (_rIndex + n) % _capacity; 116 // return _fifo[idx]; 117 // } 118 119 // This is only for the reader. 120 // Returns true if error (nothing to remove). remove()121 bool remove() 122 { 123 if(_size <= 0) 124 return true; 125 _rIndex = (_rIndex + 1) % _capacity; 126 --_size; 127 return false; 128 } 129 130 // This is only for the reader. 131 // Returns the number of items in the buffer. 132 // If NOT requesting the size snapshot, this conveniently stores a snapshot (cached) version 133 // of the size for consistent behaviour later. If requesting the size snapshot, it does not 134 // update the snapshot itself. getSize(bool useSizeSnapshot)135 int getSize(bool useSizeSnapshot/* = false*/) 136 { 137 const int sz = useSizeSnapshot ? _sizeSnapshot : _size; 138 if(!useSizeSnapshot) 139 _sizeSnapshot = sz; 140 return sz; 141 } 142 // This is only for the reader. isEmpty(bool useSizeSnapshot)143 bool isEmpty(bool useSizeSnapshot/* = false*/) const { return useSizeSnapshot ? _sizeSnapshot == 0 : _size == 0; } 144 // This is not thread safe, call it only when it is safe to do so. clear()145 void clear() { _size = 0; _sizeSnapshot = 0; _wIndex = 0; _rIndex = 0; } 146 // Clear the 'read' side of the ring buffer, which also clears the size. 147 // NOTE: A corresponding clearWrite() is not provided because 148 // it is dangerous to reset the size from the sender side - 149 // the receiver might cache the size, briefly. The sender should 150 // only grow the size while the receiver should only shrink it. clearRead()151 void clearRead() { _size = 0; _sizeSnapshot = 0; _rIndex = _wIndex; } 152 }; 153 154 // // template <class T> 155 // // class LockFreeMultiBuffer 156 // // { 157 // // int _listCapacity; 158 // // LockFreeBuffer<T> *_list; 159 // // //volatile int _size; 160 // // //int _wIndex; 161 // // //int _rIndex; 162 // // 163 // // public: 164 // // // Start simple with just 2, like a flipping buffer for example. 165 // // LockFreeMultiBuffer(int listCapacity = 1) 166 // // { 167 // // _listCapacity = listCapacity; 168 // // _list = new LockFreeBuffer<T>[_listCapacity]; 169 // // //clear(); 170 // // } 171 // // ~LockFreeMultiBuffer() 172 // // { 173 // // if(_list) 174 // // delete[] _list; 175 // // } 176 // // 177 // // void setListCapacity(int listCapacity = 1) 178 // // { 179 // // if(_list) 180 // // delete _list; 181 // // _list = 0; 182 // // _listCapacity = listCapacity; 183 // // _list = new LockFreeBuffer<T>[_listCapacity]; 184 // // } 185 // // 186 // // }; 187 188 // template <class T> 189 // class LockFreeMultiBuffer : public std::map<int, LockFreeBuffer<T>*, std::less<int> > 190 // { 191 // public: 192 // typedef typename std::map<int, LockFreeBuffer<T>*, std::less<int> > vlist; 193 // typedef typename vlist::iterator iLockFreeMultiBuffer; 194 // typedef typename vlist::const_iterator ciLockFreeMultiBuffer; 195 // 196 // private: 197 // // int _curId; 198 // T _dummyRetValue; 199 // 200 // public: 201 // //LockFreeMultiBuffer() : _curId(0) { } 202 // LockFreeMultiBuffer() { _dummyRetValue = T(); } 203 // ~LockFreeMultiBuffer() 204 // { 205 // for(iLockFreeMultiBuffer i = vlist::begin(); i != vlist::end(); ++i) 206 // { 207 // if(i->second) 208 // delete i->second; 209 // } 210 // } 211 // 212 // // Returns new buffer or zero if duplicate id or other error. 213 // // Start simple with just 2, like a flipping buffer for example. 214 // LockFreeBuffer<T>* createBuffer(int id, int capacity = 2) 215 // { 216 // LockFreeBuffer<T>* buf = new LockFreeBuffer<T>(capacity, id); 217 // std::pair < iLockFreeMultiBuffer, bool > res = 218 // vlist::insert(std::pair < const int, LockFreeBuffer<T>* >(buf->id(), buf)); 219 // // if(res.second) 220 // // { 221 // // const int c_id = _curId; 222 // // ++_curId; 223 // // return c_id; 224 // // } 225 // // return -1; 226 // 227 // if(res.second) 228 // return buf; 229 // 230 // delete buf; 231 // return 0; 232 // } 233 // 234 // // Returns true on error. 235 // bool deleteBuffer(int id) 236 // { 237 // //if(id < 0) 238 // // return true; 239 // iLockFreeMultiBuffer i = vlist::find(id); 240 // if(i == vlist::end()) 241 // return true; 242 // if(i->second) 243 // delete i->second; 244 // vlist::erase(i); 245 // return false; 246 // } 247 // 248 // LockFreeBuffer<T>* findBuffer(int id) 249 // { 250 // //if(id < 0) 251 // // return 0; 252 // iLockFreeMultiBuffer i = vlist::find(id); 253 // if(i == vlist::end()) 254 // return 0; 255 // return i->second; 256 // } 257 // 258 // // Returns true on invalid id. 259 // bool setCapacity(int id, int capacity = 2) 260 // { 261 // //if(id < 0) 262 // // return true; 263 // iLockFreeMultiBuffer i = vlist::find(id); 264 // if(i == vlist::end()) 265 // return true; 266 // i->second->setCapacity(capacity); 267 // return false; 268 // } 269 // 270 // // This is only for the writer. 271 // // Returns true on invalid id, or on fifo overflow of that id's buffer. 272 // bool put(int id, const T& item) 273 // { 274 // //if(id < 0) 275 // // return true; 276 // iLockFreeMultiBuffer i = vlist::find(id); 277 // if(i == vlist::end()) 278 // return true; 279 // return i->second->put(item); 280 // } 281 // 282 // // This is only for the reader. 283 // T get(bool useSizeSnapshot/* = false*/) 284 // { 285 // iLockFreeMultiBuffer least_i = vlist::end(); 286 // bool is_first = true; 287 // for(iLockFreeMultiBuffer i = vlist::begin(); i != vlist::end(); ++i) 288 // { 289 // LockFreeBuffer<T>* buf = i->second; 290 // if(!buf || buf->isEmpty(useSizeSnapshot)) 291 // continue; 292 // const T& temp_val = buf->peek(); 293 // if(is_first) 294 // { 295 // is_first = false; 296 // least_i = i; 297 // //least_t = temp_val; 298 // continue; 299 // } 300 // else if(temp_val < least_i->second->peek()) 301 // least_i = i; 302 // } 303 // 304 // if(least_i != vlist::end()) 305 // return least_i->second->get(); 306 // 307 // return _dummyRetValue; 308 // } 309 // 310 // // This is only for the reader. 311 // const T& peek(bool useSizeSnapshot/* = false*/, int n = 0) // const 312 // { 313 // iLockFreeMultiBuffer least_i = vlist::end(); 314 // bool is_first = true; 315 // int buf_sz; 316 // for(int idx = 0; idx <= n; ++idx) // Yes, that's <= 317 // { 318 // for(iLockFreeMultiBuffer i = vlist::begin(); i != vlist::end(); ++i) 319 // { 320 // LockFreeBuffer<T>* buf = i->second; 321 // if(!buf) 322 // continue; 323 // buf_sz = buf->getSize(useSizeSnapshot); 324 // if(buf_sz == 0 || n >= buf_sz) 325 // continue; 326 // const T& temp_val = buf->peek(); 327 // if(is_first) 328 // { 329 // is_first = false; 330 // least_i = i; 331 // 332 // //if(idx == n) 333 // // break; 334 // //++idx; 335 // continue; 336 // } 337 // else if(temp_val < least_i->second->peek()) 338 // { 339 // least_i = i; 340 // 341 // //if(idx == n) 342 // // break; 343 // //++idx; 344 // } 345 // } 346 // if(idx == n) 347 // break; 348 // ++idx; 349 // } 350 // 351 // if(least_i != vlist::end()) 352 // return least_i->second->peek(); 353 // 354 // return _dummyRetValue; 355 // } 356 // 357 // // // This is only for the reader. 358 // // // A non-constant version of peek so that we can modify the items in-place. 359 // // T& peekNonConst(bool useSizeSnapshot/* = false*/, int n = 0) // const 360 // // { 361 // // iLockFreeMultiBuffer least_i = vlist::end(); 362 // // bool is_first = true; 363 // // int buf_sz; 364 // // for(int idx = 0; idx <= n; ++idx) // Yes, that's <= 365 // // { 366 // // for(iLockFreeMultiBuffer i = vlist::begin(); i != vlist::end(); ++i) 367 // // { 368 // // LockFreeBuffer<T>* buf = i->second; 369 // // if(!buf) 370 // // continue; 371 // // buf_sz = buf->getSize(useSizeSnapshot); 372 // // if(buf_sz == 0 || n >= buf_sz) 373 // // continue; 374 // // T& temp_val = buf->peekNonConst(); 375 // // if(is_first) 376 // // { 377 // // is_first = false; 378 // // least_i = i; 379 // // 380 // // //if(idx == n) 381 // // // break; 382 // // //++idx; 383 // // continue; 384 // // } 385 // // else if(temp_val < least_i->second->peekNonConst()) 386 // // { 387 // // least_i = i; 388 // // 389 // // //if(idx == n) 390 // // // break; 391 // // //++idx; 392 // // } 393 // // } 394 // // if(idx == n) 395 // // break; 396 // // ++idx; 397 // // } 398 // // 399 // // if(least_i != vlist::end()) 400 // // return least_i->second->peekNonConst(); 401 // // 402 // // return _dummyRetValue; 403 // // } 404 // 405 // // This is only for the reader. 406 // // Returns true if error (nothing to remove). 407 // bool remove(bool useSizeSnapshot/* = false*/) 408 // { 409 // iLockFreeMultiBuffer least_i = vlist::end(); 410 // bool is_first = true; 411 // for(iLockFreeMultiBuffer i = vlist::begin(); i != vlist::end(); ++i) 412 // { 413 // LockFreeBuffer<T>* buf = i->second; 414 // if(!buf || buf->isEmpty(useSizeSnapshot)) 415 // continue; 416 // const T& temp_val = buf->peek(); 417 // if(is_first) 418 // { 419 // is_first = false; 420 // least_i = i; 421 // continue; 422 // } 423 // else if(temp_val < least_i->second->peek()) 424 // least_i = i; 425 // } 426 // 427 // if(least_i != vlist::end()) 428 // return least_i->second->remove(); 429 // 430 // return true; 431 // } 432 // 433 // // This is only for the reader. 434 // // Returns the total number of items in the buffers. 435 // // Also conveniently stores a cached version of the size for consistent behaviour later. 436 // int getSize(bool useSizeSnapshot/* = false*/) const 437 // { 438 // int sz = 0; 439 // // Hm, maybe not so accurate, sizes may be susceptable to 440 // // asynchronous change as we iterate here... 441 // for(ciLockFreeMultiBuffer i = vlist::begin(); i != vlist::end(); ++i) 442 // { 443 // if(LockFreeBuffer<T>* buf = i->second) 444 // sz += buf->getSize(useSizeSnapshot); 445 // } 446 // return sz; 447 // } 448 // 449 // // This is only for the reader. 450 // bool isEmpty(bool useSizeSnapshot/* = false*/) const 451 // { 452 // // Hm, maybe not so accurate, sizes may be susceptable to 453 // // asynchronous change as we iterate here... 454 // for(ciLockFreeMultiBuffer i = vlist::begin(); i != vlist::end(); ++i) 455 // { 456 // if(const LockFreeBuffer<T>* buf = i->second) 457 // { 458 // if(!buf->isEmpty(useSizeSnapshot)) 459 // return false; 460 // } 461 // } 462 // return true; 463 // } 464 // 465 // // This is not thread safe, call it only when it is safe to do so. 466 // void clear() 467 // { 468 // for(iLockFreeMultiBuffer i = vlist::begin(); i != vlist::end(); ++i) 469 // { 470 // if(LockFreeBuffer<T>* buf = i->second) 471 // buf->clear(); 472 // } 473 // } 474 // 475 // // Clear the 'read' side of the ring buffer, which also clears the size. 476 // // NOTE: A corresponding clearWrite() is not provided because 477 // // it is dangerous to reset the size from the sender side - 478 // // the receiver might cache the size, briefly. The sender should 479 // // only grow the size while the receiver should only shrink it. 480 // void clearRead() 481 // { 482 // for(iLockFreeMultiBuffer i = vlist::begin(); i != vlist::end(); ++i) 483 // { 484 // if(LockFreeBuffer<T>* buf = i->second) 485 // buf->clearRead(); 486 // } 487 // } 488 // }; 489 490 //--------------------------------------------------------- 491 // LockFreeMPSCBuffer 492 // A lock-free Multi-Producer Single-Consumer buffer. 493 // Similar to a FIFO or Ring Buffer, but uses a fixed number of 'bins'. 494 // There are no position counters or modulo operations. 495 // There is no size or peek method. 496 // It is intended to be fully consumed at reading time. 497 //--------------------------------------------------------- 498 499 template <class T, unsigned int capacity> 500 class LockFreeMPSCBuffer 501 { 502 T _array[capacity]; 503 std::atomic<bool> _inUse[capacity]; 504 std::atomic<bool> _hasData[capacity]; 505 506 public: LockFreeMPSCBuffer()507 LockFreeMPSCBuffer() { clear(); } 508 509 // Returns the buffer capacity. bufferCapacity()510 unsigned int bufferCapacity() const { return capacity; } 511 512 // This is only for the writer. 513 // Returns true on success, false if buffer overflow. put(const T & item)514 bool put(const T& item) 515 { 516 bool expected; 517 for(unsigned int i = 0; i < capacity; ++i) 518 { 519 // Expecting a not-in-use bin. Must reset expected each time. 520 expected = false; 521 // Safely check and set the bin's inUse flag. 522 if(_inUse[i].compare_exchange_strong(expected, true)) 523 { 524 // Bin was not in use, now safely marked as in use. Now set the item. 525 _array[i] = item; 526 // Safely set the hasData flag for the reader to examine. 527 _hasData[i].store(true); 528 // Success. 529 return true; 530 } 531 } 532 // Sorry, all bins were full. A buffer overflow condition. 533 return false; 534 } 535 536 // This is only for the reader. 537 // Returns true on success, false if there was no data 538 // available at the bin index or other error. get(T & dst,unsigned int index)539 bool get(T& dst, unsigned int index) 540 { 541 if(index >= capacity) 542 return false; 543 544 // Expecting hasData true. 545 bool expected = true; 546 // Safely check if there is data in the bin, and reset the 547 // bin's hasData and inUse flags. Clear the hasData flag first !!! 548 if(_hasData[index].compare_exchange_strong(expected, false)) 549 { 550 // It is safe to store the value in the destination. 551 dst = _array[index]; 552 // Now clear the inUse flag !!! 553 _inUse[index].store(false); 554 // Success. 555 return true; 556 } 557 // Sorry, there was no data available in that bin. 558 return false; 559 } 560 561 // This is only for the reader. 562 // Returns true on success. remove(unsigned int index)563 bool remove(unsigned int index) 564 { 565 if(index >= capacity) 566 return false; 567 568 // Expecting hasData true. 569 bool expected = true; 570 // Safely check and reset the bin's hasData and inUse flags. 571 if(_hasData[index].compare_exchange_strong(expected, false)) 572 { 573 _inUse[index].store(false); 574 // Success. 575 return true; 576 } 577 // Sorry, there was no data available in that bin. 578 return false; 579 } 580 581 // Not thread safe. Only call when safe to do so, 582 // like constructor etc. clear()583 void clear() 584 { 585 for(unsigned int i = 0; i < capacity; ++i) 586 { 587 // Clear the hasData flag first !!! 588 _hasData[i].store(false); 589 // Now clear the inUse flag !!! 590 _inUse[i].store(false); 591 } 592 } 593 594 // This is only for the reader. clearRead()595 void clearRead() 596 { 597 bool expected; 598 for(unsigned int i = 0; i < capacity; ++i) 599 { 600 // Expecting hasData true. Must reset expected each time. 601 expected = true; 602 // Safely check and reset the bin's hasData and inUse flags. 603 if(_hasData[i].compare_exchange_strong(expected, false)) 604 _inUse[i].store(false); 605 } 606 } 607 }; 608 609 //--------------------------------------------------------- 610 // LockFreeMPSCRingBuffer 611 //--------------------------------------------------------- 612 613 template <class T> 614 615 class LockFreeMPSCRingBuffer 616 { 617 unsigned int _capacity; 618 T *_fifo; 619 std::atomic<unsigned int> _size; 620 std::atomic<unsigned int> _wIndex; 621 std::atomic<unsigned int> _rIndex; 622 unsigned int _capacityMask; 623 unsigned int _sizeSnapshot; 624 625 // Rounds to the nearest or equal power of 2. 626 // For 0, 1, and 2, always returns 2. roundCapacity(unsigned int reqCap)627 unsigned int roundCapacity(unsigned int reqCap) const 628 { 629 unsigned int i; 630 for(i = 1; (1U << i) < reqCap; i++); 631 return 1U << i; 632 } 633 634 public: 635 // Start simple with just 2, like a flipping buffer for example. 636 LockFreeMPSCRingBuffer(unsigned int capacity = 2) 637 { 638 _capacity = roundCapacity(capacity); 639 _capacityMask = _capacity - 1; 640 _fifo = new T[_capacity]; 641 clear(); 642 } 643 ~LockFreeMPSCRingBuffer()644 ~LockFreeMPSCRingBuffer() 645 { 646 if(_fifo) 647 delete[] _fifo; 648 } 649 650 void setCapacity(unsigned int capacity = 2) 651 { 652 if(_fifo) 653 delete[] _fifo; 654 _fifo = 0; 655 _capacity = roundCapacity(capacity); 656 _capacityMask = _capacity - 1; 657 _fifo = new T[_capacity]; 658 } 659 660 // This is only for the writer. 661 // Returns true on success, false on fifo overflow or other error. put(const T & item)662 bool put(const T& item) 663 { 664 // Buffer full? Overflow condition. 665 if(_size.load() >= _capacity) 666 return false; 667 668 // Safely read, then increment, the current write position. 669 //std::atomic<unsigned int> pos = _wIndex++; 670 unsigned int pos = _wIndex++; 671 // Mask the position for a circular effect. 672 pos &= _capacityMask; 673 // Store the item in that position. 674 _fifo[pos] = item; 675 // Now safely increment the size. 676 _size++; 677 // Success. 678 return true; 679 } 680 681 // This is only for the reader. 682 // Returns true on success, false if nothing to read or other error. 683 // NOTE: This is not multi-reader safe. Yet. get(T & dst)684 bool get(T& dst) 685 { 686 // Nothing to read? 687 if(_size.load() == 0) 688 return false; 689 690 // Safely read, then increment, the current read position. 691 //std::atomic<unsigned int> pos = _rIndex++; 692 unsigned int pos = _rIndex++; 693 // Mask the position for a circular effect. 694 pos &= _capacityMask; 695 // Store the item in that position into the destination. 696 dst = _fifo[pos]; 697 // Now safely decrement the size. 698 _size--; 699 // Success. 700 return true; 701 } 702 703 // This is only for the reader. 704 // NOTE: This is not multi-reader safe. Yet. 705 const T& peek(unsigned int n = 0) 706 { 707 // Safely read the current read position. 708 //std::atomic<unsigned int> pos = _rIndex.load(); 709 unsigned int pos = _rIndex.load(); 710 // Add the desired position. 711 pos += n; 712 // Mask the position for a circular effect. 713 pos &= _capacityMask; 714 return _fifo[pos]; 715 } 716 717 // // This is only for the reader. 718 // // A non-constant version of peek so that we can modify the items in-place. 719 // T& peekNonConst(int n = 0) 720 // { 721 // const int idx = (_rIndex + n) % _capacity; 722 // return _fifo[idx]; 723 // } 724 725 // This is only for the reader. 726 // Returns true on success or false if nothing to remove or other error. remove()727 bool remove() 728 { 729 // Nothing to read? 730 if(_size.load() == 0) 731 return false; 732 733 // Safely increment the current read position. 734 _rIndex++; 735 // Now safely decrement the size. 736 _size--; 737 // Success. 738 return true; 739 } 740 741 // This is only for the reader. 742 // Returns the number of items in the buffer. 743 // If NOT requesting the size snapshot, this conveniently stores a snapshot (cached) version 744 // of the size for consistent behaviour later. If requesting the size snapshot, it does not 745 // update the snapshot itself. getSize(bool useSizeSnapshot)746 unsigned int getSize(bool useSizeSnapshot/* = false*/) 747 { 748 const unsigned int sz = useSizeSnapshot ? _sizeSnapshot : _size.load(); 749 if(!useSizeSnapshot) 750 _sizeSnapshot = sz; 751 return sz; 752 } 753 // This is only for the reader. isEmpty(bool useSizeSnapshot)754 bool isEmpty(bool useSizeSnapshot/* = false*/) const { return useSizeSnapshot ? _sizeSnapshot == 0 : _size.load() == 0; } 755 // This is not thread safe, call it only when it is safe to do so. clear()756 void clear() { _size.store(0); _sizeSnapshot = 0; _wIndex.store(0); _rIndex.store(0); } 757 // This is only for the reader. 758 // Clear the 'read' side of the ring buffer, which also clears the size. 759 // NOTE: A corresponding clearWrite() is not provided because it is dangerous to reset 760 // the size from the sender side - the receiver might cache the size, briefly. 761 // The sender should only grow the size while the receiver should only shrink it. 762 //void clearRead() { _size = 0; _sizeSnapshot = 0; _rIndex = _wIndex; } clearRead()763 void clearRead() { _size.store(0); _sizeSnapshot = 0; _rIndex.store(_wIndex); } 764 }; 765 766 } // namespace MusECore 767 768 #endif 769 770