$treeview $search $mathjax $extrastylesheet
artdaq_core
v3_04_20a
$projectbrief
|
$projectbrief
|
$searchbox |
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> // debugging 00011 #include "tracemf.h" // TRACE - note: no #define TRACE_NAME in .hh files 00012 00013 #include <chrono> 00014 #include <condition_variable> 00015 #include <mutex> 00016 #include <type_traits> 00017 00018 00019 // #include <boost/date_time/posix_time/posix_time_types.hpp> 00020 // #include <boost/utility/enable_if.hpp> 00021 // #include <boost/thread/condition.hpp> 00022 // #include <boost/thread/mutex.hpp> 00023 // #include <boost/thread/xtime.hpp> 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 }// end namespace detail 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 // Move the item out of elements in a manner that will not throw. 00318 holder.splice(holder.begin(), elements, elements.begin()); 00319 // Record the change in the length of elements. 00320 --size; 00321 used -= detail::memoryUsage(holder.front()); 00322 ++elementsRemoved; 00323 } 00324 if (size < capacity && used + itemSize <= memory) 00325 // we succeeded to make enough room for the new element 00326 { 00327 doInsert(item, elements, size, itemSize, used, nonempty); 00328 } 00329 else 00330 { 00331 // we cannot add the new element 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 N.B.: we rely on SizeType *not* being some synthesized large 00636 type, so that reading the value is an atomic action, as is 00637 incrementing or decrementing the value. We do *not* assume that 00638 there is any atomic getAndIncrement or getAndDecrement 00639 operation. 00640 */ 00641 detail::MemoryType memory_; 00642 detail::MemoryType used_; 00643 size_t elementsDropped_; 00644 00645 /* 00646 These private member functions assume that whatever locks 00647 necessary for safe operation have already been obtained. 00648 */ 00649 00650 /* 00651 Insert the given item into the list, if it is not already full, 00652 and increment size. Return true if the item is inserted, and 00653 false if not. 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 Return false if the queue can accept new entries. 00691 */ 00692 bool isFull() const; 00693 00694 /* 00695 These functions are declared private and not implemented to 00696 prevent their use. 00697 */ 00698 ConcurrentQueue(ConcurrentQueue<T, EnqPolicy> const&) = delete; 00699 00700 ConcurrentQueue& operator=(ConcurrentQueue<T, EnqPolicy> const&) = delete; 00701 }; 00702 00703 //------------------------------------------------------------------ 00704 // Implementation follows 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 // enqueue methods - 3 - enqNowait, enqWait, enqTimedWait 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 // dequeue methods - 3 - deqNowait, deqWait, deqTimedWait 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 // No lock is necessary: the read is atomic. 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 // No lock is necessary: the read is atomic. 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 // No lock is necessary: the read is atomic. 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 // No lock is necessary: the read is atomic. 00858 return used_; 00859 } 00860 00861 template <class T, class EnqPolicy> 00862 detail::MemoryType 00863 ConcurrentQueue<T, EnqPolicy>::memory() const 00864 { 00865 // No lock is necessary: the read is atomic. 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 // Private member functions 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 // Move the item out of elements_ in a manner that will not throw. 00936 holder.splice(holder.begin(), elements_, elements_.begin()); 00937 // Record the change in the length of elements_. 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 } // namespace daqrate 00968 00969 #endif /* artdaq_core_Core_ConcurrentQueue_hh */