00001 #ifndef artdaq_core_Core_ConcurrentQueue_hh
00002 #define artdaq_core_Core_ConcurrentQueue_hh
00003
00004 #include <algorithm>
00005 #include <cstddef>
00006 #include <exception>
00007 #include <limits>
00008 #include <list>
00009
00010 #include <iostream>
00011 #include "tracemf.h"
00012
00013 #include <chrono>
00014 #include <condition_variable>
00015 #include <mutex>
00016 #include <type_traits>
00017
00018 #undef TRACE_NAME
00019 #define TRACE_NAME "ConcurrentQueue"
00020
00021
00022
00023
00024
00025
00026
00027 namespace artdaq
00028 {
00053 namespace detail
00054 {
00064 typedef std::chrono::duration<double> seconds;
00065
00066 typedef size_t MemoryType;
00067
00073 template <typename T>
00074 class hasMemoryUsed
00075 {
00076 typedef char TrueType;
00077
00078 struct FalseType
00079 {
00080 TrueType _[2];
00081 };
00082
00083 template <MemoryType(T::*)() const>
00084 struct TestConst;
00085
00086 template <typename C>
00087 static TrueType test(TestConst<&C::memoryUsed>*) { return 0; }
00088
00089 template <typename C>
00090 static FalseType test(...)
00091 {
00092 return {};
00093 }
00094
00095 public:
00096
00102 static const bool value = (sizeof(test<T>(nullptr)) == sizeof(TrueType));
00103 };
00104
00111 template <typename T>
00112 MemoryType
00113 memoryUsage(const std::pair<T, size_t>& t)
00114 {
00115 MemoryType usage(0UL);
00116 try
00117 {
00118 usage = t.first.memoryUsed();
00119 }
00120 catch (...) {}
00121 return usage;
00122 }
00123
00130 template <typename T>
00131 typename std::enable_if<hasMemoryUsed<T>::value, MemoryType>::type
00132 memoryUsage(const T& t)
00133 {
00134 MemoryType usage(0UL);
00135 try
00136 {
00137 usage = t.memoryUsed();
00138 }
00139 catch (...) {}
00140 return usage;
00141 }
00142
00149 template <typename T>
00150 typename std::enable_if<!hasMemoryUsed<T>::value, MemoryType>::type
00151 memoryUsage(const T& t)
00152 {
00153 return sizeof(t);
00154 }
00155 }
00156
00157
00162 template <class T>
00163 struct FailIfFull
00164 {
00165 typedef bool ReturnType;
00166
00167 typedef T ValueType;
00168 typedef std::list<T> SequenceType;
00169 typedef typename SequenceType::size_type SizeType;
00170
00174 static struct QueueIsFull : public std::exception
00175 {
00180 virtual const char* what() const throw()
00181 {
00182 return "Cannot add item to a full queue";
00183 }
00184 } queueIsFull;
00185
00195 static void doInsert
00196 (
00197 T const& item,
00198 SequenceType& elements,
00199 SizeType& size,
00200 detail::MemoryType const& itemSize,
00201 detail::MemoryType& used,
00202 std::condition_variable& nonempty
00203 )
00204 {
00205 elements.push_back(item);
00206 ++size;
00207 used += itemSize;
00208 nonempty.notify_one();
00209 }
00210
00224 static ReturnType doEnq
00225 (
00226 T const& item,
00227 SequenceType& elements,
00228 SizeType& size,
00229 SizeType& capacity,
00230 detail::MemoryType& used,
00231 detail::MemoryType& memory,
00232 size_t& elementsDropped,
00233 std::condition_variable& nonempty
00234 )
00235 {
00236 detail::MemoryType itemSize = detail::memoryUsage(item);
00237 if (size >= capacity || used + itemSize > memory)
00238 {
00239 ++elementsDropped;
00240 throw queueIsFull;
00241 }
00242 else
00243 {
00244 doInsert(item, elements, size, itemSize, used, nonempty);
00245 }
00246 return true;
00247 }
00248 };
00249
00250 template <typename T>
00251 typename FailIfFull<T>::QueueIsFull FailIfFull<T>::queueIsFull{};
00252
00257 template <class T>
00258 struct KeepNewest
00259 {
00260 typedef std::pair<T, size_t> ValueType;
00261 typedef std::list<T> SequenceType;
00262 typedef typename SequenceType::size_type SizeType;
00263 typedef SizeType ReturnType;
00264
00274 static void doInsert
00275 (
00276 T const& item,
00277 SequenceType& elements,
00278 SizeType& size,
00279 detail::MemoryType const& itemSize,
00280 detail::MemoryType& used,
00281 std::condition_variable& nonempty
00282 )
00283 {
00284 elements.push_back(item);
00285 ++size;
00286 used += itemSize;
00287 nonempty.notify_one();
00288 }
00289
00302 static ReturnType doEnq
00303 (
00304 T const& item,
00305 SequenceType& elements,
00306 SizeType& size,
00307 SizeType& capacity,
00308 detail::MemoryType& used,
00309 detail::MemoryType& memory,
00310 size_t& elementsDropped,
00311 std::condition_variable& nonempty
00312 )
00313 {
00314 SizeType elementsRemoved(0);
00315 detail::MemoryType itemSize = detail::memoryUsage(item);
00316 while ((size == capacity || used + itemSize > memory) && !elements.empty())
00317 {
00318 SequenceType holder;
00319
00320 holder.splice(holder.begin(), elements, elements.begin());
00321
00322 --size;
00323 used -= detail::memoryUsage(holder.front());
00324 ++elementsRemoved;
00325 }
00326 if (size < capacity && used + itemSize <= memory)
00327
00328 {
00329 doInsert(item, elements, size, itemSize, used, nonempty);
00330 }
00331 else
00332 {
00333
00334 ++elementsRemoved;
00335 }
00336 elementsDropped += elementsRemoved;
00337 return elementsRemoved;
00338 }
00339 };
00340
00345 template <class T>
00346 struct RejectNewest
00347 {
00348 typedef std::pair<T, size_t> ValueType;
00349 typedef std::list<T> SequenceType;
00350 typedef typename SequenceType::size_type SizeType;
00351 typedef SizeType ReturnType;
00352
00362 static void doInsert
00363 (
00364 T const& item,
00365 SequenceType& elements,
00366 SizeType& size,
00367 detail::MemoryType const& itemSize,
00368 detail::MemoryType& used,
00369 std::condition_variable& nonempty
00370 )
00371 {
00372 elements.push_back(item);
00373 ++size;
00374 used += itemSize;
00375 nonempty.notify_one();
00376 }
00377
00390 static ReturnType doEnq
00391 (
00392 T const& item,
00393 SequenceType& elements,
00394 SizeType& size,
00395 SizeType& capacity,
00396 detail::MemoryType& used,
00397 detail::MemoryType& memory,
00398 size_t& elementsDropped,
00399 std::condition_variable& nonempty
00400 )
00401 {
00402 detail::MemoryType itemSize = detail::memoryUsage(item);
00403 if (size < capacity && used + itemSize <= memory)
00404 {
00405 doInsert(item, elements, size, itemSize, used, nonempty);
00406 return 0;
00407 }
00408 ++elementsDropped;
00409 return 1;
00410 }
00411 };
00412
00416 template <class T, class EnqPolicy = FailIfFull<T>>
00417 class ConcurrentQueue
00418 {
00419 public:
00420 typedef typename EnqPolicy::ValueType ValueType;
00421 typedef typename EnqPolicy::SequenceType SequenceType;
00422 typedef typename SequenceType::size_type SizeType;
00423
00430 explicit ConcurrentQueue
00431 (
00432 SizeType maxSize = std::numeric_limits<SizeType>::max(),
00433 detail::MemoryType maxMemory = std::numeric_limits<detail::MemoryType>::max()
00434 );
00435
00442 ~ConcurrentQueue();
00443
00460 typename EnqPolicy::ReturnType enqNowait(T const& item);
00461
00470 void enqWait(T const& item);
00471
00484 bool enqTimedWait(T const& item, detail::seconds const& wait);
00485
00498 bool deqNowait(ValueType& item);
00499
00510 void deqWait(ValueType& item);
00511
00525 bool deqTimedWait(ValueType& item, detail::seconds const& wait);
00526
00531 bool empty() const;
00532
00537 bool full() const;
00538
00544 SizeType size() const;
00545
00551 SizeType capacity() const;
00552
00560 bool setCapacity(SizeType capacity);
00561
00566 detail::MemoryType used() const;
00567
00573 detail::MemoryType memory() const;
00574
00583 bool setMemory(detail::MemoryType maxMemory);
00584
00590 SizeType clear();
00591
00596 void addExternallyDroppedEvents(SizeType dropped);
00597
00602 bool queueReaderIsReady() const { return readerReady_; }
00603
00611 void setReaderIsReady(bool rdy = true)
00612 {
00613 readyTime_ = std::chrono::steady_clock::now();
00614 readerReady_ = rdy;
00615 }
00616
00621 std::chrono::steady_clock::time_point getReadyTime() const { return readyTime_; }
00622
00623 private:
00624 typedef std::lock_guard<std::mutex> LockType;
00625 typedef std::unique_lock<std::mutex> WaitLockType;
00626
00627 mutable std::mutex protectElements_;
00628 mutable std::condition_variable queueNotEmpty_;
00629 mutable std::condition_variable queueNotFull_;
00630
00631 std::chrono::steady_clock::time_point readyTime_;
00632 bool readerReady_;
00633 SequenceType elements_;
00634 SizeType capacity_;
00635 SizeType size_;
00636
00637
00638
00639
00640
00641
00642
00643 detail::MemoryType memory_;
00644 detail::MemoryType used_;
00645 size_t elementsDropped_;
00646
00647
00648
00649
00650
00651
00652
00653
00654
00655
00656
00657 bool insertIfPossible(T const& item);
00658
00672 bool removeHeadIfPossible(ValueType& item);
00673
00685 void removeHead(ValueType& item);
00686
00687 void assignItem(T& item, const T& element);
00688
00689 void assignItem(std::pair<T, size_t>& item, const T& element);
00690
00691
00692
00693
00694 bool isFull() const;
00695
00696
00697
00698
00699
00700 ConcurrentQueue(ConcurrentQueue<T, EnqPolicy> const&) = delete;
00701
00702 ConcurrentQueue& operator=(ConcurrentQueue<T, EnqPolicy> const&) = delete;
00703 };
00704
00705
00706
00707
00708
00709 template <class T, class EnqPolicy>
00710 ConcurrentQueue<T, EnqPolicy>::ConcurrentQueue
00711 (
00712 SizeType maxSize,
00713 detail::MemoryType maxMemory
00714 ) :
00715 protectElements_()
00716 , readyTime_(std::chrono::steady_clock::now())
00717 , readerReady_(false)
00718 , elements_()
00719 , capacity_(maxSize)
00720 , size_(0)
00721 , memory_(maxMemory)
00722 , used_(0)
00723 , elementsDropped_(0) {}
00724
00725 template <class T, class EnqPolicy>
00726 ConcurrentQueue<T, EnqPolicy>::~ConcurrentQueue()
00727 {
00728 LockType lock(protectElements_);
00729 elements_.clear();
00730 size_ = 0;
00731 used_ = 0;
00732 elementsDropped_ = 0;
00733 }
00734
00735
00736
00737
00738 template <class T, class EnqPolicy>
00739 typename EnqPolicy::ReturnType ConcurrentQueue<T, EnqPolicy>::enqNowait(T const& item)
00740 {
00741 TLOG(12) << "enqNowait enter size=" << size_ << " capacity=" << capacity_ << " used=" << used_ << " memory=" << memory_ << TLOG_ENDL;
00742 LockType lock(protectElements_);
00743 auto retval = EnqPolicy::doEnq(item, elements_, size_, capacity_, used_, memory_,
00744 elementsDropped_, queueNotEmpty_);
00745 TLOG(12) << "enqNowait returning " << retval << TLOG_ENDL;
00746 return retval;
00747 }
00748
00749 template <class T, class EnqPolicy>
00750 void ConcurrentQueue<T, EnqPolicy>::enqWait(T const& item)
00751 {
00752 TLOG(13) << "enqWait enter" << TLOG_ENDL;
00753 WaitLockType lock(protectElements_);
00754 while (isFull()) { queueNotFull_.wait(lock); }
00755 EnqPolicy::doInsert(item, elements_, size_,
00756 detail::memoryUsage(item), used_, queueNotEmpty_);
00757 TLOG(13) << "enqWait returning" << TLOG_ENDL;
00758 }
00759
00760 template <class T, class EnqPolicy>
00761 bool ConcurrentQueue<T, EnqPolicy>::enqTimedWait(T const& item, detail::seconds const& waitTime)
00762 {
00763 TLOG(14) << "ConcurrentQueue<T,EnqPolicy>::enqTimedWait enter with waitTime=" << std::chrono::duration_cast<std::chrono::milliseconds>(waitTime).count() << " ms size=" << size_
00764 << " capacity=" << capacity_ << " used=" << used_ << " memory=" << memory_ << TLOG_ENDL;
00765 WaitLockType lock(protectElements_);
00766 if (isFull())
00767 {
00768 queueNotFull_.wait_for(lock, waitTime);
00769 }
00770 bool retval = insertIfPossible(item);
00771 TLOG(14) << "ConcurrentQueue<T,EnqPolicy>::enqTimedWait returning " << retval << TLOG_ENDL;
00772 return retval;
00773 }
00774
00775
00776
00777
00778 template <class T, class EnqPolicy>
00779 bool ConcurrentQueue<T, EnqPolicy>::deqNowait(ValueType& item)
00780 {
00781 TLOG(15) << "ConcurrentQueue<T, EnqPolicy>::deqNowait enter" << TLOG_ENDL;
00782 LockType lock(protectElements_);
00783 bool retval = removeHeadIfPossible(item);
00784 TLOG(15) << "ConcurrentQueue<T, EnqPolicy>::deqNowait returning " << retval << TLOG_ENDL;
00785 return retval;
00786 }
00787
00788 template <class T, class EnqPolicy>
00789 void ConcurrentQueue<T, EnqPolicy>::deqWait(ValueType& item)
00790 {
00791 TLOG(16) << "ConcurrentQueue<T, EnqPolicy>::deqWait enter" << TLOG_ENDL;
00792 WaitLockType lock(protectElements_);
00793 while (size_ == 0) { queueNotEmpty_.wait(lock); }
00794 removeHead(item);
00795 TLOG(16) << "ConcurrentQueue<T, EnqPolicy>::deqWait returning" << TLOG_ENDL;
00796 }
00797
00798 template <class T, class EnqPolicy>
00799 bool ConcurrentQueue<T, EnqPolicy>::deqTimedWait(ValueType& item, detail::seconds const& waitTime)
00800 {
00801 TLOG(17) << "ConcurrentQueue<T, EnqPolicy>::deqTimedWait enter with waitTime=" << std::chrono::duration_cast<std::chrono::milliseconds>(waitTime).count() << " ms size=" << size_ << TLOG_ENDL;
00802 WaitLockType lock(protectElements_);
00803 if (size_ == 0)
00804 {
00805 queueNotEmpty_.wait_for(lock, waitTime);
00806 }
00807 bool retval = removeHeadIfPossible(item);
00808 TLOG(17) << "ConcurrentQueue<T, EnqPolicy>::deqTimedWait returning " << retval << " size=" << size_ << TLOG_ENDL;
00809 return retval;
00810 }
00811
00812
00813 template <class T, class EnqPolicy>
00814 bool
00815 ConcurrentQueue<T, EnqPolicy>::empty() const
00816 {
00817
00818 return size_ == 0;
00819 }
00820
00821 template <class T, class EnqPolicy>
00822 bool
00823 ConcurrentQueue<T, EnqPolicy>::full() const
00824 {
00825 LockType lock(protectElements_);
00826 return isFull();
00827 }
00828
00829 template <class T, class EnqPolicy>
00830 typename ConcurrentQueue<T, EnqPolicy>::SizeType
00831 ConcurrentQueue<T, EnqPolicy>::size() const
00832 {
00833
00834 return size_;
00835 }
00836
00837 template <class T, class EnqPolicy>
00838 typename ConcurrentQueue<T, EnqPolicy>::SizeType
00839 ConcurrentQueue<T, EnqPolicy>::capacity() const
00840 {
00841
00842 return capacity_;
00843 }
00844
00845 template <class T, class EnqPolicy>
00846 bool
00847 ConcurrentQueue<T, EnqPolicy>::setCapacity(SizeType newcapacity)
00848 {
00849 LockType lock(protectElements_);
00850 bool isEmpty = (size_ == 0);
00851 if (isEmpty) { capacity_ = newcapacity; }
00852 return isEmpty;
00853 }
00854
00855 template <class T, class EnqPolicy>
00856 detail::MemoryType
00857 ConcurrentQueue<T, EnqPolicy>::used() const
00858 {
00859
00860 return used_;
00861 }
00862
00863 template <class T, class EnqPolicy>
00864 detail::MemoryType
00865 ConcurrentQueue<T, EnqPolicy>::memory() const
00866 {
00867
00868 return memory_;
00869 }
00870
00871 template <class T, class EnqPolicy>
00872 bool
00873 ConcurrentQueue<T, EnqPolicy>::setMemory(detail::MemoryType newmemory)
00874 {
00875 LockType lock(protectElements_);
00876 bool isEmpty = (size_ == 0);
00877 if (isEmpty) { memory_ = newmemory; }
00878 return isEmpty;
00879 }
00880
00881 template <class T, class EnqPolicy>
00882 typename ConcurrentQueue<T, EnqPolicy>::SizeType
00883 ConcurrentQueue<T, EnqPolicy>::clear()
00884 {
00885 LockType lock(protectElements_);
00886 SizeType clearedEvents = size_;
00887 elementsDropped_ += size_;
00888 elements_.clear();
00889 size_ = 0;
00890 used_ = 0;
00891 return clearedEvents;
00892 }
00893
00894 template <class T, class EnqPolicy>
00895 void
00896 ConcurrentQueue<T, EnqPolicy>::addExternallyDroppedEvents(SizeType n)
00897 {
00898 LockType lock(protectElements_);
00899 elementsDropped_ += n;
00900 }
00901
00902
00903
00904
00905
00906 template <class T, class EnqPolicy>
00907 bool
00908 ConcurrentQueue<T, EnqPolicy>::insertIfPossible(T const& item)
00909 {
00910 if (isFull())
00911 {
00912 ++elementsDropped_;
00913 return false;
00914 }
00915 else
00916 {
00917 EnqPolicy::doInsert(item, elements_, size_,
00918 detail::memoryUsage(item), used_, queueNotEmpty_);
00919 return true;
00920 }
00921 }
00922
00923 template <class T, class EnqPolicy>
00924 bool
00925 ConcurrentQueue<T, EnqPolicy>::removeHeadIfPossible(ValueType& item)
00926 {
00927 if (size_ == 0) { return false; }
00928 removeHead(item);
00929 return true;
00930 }
00931
00932 template <class T, class EnqPolicy>
00933 void
00934 ConcurrentQueue<T, EnqPolicy>::removeHead(ValueType& item)
00935 {
00936 SequenceType holder;
00937
00938 holder.splice(holder.begin(), elements_, elements_.begin());
00939
00940 --size_;
00941 queueNotFull_.notify_one();
00942 assignItem(item, holder.front());
00943 used_ -= detail::memoryUsage(item);
00944 }
00945
00946 template <class T, class EnqPolicy>
00947 void
00948 ConcurrentQueue<T, EnqPolicy>::assignItem(T& item, const T& element)
00949 {
00950 item = element;
00951 }
00952
00953 template <class T, class EnqPolicy>
00954 void
00955 ConcurrentQueue<T, EnqPolicy>::assignItem(std::pair<T, size_t>& item, const T& element)
00956 {
00957 item.first = element;
00958 item.second = elementsDropped_;
00959 elementsDropped_ = 0;
00960 }
00961
00962 template <class T, class EnqPolicy>
00963 bool
00964 ConcurrentQueue<T, EnqPolicy>::isFull() const
00965 {
00966 if (size_ >= capacity_ || used_ >= memory_) { return true; }
00967 return false;
00968 }
00969 }
00970
00971 #endif
00972
00974