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