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 "trace.h"
00012
00013 #include <chrono>
00014 #include <condition_variable>
00015 #include <mutex>
00016 #include <type_traits>
00017
00018
00019
00020
00021
00022
00023
00024 namespace artdaq
00025 {
00050 namespace detail
00051 {
00061 typedef std::chrono::duration<double> seconds;
00062
00063 typedef size_t MemoryType;
00064
00070 template <typename T>
00071 class hasMemoryUsed
00072 {
00073 typedef char TrueType;
00074
00075 struct FalseType
00076 {
00077 TrueType _[2];
00078 };
00079
00080 template <MemoryType(T::*)() const>
00081 struct TestConst;
00082
00083 template <typename C>
00084 static TrueType test(TestConst<&C::memoryUsed>*) { return 0; }
00085
00086 template <typename C>
00087 static FalseType test(...)
00088 {
00089 return {};
00090 }
00091
00092 public:
00093
00099 static const bool value = (sizeof(test<T>(nullptr)) == sizeof(TrueType));
00100 };
00101
00108 template <typename T>
00109 MemoryType
00110 memoryUsage(const std::pair<T, size_t>& t)
00111 {
00112 MemoryType usage(0UL);
00113 try
00114 {
00115 usage = t.first.memoryUsed();
00116 }
00117 catch (...) {}
00118 return usage;
00119 }
00120
00127 template <typename T>
00128 typename std::enable_if<hasMemoryUsed<T>::value, MemoryType>::type
00129 memoryUsage(const T& t)
00130 {
00131 MemoryType usage(0UL);
00132 try
00133 {
00134 usage = t.memoryUsed();
00135 }
00136 catch (...) {}
00137 return usage;
00138 }
00139
00146 template <typename T>
00147 typename std::enable_if<!hasMemoryUsed<T>::value, MemoryType>::type
00148 memoryUsage(const T& t)
00149 {
00150 return sizeof(t);
00151 }
00152 }
00153
00154
00159 template <class T>
00160 struct FailIfFull
00161 {
00162 typedef bool ReturnType;
00163
00164 typedef T ValueType;
00165 typedef std::list<T> SequenceType;
00166 typedef typename SequenceType::size_type SizeType;
00167
00171 static struct QueueIsFull : public std::exception
00172 {
00177 virtual const char* what() const throw()
00178 {
00179 return "Cannot add item to a full queue";
00180 }
00181 } queueIsFull;
00182
00192 static void doInsert
00193 (
00194 T const& item,
00195 SequenceType& elements,
00196 SizeType& size,
00197 detail::MemoryType const& itemSize,
00198 detail::MemoryType& used,
00199 std::condition_variable& nonempty
00200 )
00201 {
00202 elements.push_back(item);
00203 ++size;
00204 used += itemSize;
00205 nonempty.notify_one();
00206 }
00207
00221 static ReturnType doEnq
00222 (
00223 T const& item,
00224 SequenceType& elements,
00225 SizeType& size,
00226 SizeType& capacity,
00227 detail::MemoryType& used,
00228 detail::MemoryType& memory,
00229 size_t& elementsDropped,
00230 std::condition_variable& nonempty
00231 )
00232 {
00233 detail::MemoryType itemSize = detail::memoryUsage(item);
00234 if (size >= capacity || used + itemSize > memory)
00235 {
00236 ++elementsDropped;
00237 throw queueIsFull;
00238 }
00239 else
00240 {
00241 doInsert(item, elements, size, itemSize, used, nonempty);
00242 }
00243 return true;
00244 }
00245 };
00246
00247 template <typename T>
00248 typename FailIfFull<T>::QueueIsFull FailIfFull<T>::queueIsFull{};
00249
00254 template <class T>
00255 struct KeepNewest
00256 {
00257 typedef std::pair<T, size_t> ValueType;
00258 typedef std::list<T> SequenceType;
00259 typedef typename SequenceType::size_type SizeType;
00260 typedef SizeType ReturnType;
00261
00271 static void doInsert
00272 (
00273 T const& item,
00274 SequenceType& elements,
00275 SizeType& size,
00276 detail::MemoryType const& itemSize,
00277 detail::MemoryType& used,
00278 std::condition_variable& nonempty
00279 )
00280 {
00281 elements.push_back(item);
00282 ++size;
00283 used += itemSize;
00284 nonempty.notify_one();
00285 }
00286
00299 static ReturnType doEnq
00300 (
00301 T const& item,
00302 SequenceType& elements,
00303 SizeType& size,
00304 SizeType& capacity,
00305 detail::MemoryType& used,
00306 detail::MemoryType& memory,
00307 size_t& elementsDropped,
00308 std::condition_variable& nonempty
00309 )
00310 {
00311 SizeType elementsRemoved(0);
00312 detail::MemoryType itemSize = detail::memoryUsage(item);
00313 while ((size == capacity || used + itemSize > memory) && !elements.empty())
00314 {
00315 SequenceType holder;
00316
00317 holder.splice(holder.begin(), elements, elements.begin());
00318
00319 --size;
00320 used -= detail::memoryUsage(holder.front());
00321 ++elementsRemoved;
00322 }
00323 if (size < capacity && used + itemSize <= memory)
00324
00325 {
00326 doInsert(item, elements, size, itemSize, used, nonempty);
00327 }
00328 else
00329 {
00330
00331 ++elementsRemoved;
00332 }
00333 elementsDropped += elementsRemoved;
00334 return elementsRemoved;
00335 }
00336 };
00337
00342 template <class T>
00343 struct RejectNewest
00344 {
00345 typedef std::pair<T, size_t> ValueType;
00346 typedef std::list<T> SequenceType;
00347 typedef typename SequenceType::size_type SizeType;
00348 typedef SizeType ReturnType;
00349
00359 static void doInsert
00360 (
00361 T const& item,
00362 SequenceType& elements,
00363 SizeType& size,
00364 detail::MemoryType const& itemSize,
00365 detail::MemoryType& used,
00366 std::condition_variable& nonempty
00367 )
00368 {
00369 elements.push_back(item);
00370 ++size;
00371 used += itemSize;
00372 nonempty.notify_one();
00373 }
00374
00387 static ReturnType doEnq
00388 (
00389 T const& item,
00390 SequenceType& elements,
00391 SizeType& size,
00392 SizeType& capacity,
00393 detail::MemoryType& used,
00394 detail::MemoryType& memory,
00395 size_t& elementsDropped,
00396 std::condition_variable& nonempty
00397 )
00398 {
00399 detail::MemoryType itemSize = detail::memoryUsage(item);
00400 if (size < capacity && used + itemSize <= memory)
00401 {
00402 doInsert(item, elements, size, itemSize, used, nonempty);
00403 return 0;
00404 }
00405 ++elementsDropped;
00406 return 1;
00407 }
00408 };
00409
00413 template <class T, class EnqPolicy = FailIfFull<T>>
00414 class ConcurrentQueue
00415 {
00416 public:
00417 typedef typename EnqPolicy::ValueType ValueType;
00418 typedef typename EnqPolicy::SequenceType SequenceType;
00419 typedef typename SequenceType::size_type SizeType;
00420
00427 explicit ConcurrentQueue
00428 (
00429 SizeType maxSize = std::numeric_limits<SizeType>::max(),
00430 detail::MemoryType maxMemory = std::numeric_limits<detail::MemoryType>::max()
00431 );
00432
00439 ~ConcurrentQueue();
00440
00457 typename EnqPolicy::ReturnType enqNowait(T const& item);
00458
00467 void enqWait(T const& item);
00468
00481 bool enqTimedWait(T const& item, detail::seconds const& wait);
00482
00495 bool deqNowait(ValueType& item);
00496
00507 void deqWait(ValueType& item);
00508
00522 bool deqTimedWait(ValueType& item, detail::seconds const& wait);
00523
00528 bool empty() const;
00529
00534 bool full() const;
00535
00541 SizeType size() const;
00542
00548 SizeType capacity() const;
00549
00557 bool setCapacity(SizeType capacity);
00558
00563 detail::MemoryType used() const;
00564
00570 detail::MemoryType memory() const;
00571
00580 bool setMemory(detail::MemoryType maxMemory);
00581
00587 SizeType clear();
00588
00593 void addExternallyDroppedEvents(SizeType dropped);
00594
00599 bool queueReaderIsReady() const { return readerReady_; }
00600
00608 void setReaderIsReady(bool rdy = true)
00609 {
00610 readyTime_ = std::chrono::steady_clock::now();
00611 readerReady_ = rdy;
00612 }
00613
00618 std::chrono::steady_clock::time_point getReadyTime() const { return readyTime_; }
00619
00620 private:
00621 typedef std::lock_guard<std::mutex> LockType;
00622 typedef std::unique_lock<std::mutex> WaitLockType;
00623
00624 mutable std::mutex protectElements_;
00625 mutable std::condition_variable queueNotEmpty_;
00626 mutable std::condition_variable queueNotFull_;
00627
00628 std::chrono::steady_clock::time_point readyTime_;
00629 bool readerReady_;
00630 SequenceType elements_;
00631 SizeType capacity_;
00632 SizeType size_;
00633
00634
00635
00636
00637
00638
00639
00640 detail::MemoryType memory_;
00641 detail::MemoryType used_;
00642 size_t elementsDropped_;
00643
00644
00645
00646
00647
00648
00649
00650
00651
00652
00653
00654 bool insertIfPossible(T const& item);
00655
00669 bool removeHeadIfPossible(ValueType& item);
00670
00682 void removeHead(ValueType& item);
00683
00684 void assignItem(T& item, const T& element);
00685
00686 void assignItem(std::pair<T, size_t>& item, const T& element);
00687
00688
00689
00690
00691 bool isFull() const;
00692
00693
00694
00695
00696
00697 ConcurrentQueue(ConcurrentQueue<T, EnqPolicy> const&) = delete;
00698
00699 ConcurrentQueue& operator=(ConcurrentQueue<T, EnqPolicy> const&) = delete;
00700 };
00701
00702
00703
00704
00705
00706 template <class T, class EnqPolicy>
00707 ConcurrentQueue<T, EnqPolicy>::ConcurrentQueue
00708 (
00709 SizeType maxSize,
00710 detail::MemoryType maxMemory
00711 ) :
00712 protectElements_()
00713 , readyTime_(std::chrono::steady_clock::now())
00714 , readerReady_(false)
00715 , elements_()
00716 , capacity_(maxSize)
00717 , size_(0)
00718 , memory_(maxMemory)
00719 , used_(0)
00720 , elementsDropped_(0) {}
00721
00722 template <class T, class EnqPolicy>
00723 ConcurrentQueue<T, EnqPolicy>::~ConcurrentQueue()
00724 {
00725 LockType lock(protectElements_);
00726 elements_.clear();
00727 size_ = 0;
00728 used_ = 0;
00729 elementsDropped_ = 0;
00730 }
00731
00732
00733
00734
00735 template <class T, class EnqPolicy>
00736 typename EnqPolicy::ReturnType ConcurrentQueue<T, EnqPolicy>::enqNowait(T const& item)
00737 {
00738 TRACE(12, "ConcurrentQueue<T,EnqPolicy>::enqNowait enter size=%zu capacity=%zu used=%zu memory=%zu", size_, capacity_, used_, memory_);
00739 LockType lock(protectElements_);
00740 auto retval = EnqPolicy::doEnq(item, elements_, size_, capacity_, used_, memory_,
00741 elementsDropped_, queueNotEmpty_);
00742 TRACE(12, "ConcurrentQueue<T,EnqPolicy>::enqNowait returning %zu", (SizeType)retval);
00743 return retval;
00744 }
00745
00746 template <class T, class EnqPolicy>
00747 void ConcurrentQueue<T, EnqPolicy>::enqWait(T const& item)
00748 {
00749 TRACE(12, "ConcurrentQueue<T,EnqPolicy>::enqWait enter");
00750 WaitLockType lock(protectElements_);
00751 while (isFull()) { queueNotFull_.wait(lock); }
00752 EnqPolicy::doInsert(item, elements_, size_,
00753 detail::memoryUsage(item), used_, queueNotEmpty_);
00754 TRACE(12, "ConcurrentQueue<T,EnqPolicy>::enqWait returning");
00755 }
00756
00757 template <class T, class EnqPolicy>
00758 bool ConcurrentQueue<T, EnqPolicy>::enqTimedWait(T const& item, detail::seconds const& waitTime)
00759 {
00760 TRACE(12, "ConcurrentQueue<T,EnqPolicy>::enqTimedWait enter with waitTime=%ld ms size=%zu capacity=%zu used=%zu memory=%zu"
00761 , std::chrono::duration_cast<std::chrono::milliseconds>(waitTime).count(), size_, capacity_, used_, memory_);
00762 WaitLockType lock(protectElements_);
00763 if (isFull())
00764 {
00765 queueNotFull_.wait_for(lock, waitTime);
00766 }
00767 bool retval = insertIfPossible(item);
00768 TRACE(12, "ConcurrentQueue<T,EnqPolicy>::enqTimedWait returning %d", retval);
00769 return retval;
00770 }
00771
00772
00773
00774
00775 template <class T, class EnqPolicy>
00776 bool ConcurrentQueue<T, EnqPolicy>::deqNowait(ValueType& item)
00777 {
00778 TRACE(12, "ConcurrentQueue<T, EnqPolicy>::deqNowait enter");
00779 LockType lock(protectElements_);
00780 bool retval = removeHeadIfPossible(item);
00781 TRACE(12, "ConcurrentQueue<T, EnqPolicy>::deqNowait returning %d", retval);
00782 return retval;
00783 }
00784
00785 template <class T, class EnqPolicy>
00786 void ConcurrentQueue<T, EnqPolicy>::deqWait(ValueType& item)
00787 {
00788 TRACE(12, "ConcurrentQueue<T, EnqPolicy>::deqWait enter");
00789 WaitLockType lock(protectElements_);
00790 while (size_ == 0) { queueNotEmpty_.wait(lock); }
00791 removeHead(item);
00792 TRACE(12, "ConcurrentQueue<T, EnqPolicy>::deqWait returning");
00793 }
00794
00795 template <class T, class EnqPolicy>
00796 bool ConcurrentQueue<T, EnqPolicy>::deqTimedWait(ValueType& item, detail::seconds const& waitTime)
00797 {
00798 TRACE(12, "ConcurrentQueue<T, EnqPolicy>::deqTimedWait enter with waitTime=%ld ms size=%zu"
00799 , std::chrono::duration_cast<std::chrono::milliseconds>(waitTime).count(), size_);
00800 WaitLockType lock(protectElements_);
00801 if (size_ == 0)
00802 {
00803 queueNotEmpty_.wait_for(lock, waitTime);
00804 }
00805 bool retval = removeHeadIfPossible(item);
00806 TRACE(12, "ConcurrentQueue<T, EnqPolicy>::deqTimedWait returning %d size=%zu", retval, 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
00970
00972