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
00419
00420 private:
00421 typedef std::lock_guard<std::mutex> LockType;
00422 typedef std::unique_lock<std::mutex> WaitLockType;
00423
00424 mutable std::mutex protectElements_;
00425 mutable std::condition_variable queueNotEmpty_;
00426 mutable std::condition_variable queueNotFull_;
00427
00428 SequenceType elements_;
00429 SizeType capacity_;
00430 SizeType size_;
00431
00432
00433
00434
00435
00436
00437
00438 detail::MemoryType memory_;
00439 detail::MemoryType used_;
00440 size_t elementsDropped_;
00441
00442
00443
00444
00445
00446
00447
00448
00449
00450
00451
00452 bool insertIfPossible(T const & item);
00453
00454
00455
00456
00457
00458
00459
00460
00461
00462 bool removeHeadIfPossible(ValueType & item);
00463
00464
00465
00466
00467
00468
00469
00470
00471 void removeHead(ValueType & item);
00472
00473 void assignItem(T & item, const T & element);
00474 void assignItem(std::pair<T, size_t> & item, const T & element);
00475
00476
00477
00478
00479 bool isFull() const;
00480
00481
00482
00483
00484
00485 ConcurrentQueue(ConcurrentQueue<T, EnqPolicy> const &);
00486 ConcurrentQueue & operator=(ConcurrentQueue<T, EnqPolicy> const &);
00487 };
00488
00489
00490
00491
00492
00493 template <class T, class EnqPolicy>
00494 ConcurrentQueue<T, EnqPolicy>::ConcurrentQueue
00495 (
00496 SizeType maxSize,
00497 detail::MemoryType maxMemory
00498 ) :
00499 protectElements_(),
00500 elements_(),
00501 capacity_(maxSize),
00502 size_(0),
00503 memory_(maxMemory),
00504 used_(0),
00505 elementsDropped_(0)
00506 {}
00507
00508 template <class T, class EnqPolicy>
00509 ConcurrentQueue<T, EnqPolicy>::~ConcurrentQueue()
00510 {
00511 LockType lock(protectElements_);
00512 elements_.clear();
00513 size_ = 0;
00514 used_ = 0;
00515 elementsDropped_ = 0;
00516 }
00517
00518 template <class T, class EnqPolicy>
00519 typename EnqPolicy::ReturnType
00520 ConcurrentQueue<T, EnqPolicy>::enqNowait(T const & item)
00521 {
00522 LockType lock(protectElements_);
00523 return EnqPolicy::doEnq
00524 (item, elements_, size_, capacity_, used_, memory_,
00525 elementsDropped_, queueNotEmpty_);
00526 }
00527
00528 template <class T, class EnqPolicy>
00529 void
00530 ConcurrentQueue<T, EnqPolicy>::enqWait(T const & item)
00531 {
00532 WaitLockType lock(protectElements_);
00533 while (isFull()) { queueNotFull_.wait(lock); }
00534 EnqPolicy::doInsert(item, elements_, size_,
00535 detail::memoryUsage(item), used_, queueNotEmpty_);
00536 }
00537
00538 template <class T, class EnqPolicy>
00539 bool
00540 ConcurrentQueue<T, EnqPolicy>::enqTimedWait
00541 (
00542 T const & item,
00543 seconds const & waitTime
00544 )
00545 {
00546 WaitLockType lock(protectElements_);
00547 if (isFull()) {
00548 queueNotFull_.wait_for(lock, waitTime);
00549 }
00550 return insertIfPossible(item);
00551 }
00552
00553 template <class T, class EnqPolicy>
00554 bool
00555 ConcurrentQueue<T, EnqPolicy>::deqNowait(ValueType & item)
00556 {
00557 LockType lock(protectElements_);
00558 return removeHeadIfPossible(item);
00559 }
00560
00561 template <class T, class EnqPolicy>
00562 void
00563 ConcurrentQueue<T, EnqPolicy>::deqWait(ValueType & item)
00564 {
00565 WaitLockType lock(protectElements_);
00566 while (size_ == 0) { queueNotEmpty_.wait(lock); }
00567 removeHead(item);
00568 }
00569
00570 template <class T, class EnqPolicy>
00571 bool
00572 ConcurrentQueue<T, EnqPolicy>::deqTimedWait
00573 (
00574 ValueType & item,
00575 seconds const & waitTime
00576 )
00577 {
00578 WaitLockType lock(protectElements_);
00579 if (size_ == 0) {
00580 queueNotEmpty_.wait_for(lock, waitTime);
00581 }
00582 return removeHeadIfPossible(item);
00583 }
00584
00585 template <class T, class EnqPolicy>
00586 bool
00587 ConcurrentQueue<T, EnqPolicy>::empty() const
00588 {
00589
00590 return size_ == 0;
00591 }
00592
00593 template <class T, class EnqPolicy>
00594 bool
00595 ConcurrentQueue<T, EnqPolicy>::full() const
00596 {
00597 LockType lock(protectElements_);
00598 return isFull();
00599 }
00600
00601 template <class T, class EnqPolicy>
00602 typename ConcurrentQueue<T, EnqPolicy>::SizeType
00603 ConcurrentQueue<T, EnqPolicy>::size() const
00604 {
00605
00606 return size_;
00607 }
00608
00609 template <class T, class EnqPolicy>
00610 typename ConcurrentQueue<T, EnqPolicy>::SizeType
00611 ConcurrentQueue<T, EnqPolicy>::capacity() const
00612 {
00613
00614 return capacity_;
00615 }
00616
00617 template <class T, class EnqPolicy>
00618 bool
00619 ConcurrentQueue<T, EnqPolicy>::setCapacity(SizeType newcapacity)
00620 {
00621 LockType lock(protectElements_);
00622 bool isEmpty = (size_ == 0);
00623 if (isEmpty) { capacity_ = newcapacity; }
00624 return isEmpty;
00625 }
00626
00627 template <class T, class EnqPolicy>
00628 detail::MemoryType
00629 ConcurrentQueue<T, EnqPolicy>::used() const
00630 {
00631
00632 return used_;
00633 }
00634
00635 template <class T, class EnqPolicy>
00636 detail::MemoryType
00637 ConcurrentQueue<T, EnqPolicy>::memory() const
00638 {
00639
00640 return memory_;
00641 }
00642
00643 template <class T, class EnqPolicy>
00644 bool
00645 ConcurrentQueue<T, EnqPolicy>::setMemory(detail::MemoryType newmemory)
00646 {
00647 LockType lock(protectElements_);
00648 bool isEmpty = (size_ == 0);
00649 if (isEmpty) { memory_ = newmemory; }
00650 return isEmpty;
00651 }
00652
00653 template <class T, class EnqPolicy>
00654 typename ConcurrentQueue<T, EnqPolicy>::SizeType
00655 ConcurrentQueue<T, EnqPolicy>::clear()
00656 {
00657 LockType lock(protectElements_);
00658 SizeType clearedEvents = size_;
00659 elementsDropped_ += size_;
00660 elements_.clear();
00661 size_ = 0;
00662 used_ = 0;
00663 return clearedEvents;
00664 }
00665
00666 template <class T, class EnqPolicy>
00667 void
00668 ConcurrentQueue<T, EnqPolicy>::addExternallyDroppedEvents(SizeType n)
00669 {
00670 LockType lock(protectElements_);
00671 elementsDropped_ += n;
00672 }
00673
00674
00675
00676
00677
00678 template <class T, class EnqPolicy>
00679 bool
00680 ConcurrentQueue<T, EnqPolicy>::insertIfPossible(T const & item)
00681 {
00682 if (isFull()) {
00683 ++elementsDropped_;
00684 return false;
00685 }
00686 else {
00687 EnqPolicy::doInsert(item, elements_, size_,
00688 detail::memoryUsage(item), used_, queueNotEmpty_);
00689 return true;
00690 }
00691 }
00692
00693 template <class T, class EnqPolicy>
00694 bool
00695 ConcurrentQueue<T, EnqPolicy>::removeHeadIfPossible(ValueType & item)
00696 {
00697 if (size_ == 0) { return false; }
00698 removeHead(item);
00699 return true;
00700 }
00701
00702 template <class T, class EnqPolicy>
00703 void
00704 ConcurrentQueue<T, EnqPolicy>::removeHead(ValueType & item)
00705 {
00706 SequenceType holder;
00707
00708 holder.splice(holder.begin(), elements_, elements_.begin());
00709
00710 --size_;
00711 queueNotFull_.notify_one();
00712 assignItem(item, holder.front());
00713 used_ -= detail::memoryUsage(item);
00714 }
00715
00716 template <class T, class EnqPolicy>
00717 void
00718 ConcurrentQueue<T, EnqPolicy>::assignItem(T & item, const T & element)
00719 {
00720 item = element;
00721 }
00722
00723 template <class T, class EnqPolicy>
00724 void
00725 ConcurrentQueue<T, EnqPolicy>::assignItem(std::pair<T, size_t> & item, const T & element)
00726 {
00727 item.first = element;
00728 item.second = elementsDropped_;
00729 elementsDropped_ = 0;
00730 }
00731
00732 template <class T, class EnqPolicy>
00733 bool
00734 ConcurrentQueue<T, EnqPolicy>::isFull() const
00735 {
00736 if (size_ >= capacity_ || used_ >= memory_) { return true; }
00737 return false;
00738 }
00739
00740 }
00741
00742 #endif
00743
00745
00751