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
00019
00020
00021
00022
00023
00024
00025 namespace artdaq
00026 {
00051 namespace detail
00052 {
00062 typedef std::chrono::duration<double> seconds;
00063
00064 typedef size_t MemoryType;
00065
00071 template <typename T>
00072 class hasMemoryUsed
00073 {
00074 typedef char TrueType;
00075
00076 struct FalseType
00077 {
00078 TrueType _[2];
00079 };
00080
00081 template <MemoryType(T::*)() const>
00082 struct TestConst;
00083
00084 template <typename C>
00085 static TrueType test(TestConst<&C::memoryUsed>*) { return 0; }
00086
00087 template <typename C>
00088 static FalseType test(...)
00089 {
00090 return {};
00091 }
00092
00093 public:
00094
00100 static const bool value = (sizeof(test<T>(nullptr)) == sizeof(TrueType));
00101 };
00102
00109 template <typename T>
00110 MemoryType
00111 memoryUsage(const std::pair<T, size_t>& t)
00112 {
00113 MemoryType usage(0UL);
00114 try
00115 {
00116 usage = t.first.memoryUsed();
00117 }
00118 catch (...) {}
00119 return usage;
00120 }
00121
00128 template <typename T>
00129 typename std::enable_if<hasMemoryUsed<T>::value, MemoryType>::type
00130 memoryUsage(const T& t)
00131 {
00132 MemoryType usage(0UL);
00133 try
00134 {
00135 usage = t.memoryUsed();
00136 }
00137 catch (...) {}
00138 return usage;
00139 }
00140
00147 template <typename T>
00148 typename std::enable_if<!hasMemoryUsed<T>::value, MemoryType>::type
00149 memoryUsage(const T& t)
00150 {
00151 return sizeof(t);
00152 }
00153 }
00154
00155
00160 template <class T>
00161 struct FailIfFull
00162 {
00163 typedef bool ReturnType;
00164
00165 typedef T ValueType;
00166 typedef std::list<T> SequenceType;
00167 typedef typename SequenceType::size_type SizeType;
00168
00172 static struct QueueIsFull : public std::exception
00173 {
00178 virtual const char* what() const throw()
00179 {
00180 return "Cannot add item to a full queue";
00181 }
00182 } queueIsFull;
00183
00193 static void doInsert
00194 (
00195 T const& item,
00196 SequenceType& elements,
00197 SizeType& size,
00198 detail::MemoryType const& itemSize,
00199 detail::MemoryType& used,
00200 std::condition_variable& nonempty
00201 )
00202 {
00203 elements.push_back(item);
00204 ++size;
00205 used += itemSize;
00206 nonempty.notify_one();
00207 }
00208
00222 static ReturnType doEnq
00223 (
00224 T const& item,
00225 SequenceType& elements,
00226 SizeType& size,
00227 SizeType& capacity,
00228 detail::MemoryType& used,
00229 detail::MemoryType& memory,
00230 size_t& elementsDropped,
00231 std::condition_variable& nonempty
00232 )
00233 {
00234 detail::MemoryType itemSize = detail::memoryUsage(item);
00235 if (size >= capacity || used + itemSize > memory)
00236 {
00237 ++elementsDropped;
00238 throw queueIsFull;
00239 }
00240 else
00241 {
00242 doInsert(item, elements, size, itemSize, used, nonempty);
00243 }
00244 return true;
00245 }
00246 };
00247
00248 template <typename T>
00249 typename FailIfFull<T>::QueueIsFull FailIfFull<T>::queueIsFull{};
00250
00255 template <class T>
00256 struct KeepNewest
00257 {
00258 typedef std::pair<T, size_t> ValueType;
00259 typedef std::list<T> SequenceType;
00260 typedef typename SequenceType::size_type SizeType;
00261 typedef SizeType ReturnType;
00262
00272 static void doInsert
00273 (
00274 T const& item,
00275 SequenceType& elements,
00276 SizeType& size,
00277 detail::MemoryType const& itemSize,
00278 detail::MemoryType& used,
00279 std::condition_variable& nonempty
00280 )
00281 {
00282 elements.push_back(item);
00283 ++size;
00284 used += itemSize;
00285 nonempty.notify_one();
00286 }
00287
00300 static ReturnType doEnq
00301 (
00302 T const& item,
00303 SequenceType& elements,
00304 SizeType& size,
00305 SizeType& capacity,
00306 detail::MemoryType& used,
00307 detail::MemoryType& memory,
00308 size_t& elementsDropped,
00309 std::condition_variable& nonempty
00310 )
00311 {
00312 SizeType elementsRemoved(0);
00313 detail::MemoryType itemSize = detail::memoryUsage(item);
00314 while ((size == capacity || used + itemSize > memory) && !elements.empty())
00315 {
00316 SequenceType holder;
00317
00318 holder.splice(holder.begin(), elements, elements.begin());
00319
00320 --size;
00321 used -= detail::memoryUsage(holder.front());
00322 ++elementsRemoved;
00323 }
00324 if (size < capacity && used + itemSize <= memory)
00325
00326 {
00327 doInsert(item, elements, size, itemSize, used, nonempty);
00328 }
00329 else
00330 {
00331
00332 ++elementsRemoved;
00333 }
00334 elementsDropped += elementsRemoved;
00335 return elementsRemoved;
00336 }
00337 };
00338
00343 template <class T>
00344 struct RejectNewest
00345 {
00346 typedef std::pair<T, size_t> ValueType;
00347 typedef std::list<T> SequenceType;
00348 typedef typename SequenceType::size_type SizeType;
00349 typedef SizeType ReturnType;
00350
00360 static void doInsert
00361 (
00362 T const& item,
00363 SequenceType& elements,
00364 SizeType& size,
00365 detail::MemoryType const& itemSize,
00366 detail::MemoryType& used,
00367 std::condition_variable& nonempty
00368 )
00369 {
00370 elements.push_back(item);
00371 ++size;
00372 used += itemSize;
00373 nonempty.notify_one();
00374 }
00375
00388 static ReturnType doEnq
00389 (
00390 T const& item,
00391 SequenceType& elements,
00392 SizeType& size,
00393 SizeType& capacity,
00394 detail::MemoryType& used,
00395 detail::MemoryType& memory,
00396 size_t& elementsDropped,
00397 std::condition_variable& nonempty
00398 )
00399 {
00400 detail::MemoryType itemSize = detail::memoryUsage(item);
00401 if (size < capacity && used + itemSize <= memory)
00402 {
00403 doInsert(item, elements, size, itemSize, used, nonempty);
00404 return 0;
00405 }
00406 ++elementsDropped;
00407 return 1;
00408 }
00409 };
00410
00414 template <class T, class EnqPolicy = FailIfFull<T>>
00415 class ConcurrentQueue
00416 {
00417 public:
00418 typedef typename EnqPolicy::ValueType ValueType;
00419 typedef typename EnqPolicy::SequenceType SequenceType;
00420 typedef typename SequenceType::size_type SizeType;
00421
00428 explicit ConcurrentQueue
00429 (
00430 SizeType maxSize = std::numeric_limits<SizeType>::max(),
00431 detail::MemoryType maxMemory = std::numeric_limits<detail::MemoryType>::max()
00432 );
00433
00440 ~ConcurrentQueue();
00441
00458 typename EnqPolicy::ReturnType enqNowait(T const& item);
00459
00468 void enqWait(T const& item);
00469
00482 bool enqTimedWait(T const& item, detail::seconds const& wait);
00483
00496 bool deqNowait(ValueType& item);
00497
00508 void deqWait(ValueType& item);
00509
00523 bool deqTimedWait(ValueType& item, detail::seconds const& wait);
00524
00529 bool empty() const;
00530
00535 bool full() const;
00536
00542 SizeType size() const;
00543
00549 SizeType capacity() const;
00550
00558 bool setCapacity(SizeType capacity);
00559
00564 detail::MemoryType used() const;
00565
00571 detail::MemoryType memory() const;
00572
00581 bool setMemory(detail::MemoryType maxMemory);
00582
00588 SizeType clear();
00589
00594 void addExternallyDroppedEvents(SizeType dropped);
00595
00600 bool queueReaderIsReady() const { return readerReady_; }
00601
00609 void setReaderIsReady(bool rdy = true)
00610 {
00611 readyTime_ = std::chrono::steady_clock::now();
00612 readerReady_ = rdy;
00613 }
00614
00619 std::chrono::steady_clock::time_point getReadyTime() const { return readyTime_; }
00620
00621 private:
00622 typedef std::lock_guard<std::mutex> LockType;
00623 typedef std::unique_lock<std::mutex> WaitLockType;
00624
00625 mutable std::mutex protectElements_;
00626 mutable std::condition_variable queueNotEmpty_;
00627 mutable std::condition_variable queueNotFull_;
00628
00629 std::chrono::steady_clock::time_point readyTime_;
00630 bool readerReady_;
00631 SequenceType elements_;
00632 SizeType capacity_;
00633 SizeType size_;
00634
00635
00636
00637
00638
00639
00640
00641 detail::MemoryType memory_;
00642 detail::MemoryType used_;
00643 size_t elementsDropped_;
00644
00645
00646
00647
00648
00649
00650
00651
00652
00653
00654
00655 bool insertIfPossible(T const& item);
00656
00670 bool removeHeadIfPossible(ValueType& item);
00671
00683 void removeHead(ValueType& item);
00684
00685 void assignItem(T& item, const T& element);
00686
00687 void assignItem(std::pair<T, size_t>& item, const T& element);
00688
00689
00690
00691
00692 bool isFull() const;
00693
00694
00695
00696
00697
00698 ConcurrentQueue(ConcurrentQueue<T, EnqPolicy> const&) = delete;
00699
00700 ConcurrentQueue& operator=(ConcurrentQueue<T, EnqPolicy> const&) = delete;
00701 };
00702
00703
00704
00705
00706
00707 template <class T, class EnqPolicy>
00708 ConcurrentQueue<T, EnqPolicy>::ConcurrentQueue
00709 (
00710 SizeType maxSize,
00711 detail::MemoryType maxMemory
00712 ) :
00713 protectElements_()
00714 , readyTime_(std::chrono::steady_clock::now())
00715 , readerReady_(false)
00716 , elements_()
00717 , capacity_(maxSize)
00718 , size_(0)
00719 , memory_(maxMemory)
00720 , used_(0)
00721 , elementsDropped_(0) {}
00722
00723 template <class T, class EnqPolicy>
00724 ConcurrentQueue<T, EnqPolicy>::~ConcurrentQueue()
00725 {
00726 LockType lock(protectElements_);
00727 elements_.clear();
00728 size_ = 0;
00729 used_ = 0;
00730 elementsDropped_ = 0;
00731 }
00732
00733
00734
00735
00736 template <class T, class EnqPolicy>
00737 typename EnqPolicy::ReturnType ConcurrentQueue<T, EnqPolicy>::enqNowait(T const& item)
00738 {
00739 TLOG(12,"ConcurrentQueue") << "enqNowait enter size=" << size_ << " capacity=" << capacity_ << " used=" << used_ << " memory=" << memory_ ;
00740 LockType lock(protectElements_);
00741 auto retval = EnqPolicy::doEnq(item, elements_, size_, capacity_, used_, memory_,
00742 elementsDropped_, queueNotEmpty_);
00743 TLOG(12,"ConcurrentQueue") << "enqNowait returning " << retval ;
00744 return retval;
00745 }
00746
00747 template <class T, class EnqPolicy>
00748 void ConcurrentQueue<T, EnqPolicy>::enqWait(T const& item)
00749 {
00750 TLOG(13,"ConcurrentQueue") << "enqWait enter" ;
00751 WaitLockType lock(protectElements_);
00752 while (isFull()) { queueNotFull_.wait(lock); }
00753 EnqPolicy::doInsert(item, elements_, size_,
00754 detail::memoryUsage(item), used_, queueNotEmpty_);
00755 TLOG(13,"ConcurrentQueue") << "enqWait returning" ;
00756 }
00757
00758 template <class T, class EnqPolicy>
00759 bool ConcurrentQueue<T, EnqPolicy>::enqTimedWait(T const& item, detail::seconds const& waitTime)
00760 {
00761 TLOG(14,"ConcurrentQueue") << "ConcurrentQueue<T,EnqPolicy>::enqTimedWait enter with waitTime=" << std::chrono::duration_cast<std::chrono::milliseconds>(waitTime).count() << " ms size=" << size_
00762 << " capacity=" << capacity_ << " used=" << used_ << " memory=" << memory_ ;
00763 WaitLockType lock(protectElements_);
00764 if (isFull())
00765 {
00766 queueNotFull_.wait_for(lock, waitTime);
00767 }
00768 bool retval = insertIfPossible(item);
00769 TLOG(14,"ConcurrentQueue") << "ConcurrentQueue<T,EnqPolicy>::enqTimedWait returning " << retval ;
00770 return retval;
00771 }
00772
00773
00774
00775
00776 template <class T, class EnqPolicy>
00777 bool ConcurrentQueue<T, EnqPolicy>::deqNowait(ValueType& item)
00778 {
00779 TLOG(15,"ConcurrentQueue") << "ConcurrentQueue<T, EnqPolicy>::deqNowait enter" ;
00780 LockType lock(protectElements_);
00781 bool retval = removeHeadIfPossible(item);
00782 TLOG(15,"ConcurrentQueue") << "ConcurrentQueue<T, EnqPolicy>::deqNowait returning " << retval ;
00783 return retval;
00784 }
00785
00786 template <class T, class EnqPolicy>
00787 void ConcurrentQueue<T, EnqPolicy>::deqWait(ValueType& item)
00788 {
00789 TLOG(16,"ConcurrentQueue") << "ConcurrentQueue<T, EnqPolicy>::deqWait enter" ;
00790 WaitLockType lock(protectElements_);
00791 while (size_ == 0) { queueNotEmpty_.wait(lock); }
00792 removeHead(item);
00793 TLOG(16,"ConcurrentQueue") << "ConcurrentQueue<T, EnqPolicy>::deqWait returning" ;
00794 }
00795
00796 template <class T, class EnqPolicy>
00797 bool ConcurrentQueue<T, EnqPolicy>::deqTimedWait(ValueType& item, detail::seconds const& waitTime)
00798 {
00799 TLOG(17,"ConcurrentQueue") << "ConcurrentQueue<T, EnqPolicy>::deqTimedWait enter with waitTime=" << std::chrono::duration_cast<std::chrono::milliseconds>(waitTime).count() << " ms size=" << size_ ;
00800 WaitLockType lock(protectElements_);
00801 if (size_ == 0)
00802 {
00803 queueNotEmpty_.wait_for(lock, waitTime);
00804 }
00805 bool retval = removeHeadIfPossible(item);
00806 TLOG(17,"ConcurrentQueue") << "ConcurrentQueue<T, EnqPolicy>::deqTimedWait returning " << retval << " size=" << size_ ;
00807 return retval;
00808 }
00809
00810
00811 template <class T, class EnqPolicy>
00812 bool
00813 ConcurrentQueue<T, EnqPolicy>::empty() const
00814 {
00815
00816 return size_ == 0;
00817 }
00818
00819 template <class T, class EnqPolicy>
00820 bool
00821 ConcurrentQueue<T, EnqPolicy>::full() const
00822 {
00823 LockType lock(protectElements_);
00824 return isFull();
00825 }
00826
00827 template <class T, class EnqPolicy>
00828 typename ConcurrentQueue<T, EnqPolicy>::SizeType
00829 ConcurrentQueue<T, EnqPolicy>::size() const
00830 {
00831
00832 return size_;
00833 }
00834
00835 template <class T, class EnqPolicy>
00836 typename ConcurrentQueue<T, EnqPolicy>::SizeType
00837 ConcurrentQueue<T, EnqPolicy>::capacity() const
00838 {
00839
00840 return capacity_;
00841 }
00842
00843 template <class T, class EnqPolicy>
00844 bool
00845 ConcurrentQueue<T, EnqPolicy>::setCapacity(SizeType newcapacity)
00846 {
00847 LockType lock(protectElements_);
00848 bool isEmpty = (size_ == 0);
00849 if (isEmpty) { capacity_ = newcapacity; }
00850 return isEmpty;
00851 }
00852
00853 template <class T, class EnqPolicy>
00854 detail::MemoryType
00855 ConcurrentQueue<T, EnqPolicy>::used() const
00856 {
00857
00858 return used_;
00859 }
00860
00861 template <class T, class EnqPolicy>
00862 detail::MemoryType
00863 ConcurrentQueue<T, EnqPolicy>::memory() const
00864 {
00865
00866 return memory_;
00867 }
00868
00869 template <class T, class EnqPolicy>
00870 bool
00871 ConcurrentQueue<T, EnqPolicy>::setMemory(detail::MemoryType newmemory)
00872 {
00873 LockType lock(protectElements_);
00874 bool isEmpty = (size_ == 0);
00875 if (isEmpty) { memory_ = newmemory; }
00876 return isEmpty;
00877 }
00878
00879 template <class T, class EnqPolicy>
00880 typename ConcurrentQueue<T, EnqPolicy>::SizeType
00881 ConcurrentQueue<T, EnqPolicy>::clear()
00882 {
00883 LockType lock(protectElements_);
00884 SizeType clearedEvents = size_;
00885 elementsDropped_ += size_;
00886 elements_.clear();
00887 size_ = 0;
00888 used_ = 0;
00889 return clearedEvents;
00890 }
00891
00892 template <class T, class EnqPolicy>
00893 void
00894 ConcurrentQueue<T, EnqPolicy>::addExternallyDroppedEvents(SizeType n)
00895 {
00896 LockType lock(protectElements_);
00897 elementsDropped_ += n;
00898 }
00899
00900
00901
00902
00903
00904 template <class T, class EnqPolicy>
00905 bool
00906 ConcurrentQueue<T, EnqPolicy>::insertIfPossible(T const& item)
00907 {
00908 if (isFull())
00909 {
00910 ++elementsDropped_;
00911 return false;
00912 }
00913 else
00914 {
00915 EnqPolicy::doInsert(item, elements_, size_,
00916 detail::memoryUsage(item), used_, queueNotEmpty_);
00917 return true;
00918 }
00919 }
00920
00921 template <class T, class EnqPolicy>
00922 bool
00923 ConcurrentQueue<T, EnqPolicy>::removeHeadIfPossible(ValueType& item)
00924 {
00925 if (size_ == 0) { return false; }
00926 removeHead(item);
00927 return true;
00928 }
00929
00930 template <class T, class EnqPolicy>
00931 void
00932 ConcurrentQueue<T, EnqPolicy>::removeHead(ValueType& item)
00933 {
00934 SequenceType holder;
00935
00936 holder.splice(holder.begin(), elements_, elements_.begin());
00937
00938 --size_;
00939 queueNotFull_.notify_one();
00940 assignItem(item, holder.front());
00941 used_ -= detail::memoryUsage(item);
00942 }
00943
00944 template <class T, class EnqPolicy>
00945 void
00946 ConcurrentQueue<T, EnqPolicy>::assignItem(T& item, const T& element)
00947 {
00948 item = element;
00949 }
00950
00951 template <class T, class EnqPolicy>
00952 void
00953 ConcurrentQueue<T, EnqPolicy>::assignItem(std::pair<T, size_t>& item, const T& element)
00954 {
00955 item.first = element;
00956 item.second = elementsDropped_;
00957 elementsDropped_ = 0;
00958 }
00959
00960 template <class T, class EnqPolicy>
00961 bool
00962 ConcurrentQueue<T, EnqPolicy>::isFull() const
00963 {
00964 if (size_ >= capacity_ || used_ >= memory_) { return true; }
00965 return false;
00966 }
00967 }
00968
00969 #endif