$treeview $search $mathjax $extrastylesheet
artdaq_core
v3_05_08
$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)); 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 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 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 00243 template<typename T> 00244 typename FailIfFull<T>::QueueIsFull FailIfFull<T>::queueIsFull{}; 00245 00250 template<class T> 00251 struct KeepNewest 00252 { 00253 typedef std::pair<T, size_t> ValueType; 00254 typedef std::list<T> SequenceType; 00255 typedef typename SequenceType::size_type SizeType; 00256 typedef SizeType ReturnType; 00257 00267 static void doInsert( 00268 T const& item, 00269 SequenceType& elements, 00270 SizeType& size, 00271 detail::MemoryType const& itemSize, 00272 detail::MemoryType& used, 00273 std::condition_variable& nonempty) 00274 { 00275 elements.push_back(item); 00276 ++size; 00277 used += itemSize; 00278 nonempty.notify_one(); 00279 } 00280 00293 static ReturnType doEnq( 00294 T const& item, 00295 SequenceType& elements, 00296 SizeType& size, 00297 SizeType& capacity, 00298 detail::MemoryType& used, 00299 detail::MemoryType& memory, 00300 size_t& elementsDropped, 00301 std::condition_variable& nonempty) 00302 { 00303 SizeType elementsRemoved(0); 00304 detail::MemoryType itemSize = detail::memoryUsage(item); 00305 while ((size == capacity || used + itemSize > memory) && !elements.empty()) 00306 { 00307 SequenceType holder; 00308 // Move the item out of elements in a manner that will not throw. 00309 holder.splice(holder.begin(), elements, elements.begin()); 00310 // Record the change in the length of elements. 00311 --size; 00312 used -= detail::memoryUsage(holder.front()); 00313 ++elementsRemoved; 00314 } 00315 if (size < capacity && used + itemSize <= memory) 00316 // we succeeded to make enough room for the new element 00317 { 00318 doInsert(item, elements, size, itemSize, used, nonempty); 00319 } 00320 else 00321 { 00322 // we cannot add the new element 00323 ++elementsRemoved; 00324 } 00325 elementsDropped += elementsRemoved; 00326 return elementsRemoved; 00327 } 00328 }; 00329 00334 template<class T> 00335 struct RejectNewest 00336 { 00337 typedef std::pair<T, size_t> ValueType; 00338 typedef std::list<T> SequenceType; 00339 typedef typename SequenceType::size_type SizeType; 00340 typedef SizeType ReturnType; 00341 00351 static void doInsert( 00352 T const& item, 00353 SequenceType& elements, 00354 SizeType& size, 00355 detail::MemoryType const& itemSize, 00356 detail::MemoryType& used, 00357 std::condition_variable& nonempty) 00358 { 00359 elements.push_back(item); 00360 ++size; 00361 used += itemSize; 00362 nonempty.notify_one(); 00363 } 00364 00377 static ReturnType doEnq( 00378 T const& item, 00379 SequenceType& elements, 00380 SizeType& size, 00381 SizeType& capacity, 00382 detail::MemoryType& used, 00383 detail::MemoryType& memory, 00384 size_t& elementsDropped, 00385 std::condition_variable& nonempty) 00386 { 00387 detail::MemoryType itemSize = detail::memoryUsage(item); 00388 if (size < capacity && used + itemSize <= memory) 00389 { 00390 doInsert(item, elements, size, itemSize, used, nonempty); 00391 return 0; 00392 } 00393 ++elementsDropped; 00394 return 1; 00395 } 00396 }; 00397 00401 template<class T, class EnqPolicy = FailIfFull<T>> 00402 class ConcurrentQueue 00403 { 00404 public: 00405 typedef typename EnqPolicy::ValueType ValueType; 00406 typedef typename EnqPolicy::SequenceType SequenceType; 00407 typedef typename SequenceType::size_type SizeType; 00408 00415 explicit ConcurrentQueue( 00416 SizeType maxSize = std::numeric_limits<SizeType>::max(), 00417 detail::MemoryType maxMemory = std::numeric_limits<detail::MemoryType>::max()); 00418 00425 ~ConcurrentQueue(); 00426 00443 typename EnqPolicy::ReturnType enqNowait(T const& item); 00444 00453 void enqWait(T const& item); 00454 00467 bool enqTimedWait(T const& item, detail::seconds const& wait); 00468 00481 bool deqNowait(ValueType& item); 00482 00493 void deqWait(ValueType& item); 00494 00508 bool deqTimedWait(ValueType& item, detail::seconds const& wait); 00509 00514 bool empty() const; 00515 00520 bool full() const; 00521 00527 SizeType size() const; 00528 00534 SizeType capacity() const; 00535 00543 bool setCapacity(SizeType capacity); 00544 00549 detail::MemoryType used() const; 00550 00556 detail::MemoryType memory() const; 00557 00566 bool setMemory(detail::MemoryType maxMemory); 00567 00573 SizeType clear(); 00574 00579 void addExternallyDroppedEvents(SizeType dropped); 00580 00585 bool queueReaderIsReady() const { return readerReady_; } 00586 00594 void setReaderIsReady(bool rdy = true) 00595 { 00596 readyTime_ = std::chrono::steady_clock::now(); 00597 readerReady_ = rdy; 00598 } 00599 00604 std::chrono::steady_clock::time_point getReadyTime() const { return readyTime_; } 00605 00606 private: 00607 typedef std::lock_guard<std::mutex> LockType; 00608 typedef std::unique_lock<std::mutex> WaitLockType; 00609 00610 mutable std::mutex protectElements_; 00611 mutable std::condition_variable queueNotEmpty_; 00612 mutable std::condition_variable queueNotFull_; 00613 00614 std::chrono::steady_clock::time_point readyTime_; 00615 bool readerReady_; 00616 SequenceType elements_; 00617 SizeType capacity_; 00618 SizeType size_; 00619 /* 00620 N.B.: we rely on SizeType *not* being some synthesized large 00621 type, so that reading the value is an atomic action, as is 00622 incrementing or decrementing the value. We do *not* assume that 00623 there is any atomic getAndIncrement or getAndDecrement 00624 operation. 00625 */ 00626 detail::MemoryType memory_; 00627 detail::MemoryType used_; 00628 size_t elementsDropped_; 00629 00630 /* 00631 These private member functions assume that whatever locks 00632 necessary for safe operation have already been obtained. 00633 */ 00634 00635 /* 00636 Insert the given item into the list, if it is not already full, 00637 and increment size. Return true if the item is inserted, and 00638 false if not. 00639 */ 00640 bool insertIfPossible(T const& item); 00641 00655 bool removeHeadIfPossible(ValueType& item); 00656 00668 void removeHead(ValueType& item); 00669 00670 void assignItem(T& item, const T& element); 00671 00672 void assignItem(std::pair<T, size_t>& item, const T& element); 00673 00674 /* 00675 Return false if the queue can accept new entries. 00676 */ 00677 bool isFull() const; 00678 00679 /* 00680 These functions are declared private and not implemented to 00681 prevent their use. 00682 */ 00683 ConcurrentQueue(ConcurrentQueue<T, EnqPolicy> const&) = delete; 00684 00685 ConcurrentQueue& operator=(ConcurrentQueue<T, EnqPolicy> const&) = delete; 00686 }; 00687 00688 //------------------------------------------------------------------ 00689 // Implementation follows 00690 //------------------------------------------------------------------ 00691 00692 template<class T, class EnqPolicy> 00693 ConcurrentQueue<T, EnqPolicy>::ConcurrentQueue( 00694 SizeType maxSize, 00695 detail::MemoryType maxMemory) 00696 : protectElements_() 00697 , readyTime_(std::chrono::steady_clock::now()) 00698 , readerReady_(false) 00699 , elements_() 00700 , capacity_(maxSize) 00701 , size_(0) 00702 , memory_(maxMemory) 00703 , used_(0) 00704 , elementsDropped_(0) 00705 {} 00706 00707 template<class T, class EnqPolicy> 00708 ConcurrentQueue<T, EnqPolicy>::~ConcurrentQueue() 00709 { 00710 LockType lock(protectElements_); 00711 elements_.clear(); 00712 size_ = 0; 00713 used_ = 0; 00714 elementsDropped_ = 0; 00715 } 00716 00717 // enqueue methods - 3 - enqNowait, enqWait, enqTimedWait 00718 00719 template<class T, class EnqPolicy> 00720 typename EnqPolicy::ReturnType ConcurrentQueue<T, EnqPolicy>::enqNowait(T const& item) 00721 { 00722 TLOG(12, "ConcurrentQueue") << "enqNowait enter size=" << size_ << " capacity=" << capacity_ << " used=" << used_ << " memory=" << memory_; 00723 LockType lock(protectElements_); 00724 auto retval = EnqPolicy::doEnq(item, elements_, size_, capacity_, used_, memory_, 00725 elementsDropped_, queueNotEmpty_); 00726 TLOG(12, "ConcurrentQueue") << "enqNowait returning " << retval; 00727 return retval; 00728 } 00729 00730 template<class T, class EnqPolicy> 00731 void ConcurrentQueue<T, EnqPolicy>::enqWait(T const& item) 00732 { 00733 TLOG(13, "ConcurrentQueue") << "enqWait enter"; 00734 WaitLockType lock(protectElements_); 00735 while (isFull()) { queueNotFull_.wait(lock); } 00736 EnqPolicy::doInsert(item, elements_, size_, 00737 detail::memoryUsage(item), used_, queueNotEmpty_); 00738 TLOG(13, "ConcurrentQueue") << "enqWait returning"; 00739 } 00740 00741 template<class T, class EnqPolicy> 00742 bool ConcurrentQueue<T, EnqPolicy>::enqTimedWait(T const& item, detail::seconds const& waitTime) 00743 { 00744 TLOG(14, "ConcurrentQueue") << "ConcurrentQueue<T,EnqPolicy>::enqTimedWait enter with waitTime=" << std::chrono::duration_cast<std::chrono::milliseconds>(waitTime).count() << " ms size=" << size_ 00745 << " capacity=" << capacity_ << " used=" << used_ << " memory=" << memory_; 00746 WaitLockType lock(protectElements_); 00747 if (isFull()) 00748 { 00749 queueNotFull_.wait_for(lock, waitTime); 00750 } 00751 bool retval = insertIfPossible(item); 00752 TLOG(14, "ConcurrentQueue") << "ConcurrentQueue<T,EnqPolicy>::enqTimedWait returning " << retval; 00753 return retval; 00754 } 00755 00756 // dequeue methods - 3 - deqNowait, deqWait, deqTimedWait 00757 00758 template<class T, class EnqPolicy> 00759 bool ConcurrentQueue<T, EnqPolicy>::deqNowait(ValueType& item) 00760 { 00761 TLOG(15, "ConcurrentQueue") << "ConcurrentQueue<T, EnqPolicy>::deqNowait enter"; 00762 LockType lock(protectElements_); 00763 bool retval = removeHeadIfPossible(item); 00764 TLOG(15, "ConcurrentQueue") << "ConcurrentQueue<T, EnqPolicy>::deqNowait returning " << retval; 00765 return retval; 00766 } 00767 00768 template<class T, class EnqPolicy> 00769 void ConcurrentQueue<T, EnqPolicy>::deqWait(ValueType& item) 00770 { 00771 TLOG(16, "ConcurrentQueue") << "ConcurrentQueue<T, EnqPolicy>::deqWait enter"; 00772 WaitLockType lock(protectElements_); 00773 while (size_ == 0) { queueNotEmpty_.wait(lock); } 00774 removeHead(item); 00775 TLOG(16, "ConcurrentQueue") << "ConcurrentQueue<T, EnqPolicy>::deqWait returning"; 00776 } 00777 00778 template<class T, class EnqPolicy> 00779 bool ConcurrentQueue<T, EnqPolicy>::deqTimedWait(ValueType& item, detail::seconds const& waitTime) 00780 { 00781 TLOG(17, "ConcurrentQueue") << "ConcurrentQueue<T, EnqPolicy>::deqTimedWait enter with waitTime=" << std::chrono::duration_cast<std::chrono::milliseconds>(waitTime).count() << " ms size=" << size_; 00782 WaitLockType lock(protectElements_); 00783 if (size_ == 0) 00784 { 00785 queueNotEmpty_.wait_for(lock, waitTime); 00786 } 00787 bool retval = removeHeadIfPossible(item); 00788 TLOG(17, "ConcurrentQueue") << "ConcurrentQueue<T, EnqPolicy>::deqTimedWait returning " << retval << " size=" << size_; 00789 return retval; 00790 } 00791 00792 template<class T, class EnqPolicy> 00793 bool ConcurrentQueue<T, EnqPolicy>::empty() const 00794 { 00795 // No lock is necessary: the read is atomic. 00796 return size_ == 0; 00797 } 00798 00799 template<class T, class EnqPolicy> 00800 bool ConcurrentQueue<T, EnqPolicy>::full() const 00801 { 00802 LockType lock(protectElements_); 00803 return isFull(); 00804 } 00805 00806 template<class T, class EnqPolicy> 00807 typename ConcurrentQueue<T, EnqPolicy>::SizeType 00808 ConcurrentQueue<T, EnqPolicy>::size() const 00809 { 00810 // No lock is necessary: the read is atomic. 00811 return size_; 00812 } 00813 00814 template<class T, class EnqPolicy> 00815 typename ConcurrentQueue<T, EnqPolicy>::SizeType 00816 ConcurrentQueue<T, EnqPolicy>::capacity() const 00817 { 00818 // No lock is necessary: the read is atomic. 00819 return capacity_; 00820 } 00821 00822 template<class T, class EnqPolicy> 00823 bool ConcurrentQueue<T, EnqPolicy>::setCapacity(SizeType newcapacity) 00824 { 00825 LockType lock(protectElements_); 00826 bool isEmpty = (size_ == 0); 00827 if (isEmpty) { capacity_ = newcapacity; } 00828 return isEmpty; 00829 } 00830 00831 template<class T, class EnqPolicy> 00832 detail::MemoryType 00833 ConcurrentQueue<T, EnqPolicy>::used() const 00834 { 00835 // No lock is necessary: the read is atomic. 00836 return used_; 00837 } 00838 00839 template<class T, class EnqPolicy> 00840 detail::MemoryType 00841 ConcurrentQueue<T, EnqPolicy>::memory() const 00842 { 00843 // No lock is necessary: the read is atomic. 00844 return memory_; 00845 } 00846 00847 template<class T, class EnqPolicy> 00848 bool ConcurrentQueue<T, EnqPolicy>::setMemory(detail::MemoryType newmemory) 00849 { 00850 LockType lock(protectElements_); 00851 bool isEmpty = (size_ == 0); 00852 if (isEmpty) { memory_ = newmemory; } 00853 return isEmpty; 00854 } 00855 00856 template<class T, class EnqPolicy> 00857 typename ConcurrentQueue<T, EnqPolicy>::SizeType 00858 ConcurrentQueue<T, EnqPolicy>::clear() 00859 { 00860 LockType lock(protectElements_); 00861 SizeType clearedEvents = size_; 00862 elementsDropped_ += size_; 00863 elements_.clear(); 00864 size_ = 0; 00865 used_ = 0; 00866 return clearedEvents; 00867 } 00868 00869 template<class T, class EnqPolicy> 00870 void ConcurrentQueue<T, EnqPolicy>::addExternallyDroppedEvents(SizeType n) 00871 { 00872 LockType lock(protectElements_); 00873 elementsDropped_ += n; 00874 } 00875 00876 //----------------------------------------------------------- 00877 // Private member functions 00878 //----------------------------------------------------------- 00879 00880 template<class T, class EnqPolicy> 00881 bool ConcurrentQueue<T, EnqPolicy>::insertIfPossible(T const& item) 00882 { 00883 if (isFull()) 00884 { 00885 ++elementsDropped_; 00886 return false; 00887 } 00888 else 00889 { 00890 EnqPolicy::doInsert(item, elements_, size_, 00891 detail::memoryUsage(item), used_, queueNotEmpty_); 00892 return true; 00893 } 00894 } 00895 00896 template<class T, class EnqPolicy> 00897 bool ConcurrentQueue<T, EnqPolicy>::removeHeadIfPossible(ValueType& item) 00898 { 00899 if (size_ == 0) { return false; } 00900 removeHead(item); 00901 return true; 00902 } 00903 00904 template<class T, class EnqPolicy> 00905 void ConcurrentQueue<T, EnqPolicy>::removeHead(ValueType& item) 00906 { 00907 SequenceType holder; 00908 // Move the item out of elements_ in a manner that will not throw. 00909 holder.splice(holder.begin(), elements_, elements_.begin()); 00910 // Record the change in the length of elements_. 00911 --size_; 00912 queueNotFull_.notify_one(); 00913 assignItem(item, holder.front()); 00914 used_ -= detail::memoryUsage(item); 00915 } 00916 00917 template<class T, class EnqPolicy> 00918 void ConcurrentQueue<T, EnqPolicy>::assignItem(T& item, const T& element) 00919 { 00920 item = element; 00921 } 00922 00923 template<class T, class EnqPolicy> 00924 void ConcurrentQueue<T, EnqPolicy>::assignItem(std::pair<T, size_t>& item, const T& element) 00925 { 00926 item.first = element; 00927 item.second = elementsDropped_; 00928 elementsDropped_ = 0; 00929 } 00930 00931 template<class T, class EnqPolicy> 00932 bool ConcurrentQueue<T, EnqPolicy>::isFull() const 00933 { 00934 if (size_ >= capacity_ || used_ >= memory_) { return true; } 00935 return false; 00936 } 00937 } // namespace artdaq 00938 00939 #endif /* artdaq_core_Core_ConcurrentQueue_hh */