$treeview $search $mathjax $extrastylesheet
artdaq_core
v3_06_01
$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 // #include <boost/date_time/posix_time/posix_time_types.hpp> 00019 // #include <boost/utility/enable_if.hpp> 00020 // #include <boost/thread/condition.hpp> 00021 // #include <boost/thread/mutex.hpp> 00022 // #include <boost/thread/xtime.hpp> 00023 00024 namespace artdaq { 00048 namespace detail { 00058 typedef std::chrono::duration<double> seconds; 00059 00060 typedef size_t MemoryType; 00061 00067 template<typename T> 00068 class hasMemoryUsed 00069 { 00070 typedef char TrueType; 00071 00072 struct FalseType 00073 { 00074 TrueType _[2]; 00075 }; 00076 00077 template<MemoryType (T::*)() const> 00078 struct TestConst; 00079 00080 template<typename C> 00081 static TrueType test(TestConst<&C::memoryUsed>*) 00082 { 00083 return 0; 00084 } 00085 00086 template<typename C> 00087 static FalseType test(...) 00088 { 00089 return {}; 00090 } 00091 00092 public: 00098 static const bool value = (sizeof(test<T>(nullptr)) == sizeof(TrueType)); // NOLINT(cert-err58-cpp) 00099 }; 00100 00107 template<typename T> 00108 MemoryType 00109 memoryUsage(const std::pair<T, size_t>& t) 00110 { 00111 MemoryType usage(0UL); 00112 try 00113 { 00114 usage = t.first.memoryUsed(); 00115 } 00116 catch (...) 00117 {} 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 {} 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 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 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 }; 00182 00192 static void doInsert( 00193 T const& item, 00194 SequenceType& elements, 00195 SizeType& size, 00196 detail::MemoryType const& itemSize, 00197 detail::MemoryType& used, 00198 std::condition_variable& nonempty) 00199 { 00200 elements.push_back(item); 00201 ++size; 00202 used += itemSize; 00203 nonempty.notify_one(); 00204 } 00205 00219 static ReturnType doEnq( 00220 T const& item, 00221 SequenceType& elements, 00222 SizeType& size, 00223 SizeType& capacity, 00224 detail::MemoryType& used, 00225 detail::MemoryType& memory, 00226 size_t& elementsDropped, 00227 std::condition_variable& nonempty) 00228 { 00229 detail::MemoryType itemSize = detail::memoryUsage(item); 00230 if (size >= capacity || used + itemSize > memory) 00231 { 00232 ++elementsDropped; 00233 throw QueueIsFull(); 00234 } 00235 else 00236 { 00237 doInsert(item, elements, size, itemSize, used, nonempty); 00238 } 00239 return true; 00240 } 00241 }; 00242 00247 template<class T> 00248 struct KeepNewest 00249 { 00250 typedef std::pair<T, size_t> ValueType; 00251 typedef std::list<T> SequenceType; 00252 typedef typename SequenceType::size_type SizeType; 00253 typedef SizeType ReturnType; 00254 00264 static void doInsert( 00265 T const& item, 00266 SequenceType& elements, 00267 SizeType& size, 00268 detail::MemoryType const& itemSize, 00269 detail::MemoryType& used, 00270 std::condition_variable& nonempty) 00271 { 00272 elements.push_back(item); 00273 ++size; 00274 used += itemSize; 00275 nonempty.notify_one(); 00276 } 00277 00290 static ReturnType doEnq( 00291 T const& item, 00292 SequenceType& elements, 00293 SizeType& size, 00294 SizeType& capacity, 00295 detail::MemoryType& used, 00296 detail::MemoryType& memory, 00297 size_t& elementsDropped, 00298 std::condition_variable& nonempty) 00299 { 00300 SizeType elementsRemoved(0); 00301 detail::MemoryType itemSize = detail::memoryUsage(item); 00302 while ((size == capacity || used + itemSize > memory) && !elements.empty()) 00303 { 00304 SequenceType holder; 00305 // Move the item out of elements in a manner that will not throw. 00306 holder.splice(holder.begin(), elements, elements.begin()); 00307 // Record the change in the length of elements. 00308 --size; 00309 used -= detail::memoryUsage(holder.front()); 00310 ++elementsRemoved; 00311 } 00312 if (size < capacity && used + itemSize <= memory) 00313 // we succeeded to make enough room for the new element 00314 { 00315 doInsert(item, elements, size, itemSize, used, nonempty); 00316 } 00317 else 00318 { 00319 // we cannot add the new element 00320 ++elementsRemoved; 00321 } 00322 elementsDropped += elementsRemoved; 00323 return elementsRemoved; 00324 } 00325 }; 00326 00331 template<class T> 00332 struct RejectNewest 00333 { 00334 typedef std::pair<T, size_t> ValueType; 00335 typedef std::list<T> SequenceType; 00336 typedef typename SequenceType::size_type SizeType; 00337 typedef SizeType ReturnType; 00338 00348 static void doInsert( 00349 T const& item, 00350 SequenceType& elements, 00351 SizeType& size, 00352 detail::MemoryType const& itemSize, 00353 detail::MemoryType& used, 00354 std::condition_variable& nonempty) 00355 { 00356 elements.push_back(item); 00357 ++size; 00358 used += itemSize; 00359 nonempty.notify_one(); 00360 } 00361 00374 static ReturnType doEnq( 00375 T const& item, 00376 SequenceType& elements, 00377 SizeType& size, 00378 SizeType& capacity, 00379 detail::MemoryType& used, 00380 detail::MemoryType& memory, 00381 size_t& elementsDropped, 00382 std::condition_variable& nonempty) 00383 { 00384 detail::MemoryType itemSize = detail::memoryUsage(item); 00385 if (size < capacity && used + itemSize <= memory) 00386 { 00387 doInsert(item, elements, size, itemSize, used, nonempty); 00388 return 0; 00389 } 00390 ++elementsDropped; 00391 return 1; 00392 } 00393 }; 00394 00398 template<class T, class EnqPolicy = FailIfFull<T>> 00399 class ConcurrentQueue 00400 { 00401 public: 00402 typedef typename EnqPolicy::ValueType ValueType; 00403 typedef typename EnqPolicy::SequenceType SequenceType; 00404 typedef typename SequenceType::size_type SizeType; 00405 00412 explicit ConcurrentQueue( 00413 SizeType maxSize = std::numeric_limits<SizeType>::max(), 00414 detail::MemoryType maxMemory = std::numeric_limits<detail::MemoryType>::max()); 00415 00422 ~ConcurrentQueue(); 00423 00440 typename EnqPolicy::ReturnType enqNowait(T const& item); 00441 00450 void enqWait(T const& item); 00451 00464 bool enqTimedWait(T const& item, detail::seconds const& wait); 00465 00478 bool deqNowait(ValueType& item); 00479 00490 void deqWait(ValueType& item); 00491 00505 bool deqTimedWait(ValueType& item, detail::seconds const& wait); 00506 00511 bool empty() const; 00512 00517 bool full() const; 00518 00524 SizeType size() const; 00525 00531 SizeType capacity() const; 00532 00540 bool setCapacity(SizeType capacity); 00541 00546 detail::MemoryType used() const; 00547 00553 detail::MemoryType memory() const; 00554 00563 bool setMemory(detail::MemoryType maxMemory); 00564 00570 SizeType clear(); 00571 00576 void addExternallyDroppedEvents(SizeType dropped); 00577 00582 bool queueReaderIsReady() const { return readerReady_; } 00583 00591 void setReaderIsReady(bool rdy = true) 00592 { 00593 readyTime_ = std::chrono::steady_clock::now(); 00594 readerReady_ = rdy; 00595 } 00596 00601 std::chrono::steady_clock::time_point getReadyTime() const { return readyTime_; } 00602 00603 private: 00604 typedef std::lock_guard<std::mutex> LockType; 00605 typedef std::unique_lock<std::mutex> WaitLockType; 00606 00607 mutable std::mutex protectElements_; 00608 mutable std::condition_variable queueNotEmpty_; 00609 mutable std::condition_variable queueNotFull_; 00610 00611 std::chrono::steady_clock::time_point readyTime_; 00612 bool readerReady_{false}; 00613 SequenceType elements_; 00614 SizeType capacity_; 00615 SizeType size_; 00616 /* 00617 N.B.: we rely on SizeType *not* being some synthesized large 00618 type, so that reading the value is an atomic action, as is 00619 incrementing or decrementing the value. We do *not* assume that 00620 there is any atomic getAndIncrement or getAndDecrement 00621 operation. 00622 */ 00623 detail::MemoryType memory_; 00624 detail::MemoryType used_{0}; 00625 size_t elementsDropped_{0}; 00626 00627 /* 00628 These private member functions assume that whatever locks 00629 necessary for safe operation have already been obtained. 00630 */ 00631 00632 /* 00633 Insert the given item into the list, if it is not already full, 00634 and increment size. Return true if the item is inserted, and 00635 false if not. 00636 */ 00637 bool insertIfPossible(T const& item); 00638 00652 bool removeHeadIfPossible(ValueType& item); 00653 00665 void removeHead(ValueType& item); 00666 00667 void assignItem(T& item, const T& element); 00668 00669 void assignItem(std::pair<T, size_t>& item, const T& element); 00670 00671 /* 00672 Return false if the queue can accept new entries. 00673 */ 00674 bool isFull() const; 00675 00676 /* 00677 These functions are declared private and not implemented to 00678 prevent their use. 00679 */ 00680 ConcurrentQueue(ConcurrentQueue<T, EnqPolicy> const&) = delete; 00681 00682 ConcurrentQueue& operator=(ConcurrentQueue<T, EnqPolicy> const&) = delete; 00683 ConcurrentQueue(ConcurrentQueue<T, EnqPolicy>&&) = default; 00684 ConcurrentQueue& operator=(ConcurrentQueue<T, EnqPolicy>&&) = default; 00685 }; 00686 00687 //------------------------------------------------------------------ 00688 // Implementation follows 00689 //------------------------------------------------------------------ 00690 00691 template<class T, class EnqPolicy> 00692 ConcurrentQueue<T, EnqPolicy>::ConcurrentQueue( 00693 SizeType maxSize, 00694 detail::MemoryType maxMemory) 00695 : protectElements_() 00696 , readyTime_(std::chrono::steady_clock::now()) 00697 , elements_() 00698 , capacity_(maxSize) 00699 , size_(0) 00700 , memory_(maxMemory) 00701 {} 00702 00703 template<class T, class EnqPolicy> 00704 ConcurrentQueue<T, EnqPolicy>::~ConcurrentQueue() 00705 { 00706 LockType lock(protectElements_); 00707 elements_.clear(); 00708 size_ = 0; 00709 used_ = 0; 00710 elementsDropped_ = 0; 00711 } 00712 00713 // enqueue methods - 3 - enqNowait, enqWait, enqTimedWait 00714 00715 template<class T, class EnqPolicy> 00716 typename EnqPolicy::ReturnType ConcurrentQueue<T, EnqPolicy>::enqNowait(T const& item) 00717 { 00718 TLOG(12, "ConcurrentQueue") << "enqNowait enter size=" << size_ << " capacity=" << capacity_ << " used=" << used_ << " memory=" << memory_; 00719 LockType lock(protectElements_); 00720 auto retval = EnqPolicy::doEnq(item, elements_, size_, capacity_, used_, memory_, 00721 elementsDropped_, queueNotEmpty_); 00722 TLOG(12, "ConcurrentQueue") << "enqNowait returning " << retval; 00723 return retval; 00724 } 00725 00726 template<class T, class EnqPolicy> 00727 void ConcurrentQueue<T, EnqPolicy>::enqWait(T const& item) 00728 { 00729 TLOG(13, "ConcurrentQueue") << "enqWait enter"; 00730 WaitLockType lock(protectElements_); 00731 while (isFull()) { queueNotFull_.wait(lock); } 00732 EnqPolicy::doInsert(item, elements_, size_, 00733 detail::memoryUsage(item), used_, queueNotEmpty_); 00734 TLOG(13, "ConcurrentQueue") << "enqWait returning"; 00735 } 00736 00737 template<class T, class EnqPolicy> 00738 bool ConcurrentQueue<T, EnqPolicy>::enqTimedWait(T const& item, detail::seconds const& waitTime) 00739 { 00740 TLOG(14, "ConcurrentQueue") << "ConcurrentQueue<T,EnqPolicy>::enqTimedWait enter with waitTime=" << std::chrono::duration_cast<std::chrono::milliseconds>(waitTime).count() << " ms size=" << size_ 00741 << " capacity=" << capacity_ << " used=" << used_ << " memory=" << memory_; 00742 WaitLockType lock(protectElements_); 00743 if (isFull()) 00744 { 00745 queueNotFull_.wait_for(lock, waitTime); 00746 } 00747 bool retval = insertIfPossible(item); 00748 TLOG(14, "ConcurrentQueue") << "ConcurrentQueue<T,EnqPolicy>::enqTimedWait returning " << retval; 00749 return retval; 00750 } 00751 00752 // dequeue methods - 3 - deqNowait, deqWait, deqTimedWait 00753 00754 template<class T, class EnqPolicy> 00755 bool ConcurrentQueue<T, EnqPolicy>::deqNowait(ValueType& item) 00756 { 00757 TLOG(15, "ConcurrentQueue") << "ConcurrentQueue<T, EnqPolicy>::deqNowait enter"; 00758 LockType lock(protectElements_); 00759 bool retval = removeHeadIfPossible(item); 00760 TLOG(15, "ConcurrentQueue") << "ConcurrentQueue<T, EnqPolicy>::deqNowait returning " << retval; 00761 return retval; 00762 } 00763 00764 template<class T, class EnqPolicy> 00765 void ConcurrentQueue<T, EnqPolicy>::deqWait(ValueType& item) 00766 { 00767 TLOG(16, "ConcurrentQueue") << "ConcurrentQueue<T, EnqPolicy>::deqWait enter"; 00768 WaitLockType lock(protectElements_); 00769 while (size_ == 0) { queueNotEmpty_.wait(lock); } 00770 removeHead(item); 00771 TLOG(16, "ConcurrentQueue") << "ConcurrentQueue<T, EnqPolicy>::deqWait returning"; 00772 } 00773 00774 template<class T, class EnqPolicy> 00775 bool ConcurrentQueue<T, EnqPolicy>::deqTimedWait(ValueType& item, detail::seconds const& waitTime) 00776 { 00777 TLOG(17, "ConcurrentQueue") << "ConcurrentQueue<T, EnqPolicy>::deqTimedWait enter with waitTime=" << std::chrono::duration_cast<std::chrono::milliseconds>(waitTime).count() << " ms size=" << size_; 00778 WaitLockType lock(protectElements_); 00779 if (size_ == 0) 00780 { 00781 queueNotEmpty_.wait_for(lock, waitTime); 00782 } 00783 bool retval = removeHeadIfPossible(item); 00784 TLOG(17, "ConcurrentQueue") << "ConcurrentQueue<T, EnqPolicy>::deqTimedWait returning " << retval << " size=" << size_; 00785 return retval; 00786 } 00787 00788 template<class T, class EnqPolicy> 00789 bool ConcurrentQueue<T, EnqPolicy>::empty() const 00790 { 00791 // No lock is necessary: the read is atomic. 00792 return size_ == 0; 00793 } 00794 00795 template<class T, class EnqPolicy> 00796 bool ConcurrentQueue<T, EnqPolicy>::full() const 00797 { 00798 LockType lock(protectElements_); 00799 return isFull(); 00800 } 00801 00802 template<class T, class EnqPolicy> 00803 typename ConcurrentQueue<T, EnqPolicy>::SizeType 00804 ConcurrentQueue<T, EnqPolicy>::size() const 00805 { 00806 // No lock is necessary: the read is atomic. 00807 return size_; 00808 } 00809 00810 template<class T, class EnqPolicy> 00811 typename ConcurrentQueue<T, EnqPolicy>::SizeType 00812 ConcurrentQueue<T, EnqPolicy>::capacity() const 00813 { 00814 // No lock is necessary: the read is atomic. 00815 return capacity_; 00816 } 00817 00818 template<class T, class EnqPolicy> 00819 bool ConcurrentQueue<T, EnqPolicy>::setCapacity(SizeType newcapacity) 00820 { 00821 LockType lock(protectElements_); 00822 bool isEmpty = (size_ == 0); 00823 if (isEmpty) { capacity_ = newcapacity; } 00824 return isEmpty; 00825 } 00826 00827 template<class T, class EnqPolicy> 00828 detail::MemoryType 00829 ConcurrentQueue<T, EnqPolicy>::used() const 00830 { 00831 // No lock is necessary: the read is atomic. 00832 return used_; 00833 } 00834 00835 template<class T, class EnqPolicy> 00836 detail::MemoryType 00837 ConcurrentQueue<T, EnqPolicy>::memory() const 00838 { 00839 // No lock is necessary: the read is atomic. 00840 return memory_; 00841 } 00842 00843 template<class T, class EnqPolicy> 00844 bool ConcurrentQueue<T, EnqPolicy>::setMemory(detail::MemoryType maxMemory) 00845 { 00846 LockType lock(protectElements_); 00847 bool isEmpty = (size_ == 0); 00848 if (isEmpty) { memory_ = maxMemory; } 00849 return isEmpty; 00850 } 00851 00852 template<class T, class EnqPolicy> 00853 typename ConcurrentQueue<T, EnqPolicy>::SizeType 00854 ConcurrentQueue<T, EnqPolicy>::clear() 00855 { 00856 LockType lock(protectElements_); 00857 SizeType clearedEvents = size_; 00858 elementsDropped_ += size_; 00859 elements_.clear(); 00860 size_ = 0; 00861 used_ = 0; 00862 return clearedEvents; 00863 } 00864 00865 template<class T, class EnqPolicy> 00866 void ConcurrentQueue<T, EnqPolicy>::addExternallyDroppedEvents(SizeType dropped) 00867 { 00868 LockType lock(protectElements_); 00869 elementsDropped_ += dropped; 00870 } 00871 00872 //----------------------------------------------------------- 00873 // Private member functions 00874 //----------------------------------------------------------- 00875 00876 template<class T, class EnqPolicy> 00877 bool ConcurrentQueue<T, EnqPolicy>::insertIfPossible(T const& item) 00878 { 00879 if (isFull()) 00880 { 00881 ++elementsDropped_; 00882 return false; 00883 } 00884 else 00885 { 00886 EnqPolicy::doInsert(item, elements_, size_, 00887 detail::memoryUsage(item), used_, queueNotEmpty_); 00888 return true; 00889 } 00890 } 00891 00892 template<class T, class EnqPolicy> 00893 bool ConcurrentQueue<T, EnqPolicy>::removeHeadIfPossible(ValueType& item) 00894 { 00895 if (size_ == 0) { return false; } 00896 removeHead(item); 00897 return true; 00898 } 00899 00900 template<class T, class EnqPolicy> 00901 void ConcurrentQueue<T, EnqPolicy>::removeHead(ValueType& item) 00902 { 00903 SequenceType holder; 00904 // Move the item out of elements_ in a manner that will not throw. 00905 holder.splice(holder.begin(), elements_, elements_.begin()); 00906 // Record the change in the length of elements_. 00907 --size_; 00908 queueNotFull_.notify_one(); 00909 assignItem(item, holder.front()); 00910 used_ -= detail::memoryUsage(item); 00911 } 00912 00913 template<class T, class EnqPolicy> 00914 void ConcurrentQueue<T, EnqPolicy>::assignItem(T& item, const T& element) 00915 { 00916 item = element; 00917 } 00918 00919 template<class T, class EnqPolicy> 00920 void ConcurrentQueue<T, EnqPolicy>::assignItem(std::pair<T, size_t>& item, const T& element) 00921 { 00922 item.first = element; 00923 item.second = elementsDropped_; 00924 elementsDropped_ = 0; 00925 } 00926 00927 template<class T, class EnqPolicy> 00928 bool ConcurrentQueue<T, EnqPolicy>::isFull() const 00929 { 00930 if (size_ >= capacity_ || used_ >= memory_) { return true; } 00931 return false; 00932 } 00933 } // namespace artdaq 00934 00935 #endif /* artdaq_core_Core_ConcurrentQueue_hh */