00001
00002 #ifndef artdaq_core_Core_ConcurrentQueue_hh
00003 #define artdaq_core_Core_ConcurrentQueue_hh
00004
00005 #include <algorithm>
00006 #include <cstddef>
00007 #include <exception>
00008 #include <limits>
00009 #include <list>
00010
00011 #include <iostream>
00012 #include "trace.h"
00013
00014 #include <chrono>
00015 #include <condition_variable>
00016 #include <mutex>
00017 #include <type_traits>
00018
00019
00020
00021
00022
00023
00024
00025 namespace daqrate {
00026
00027
00028
00029
00030
00031
00032
00033 typedef std::chrono::duration<double> seconds;
00034
00059 namespace detail {
00060 typedef size_t MemoryType;
00061
00062
00063
00064
00065
00066
00067 template <typename T>
00068 class hasMemoryUsed {
00069 typedef char TrueType;
00070 struct FalseType { TrueType _[2]; };
00071
00072 template <MemoryType(T:: *)() const>
00073 struct TestConst;
00074
00075 template <typename C>
00076 static TrueType test(TestConst<&C::memoryUsed> *);
00077 template <typename C>
00078 static FalseType test(...);
00079
00080 public:
00081 static const bool value = (sizeof(test<T>(0)) == sizeof(TrueType));
00082 };
00083
00084 template <typename T>
00085 MemoryType
00086 memoryUsage(const std::pair<T, size_t> & t)
00087 {
00088 MemoryType usage(0UL);
00089 try {
00090 usage = t.first.memoryUsed();
00091 }
00092 catch (...)
00093 {}
00094 return usage;
00095 }
00096
00097 template <typename T>
00098 typename std::enable_if<hasMemoryUsed<T>::value, MemoryType>::type
00099 memoryUsage(const T & t)
00100 {
00101 MemoryType usage(0UL);
00102 try {
00103 usage = t.memoryUsed();
00104 }
00105 catch (...)
00106 {}
00107 return usage;
00108 }
00109
00110 template <typename T>
00111 typename std::enable_if < !hasMemoryUsed<T>::value, MemoryType >::type
00112 memoryUsage(const T & t)
00113 { return sizeof(t); }
00114
00115 }
00116
00117
00118 template <class T>
00119 struct FailIfFull {
00120 typedef bool ReturnType;
00121
00122 typedef T ValueType;
00123 typedef std::list<T> SequenceType;
00124 typedef typename SequenceType::size_type SizeType;
00125
00126 static struct QueueIsFull : public std::exception {
00127 virtual const char * what() const throw() {
00128 return "Cannot add item to a full queue";
00129 }
00130 } queueIsFull;
00131
00132 static void doInsert
00133 (
00134 T const & item,
00135 SequenceType & elements,
00136 SizeType & size,
00137 detail::MemoryType const & itemSize,
00138 detail::MemoryType & used,
00139 std::condition_variable & nonempty
00140 ) {
00141 elements.push_back(item);
00142 ++size;
00143 used += itemSize;
00144 nonempty.notify_one();
00145 }
00146
00147 static ReturnType doEnq
00148 (
00149 T const & item,
00150 SequenceType & elements,
00151 SizeType & size,
00152 SizeType & capacity,
00153 detail::MemoryType & used,
00154 detail::MemoryType & memory,
00155 size_t & elementsDropped,
00156 std::condition_variable & nonempty
00157 ) {
00158 detail::MemoryType itemSize = detail::memoryUsage(item);
00159 if (size >= capacity || used + itemSize > memory) {
00160 ++elementsDropped;
00161 throw queueIsFull;
00162 }
00163 else {
00164 doInsert(item, elements, size, itemSize, used, nonempty);
00165 }
00166 return true;
00167 }
00168 };
00169
00170 template<typename T>
00171 typename FailIfFull<T>::QueueIsFull FailIfFull<T>::queueIsFull {};
00172
00173 template <class T>
00174 struct KeepNewest {
00175 typedef std::pair<T, size_t> ValueType;
00176 typedef std::list<T> SequenceType;
00177 typedef typename SequenceType::size_type SizeType;
00178 typedef SizeType ReturnType;
00179
00180 static void doInsert
00181 (
00182 T const & item,
00183 SequenceType & elements,
00184 SizeType & size,
00185 detail::MemoryType const & itemSize,
00186 detail::MemoryType & used,
00187 std::condition_variable & nonempty
00188 ) {
00189 elements.push_back(item);
00190 ++size;
00191 used += itemSize;
00192 nonempty.notify_one();
00193 }
00194
00195 static ReturnType doEnq
00196 (
00197 T const & item,
00198 SequenceType & elements,
00199 SizeType & size,
00200 SizeType & capacity,
00201 detail::MemoryType & used,
00202 detail::MemoryType & memory,
00203 size_t & elementsDropped,
00204 std::condition_variable & nonempty
00205 ) {
00206 SizeType elementsRemoved(0);
00207 detail::MemoryType itemSize = detail::memoryUsage(item);
00208 while ((size == capacity || used + itemSize > memory) && !elements.empty()) {
00209 SequenceType holder;
00210
00211 holder.splice(holder.begin(), elements, elements.begin());
00212
00213 --size;
00214 used -= detail::memoryUsage(holder.front());
00215 ++elementsRemoved;
00216 }
00217 if (size < capacity && used + itemSize <= memory)
00218
00219 {
00220 doInsert(item, elements, size, itemSize, used, nonempty);
00221 }
00222 else {
00223
00224 ++elementsRemoved;
00225 }
00226 elementsDropped += elementsRemoved;
00227 return elementsRemoved;
00228 }
00229 };
00230
00231
00232 template <class T>
00233 struct RejectNewest {
00234 typedef std::pair<T, size_t> ValueType;
00235 typedef std::list<T> SequenceType;
00236 typedef typename SequenceType::size_type SizeType;
00237 typedef SizeType ReturnType;
00238
00239 static void doInsert
00240 (
00241 T const & item,
00242 SequenceType & elements,
00243 SizeType & size,
00244 detail::MemoryType const & itemSize,
00245 detail::MemoryType & used,
00246 std::condition_variable & nonempty
00247 ) {
00248 elements.push_back(item);
00249 ++size;
00250 used += itemSize;
00251 nonempty.notify_one();
00252 }
00253
00254 static ReturnType doEnq
00255 (
00256 T const & item,
00257 SequenceType & elements,
00258 SizeType & size,
00259 SizeType & capacity,
00260 detail::MemoryType & used,
00261 detail::MemoryType & memory,
00262 size_t & elementsDropped,
00263 std::condition_variable & nonempty
00264 ) {
00265 detail::MemoryType itemSize = detail::memoryUsage(item);
00266 if (size < capacity && used + itemSize <= memory) {
00267 doInsert(item, elements, size, itemSize, used, nonempty);
00268 return 0;
00269 }
00270 ++elementsDropped;
00271 return 1;
00272 }
00273 };
00274
00279 template <class T, class EnqPolicy = FailIfFull<T> >
00280 class ConcurrentQueue {
00281 public:
00282 typedef typename EnqPolicy::ValueType ValueType;
00283 typedef typename EnqPolicy::SequenceType SequenceType;
00284 typedef typename SequenceType::size_type SizeType;
00285
00290 explicit ConcurrentQueue
00291 (
00292 SizeType maxSize = std::numeric_limits<SizeType>::max(),
00293 detail::MemoryType maxMemory = std::numeric_limits<detail::MemoryType>::max()
00294 );
00295
00302 ~ConcurrentQueue();
00303
00316 typename EnqPolicy::ReturnType enqNowait(T const & item);
00317
00323 void enqWait(T const & p);
00324
00332 bool enqTimedWait(T const & p, seconds const &);
00333
00341 bool deqNowait(ValueType &);
00342
00349 void deqWait(ValueType &);
00350
00359 bool deqTimedWait(ValueType &, seconds const &);
00360
00364 bool empty() const;
00365
00369 bool full() const;
00370
00375 SizeType size() const;
00376
00381 SizeType capacity() const;
00382
00388 bool setCapacity(SizeType n);
00389
00393 detail::MemoryType used() const;
00394
00399 detail::MemoryType memory() const;
00400
00407 bool setMemory(detail::MemoryType n);
00408
00414 SizeType clear();
00415
00419 void addExternallyDroppedEvents(SizeType);
00420
00424 bool queueReaderIsReady() { return readerReady_; }
00425
00429 void setReaderIsReady(bool rdy = true) {
00430 readyTime_ = std::chrono::steady_clock::now();
00431 readerReady_ = rdy;
00432 }
00433
00437 std::chrono::steady_clock::time_point getReadyTime() { return readyTime_; }
00438
00439 private:
00440 typedef std::lock_guard<std::mutex> LockType;
00441 typedef std::unique_lock<std::mutex> WaitLockType;
00442
00443 mutable std::mutex protectElements_;
00444 mutable std::condition_variable queueNotEmpty_;
00445 mutable std::condition_variable queueNotFull_;
00446
00447 std::chrono::steady_clock::time_point readyTime_;
00448 bool readerReady_;
00449 SequenceType elements_;
00450 SizeType capacity_;
00451 SizeType size_;
00452
00453
00454
00455
00456
00457
00458
00459 detail::MemoryType memory_;
00460 detail::MemoryType used_;
00461 size_t elementsDropped_;
00462
00463
00464
00465
00466
00467
00468
00469
00470
00471
00472
00473 bool insertIfPossible(T const & item);
00474
00475
00476
00477
00478
00479
00480
00481
00482
00483 bool removeHeadIfPossible(ValueType & item);
00484
00485
00486
00487
00488
00489
00490
00491
00492 void removeHead(ValueType & item);
00493
00494 void assignItem(T & item, const T & element);
00495 void assignItem(std::pair<T, size_t> & item, const T & element);
00496
00497
00498
00499
00500 bool isFull() const;
00501
00502
00503
00504
00505
00506 ConcurrentQueue(ConcurrentQueue<T, EnqPolicy> const &);
00507 ConcurrentQueue & operator=(ConcurrentQueue<T, EnqPolicy> const &);
00508 };
00509
00510
00511
00512
00513
00514 template <class T, class EnqPolicy>
00515 ConcurrentQueue<T, EnqPolicy>::ConcurrentQueue
00516 (
00517 SizeType maxSize,
00518 detail::MemoryType maxMemory
00519 ) :
00520 protectElements_(),
00521 readyTime_(std::chrono::steady_clock::now()),
00522 readerReady_(false),
00523 elements_(),
00524 capacity_(maxSize),
00525 size_(0),
00526 memory_(maxMemory),
00527 used_(0),
00528 elementsDropped_(0)
00529 {}
00530
00531 template <class T, class EnqPolicy>
00532 ConcurrentQueue<T, EnqPolicy>::~ConcurrentQueue()
00533 {
00534 LockType lock(protectElements_);
00535 elements_.clear();
00536 size_ = 0;
00537 used_ = 0;
00538 elementsDropped_ = 0;
00539 }
00540
00541
00542
00543
00544 template <class T, class EnqPolicy>
00545 typename EnqPolicy::ReturnType ConcurrentQueue<T, EnqPolicy>::enqNowait(T const & item)
00546 {
00547 TRACE( 12, "ConcurrentQueue<T,EnqPolicy>::enqNowait enter size=%zu capacity=%zu used=%zu memory=%zu", size_, capacity_, used_, memory_ );
00548 LockType lock(protectElements_);
00549 auto retval = EnqPolicy::doEnq(item, elements_, size_, capacity_, used_, memory_,
00550 elementsDropped_, queueNotEmpty_);
00551 TRACE( 12, "ConcurrentQueue<T,EnqPolicy>::enqNowait returning %zu", (SizeType)retval );
00552 return retval;
00553 }
00554
00555 template <class T, class EnqPolicy>
00556 void ConcurrentQueue<T, EnqPolicy>::enqWait(T const & item)
00557 {
00558 TRACE( 12, "ConcurrentQueue<T,EnqPolicy>::enqWait enter" );
00559 WaitLockType lock(protectElements_);
00560 while (isFull()) { queueNotFull_.wait(lock); }
00561 EnqPolicy::doInsert(item, elements_, size_,
00562 detail::memoryUsage(item), used_, queueNotEmpty_);
00563 TRACE( 12, "ConcurrentQueue<T,EnqPolicy>::enqWait returning" );
00564 }
00565
00566 template <class T, class EnqPolicy>
00567 bool ConcurrentQueue<T,EnqPolicy>::enqTimedWait( T const & item, seconds const & waitTime )
00568 {
00569 TRACE( 12, "ConcurrentQueue<T,EnqPolicy>::enqTimedWait enter with waitTime=%ld ms size=%zu capacity=%zu used=%zu memory=%zu"
00570 , std::chrono::duration_cast<std::chrono::milliseconds>(waitTime).count(), size_, capacity_, used_, memory_ );
00571 WaitLockType lock(protectElements_);
00572 if (isFull()) {
00573 queueNotFull_.wait_for(lock, waitTime);
00574 }
00575 bool retval=insertIfPossible(item);
00576 TRACE( 12, "ConcurrentQueue<T,EnqPolicy>::enqTimedWait returning %d", retval );
00577 return retval;
00578 }
00579
00580
00581
00582
00583 template <class T, class EnqPolicy>
00584 bool ConcurrentQueue<T, EnqPolicy>::deqNowait(ValueType & item)
00585 {
00586 TRACE( 12, "ConcurrentQueue<T, EnqPolicy>::deqNowait enter" );
00587 LockType lock(protectElements_);
00588 bool retval = removeHeadIfPossible(item);
00589 TRACE( 12, "ConcurrentQueue<T, EnqPolicy>::deqNowait returning %d", retval );
00590 return retval;
00591 }
00592
00593 template <class T, class EnqPolicy>
00594 void ConcurrentQueue<T, EnqPolicy>::deqWait( ValueType & item )
00595 {
00596 TRACE( 12, "ConcurrentQueue<T, EnqPolicy>::deqWait enter" );
00597 WaitLockType lock(protectElements_);
00598 while (size_ == 0) { queueNotEmpty_.wait(lock); }
00599 removeHead(item);
00600 TRACE( 12, "ConcurrentQueue<T, EnqPolicy>::deqWait returning" );
00601 }
00602
00603 template <class T, class EnqPolicy>
00604 bool ConcurrentQueue<T, EnqPolicy>::deqTimedWait( ValueType & item, seconds const & waitTime )
00605 {
00606 TRACE( 12, "ConcurrentQueue<T, EnqPolicy>::deqTimedWait enter with waitTime=%ld ms size=%zu"
00607 , std::chrono::duration_cast<std::chrono::milliseconds>(waitTime).count(), size_ );
00608 WaitLockType lock(protectElements_);
00609 if (size_ == 0) {
00610 queueNotEmpty_.wait_for(lock, waitTime);
00611 }
00612 bool retval=removeHeadIfPossible(item);
00613 TRACE( 12, "ConcurrentQueue<T, EnqPolicy>::deqTimedWait returning %d size=%zu", retval, size_ );
00614 return retval;
00615 }
00616
00617
00618
00619 template <class T, class EnqPolicy>
00620 bool
00621 ConcurrentQueue<T, EnqPolicy>::empty() const
00622 {
00623
00624 return size_ == 0;
00625 }
00626
00627 template <class T, class EnqPolicy>
00628 bool
00629 ConcurrentQueue<T, EnqPolicy>::full() const
00630 {
00631 LockType lock(protectElements_);
00632 return isFull();
00633 }
00634
00635 template <class T, class EnqPolicy>
00636 typename ConcurrentQueue<T, EnqPolicy>::SizeType
00637 ConcurrentQueue<T, EnqPolicy>::size() const
00638 {
00639
00640 return size_;
00641 }
00642
00643 template <class T, class EnqPolicy>
00644 typename ConcurrentQueue<T, EnqPolicy>::SizeType
00645 ConcurrentQueue<T, EnqPolicy>::capacity() const
00646 {
00647
00648 return capacity_;
00649 }
00650
00651 template <class T, class EnqPolicy>
00652 bool
00653 ConcurrentQueue<T, EnqPolicy>::setCapacity(SizeType newcapacity)
00654 {
00655 LockType lock(protectElements_);
00656 bool isEmpty = (size_ == 0);
00657 if (isEmpty) { capacity_ = newcapacity; }
00658 return isEmpty;
00659 }
00660
00661 template <class T, class EnqPolicy>
00662 detail::MemoryType
00663 ConcurrentQueue<T, EnqPolicy>::used() const
00664 {
00665
00666 return used_;
00667 }
00668
00669 template <class T, class EnqPolicy>
00670 detail::MemoryType
00671 ConcurrentQueue<T, EnqPolicy>::memory() const
00672 {
00673
00674 return memory_;
00675 }
00676
00677 template <class T, class EnqPolicy>
00678 bool
00679 ConcurrentQueue<T, EnqPolicy>::setMemory(detail::MemoryType newmemory)
00680 {
00681 LockType lock(protectElements_);
00682 bool isEmpty = (size_ == 0);
00683 if (isEmpty) { memory_ = newmemory; }
00684 return isEmpty;
00685 }
00686
00687 template <class T, class EnqPolicy>
00688 typename ConcurrentQueue<T, EnqPolicy>::SizeType
00689 ConcurrentQueue<T, EnqPolicy>::clear()
00690 {
00691 LockType lock(protectElements_);
00692 SizeType clearedEvents = size_;
00693 elementsDropped_ += size_;
00694 elements_.clear();
00695 size_ = 0;
00696 used_ = 0;
00697 return clearedEvents;
00698 }
00699
00700 template <class T, class EnqPolicy>
00701 void
00702 ConcurrentQueue<T, EnqPolicy>::addExternallyDroppedEvents(SizeType n)
00703 {
00704 LockType lock(protectElements_);
00705 elementsDropped_ += n;
00706 }
00707
00708
00709
00710
00711
00712 template <class T, class EnqPolicy>
00713 bool
00714 ConcurrentQueue<T, EnqPolicy>::insertIfPossible(T const & item)
00715 {
00716 if (isFull()) {
00717 ++elementsDropped_;
00718 return false;
00719 }
00720 else {
00721 EnqPolicy::doInsert(item, elements_, size_,
00722 detail::memoryUsage(item), used_, queueNotEmpty_);
00723 return true;
00724 }
00725 }
00726
00727 template <class T, class EnqPolicy>
00728 bool
00729 ConcurrentQueue<T, EnqPolicy>::removeHeadIfPossible(ValueType & item)
00730 {
00731 if (size_ == 0) { return false; }
00732 removeHead(item);
00733 return true;
00734 }
00735
00736 template <class T, class EnqPolicy>
00737 void
00738 ConcurrentQueue<T, EnqPolicy>::removeHead(ValueType & item)
00739 {
00740 SequenceType holder;
00741
00742 holder.splice(holder.begin(), elements_, elements_.begin());
00743
00744 --size_;
00745 queueNotFull_.notify_one();
00746 assignItem(item, holder.front());
00747 used_ -= detail::memoryUsage(item);
00748 }
00749
00750 template <class T, class EnqPolicy>
00751 void
00752 ConcurrentQueue<T, EnqPolicy>::assignItem(T & item, const T & element)
00753 {
00754 item = element;
00755 }
00756
00757 template <class T, class EnqPolicy>
00758 void
00759 ConcurrentQueue<T, EnqPolicy>::assignItem(std::pair<T, size_t> & item, const T & element)
00760 {
00761 item.first = element;
00762 item.second = elementsDropped_;
00763 elementsDropped_ = 0;
00764 }
00765
00766 template <class T, class EnqPolicy>
00767 bool
00768 ConcurrentQueue<T, EnqPolicy>::isFull() const
00769 {
00770 if (size_ >= capacity_ || used_ >= memory_) { return true; }
00771 return false;
00772 }
00773
00774 }
00775
00776 #endif
00777
00779
00785