1 #ifndef artdaq_core_Core_ConcurrentQueue_hh
2 #define artdaq_core_Core_ConcurrentQueue_hh
14 #include <condition_variable>
16 #include <type_traits>
19 #define TRACE_NAME "ConcurrentQueue"
64 typedef std::chrono::duration<double>
seconds;
76 typedef char TrueType;
83 template <MemoryType(T::*)() const>
87 static TrueType test(TestConst<&C::memoryUsed>*) {
return 0; }
90 static FalseType test(...)
102 static const bool value = (
sizeof(test<T>(
nullptr)) ==
sizeof(TrueType));
111 template <
typename T>
118 usage = t.first.memoryUsed();
130 template <
typename T>
131 typename std::enable_if<hasMemoryUsed<T>::value,
MemoryType>::type
137 usage = t.memoryUsed();
149 template <
typename T>
150 typename std::enable_if<!hasMemoryUsed<T>::value,
MemoryType>::type
180 virtual const char*
what()
const throw()
182 return "Cannot add item to a full queue";
202 std::condition_variable& nonempty
205 elements.push_back(item);
208 nonempty.notify_one();
232 size_t& elementsDropped,
233 std::condition_variable& nonempty
237 if (size >= capacity || used + itemSize > memory)
244 doInsert(item, elements, size, itemSize, used, nonempty);
250 template <
typename T>
281 std::condition_variable& nonempty
284 elements.push_back(item);
287 nonempty.notify_one();
310 size_t& elementsDropped,
311 std::condition_variable& nonempty
316 while ((size == capacity || used + itemSize > memory) && !elements.empty())
320 holder.splice(holder.begin(), elements, elements.begin());
326 if (size < capacity && used + itemSize <= memory)
329 doInsert(item, elements, size, itemSize, used, nonempty);
336 elementsDropped += elementsRemoved;
337 return elementsRemoved;
369 std::condition_variable& nonempty
372 elements.push_back(item);
375 nonempty.notify_one();
398 size_t& elementsDropped,
399 std::condition_variable& nonempty
403 if (size < capacity && used + itemSize <= memory)
405 doInsert(item, elements, size, itemSize, used, nonempty);
416 template <
class T,
class EnqPolicy = FailIfFull<T>>
432 SizeType maxSize = std::numeric_limits<SizeType>::max(),
460 typename EnqPolicy::ReturnType
enqNowait(T
const& item);
613 readyTime_ = std::chrono::steady_clock::now();
621 std::chrono::steady_clock::time_point
getReadyTime()
const {
return readyTime_; }
624 typedef std::lock_guard<std::mutex> LockType;
625 typedef std::unique_lock<std::mutex> WaitLockType;
627 mutable std::mutex protectElements_;
628 mutable std::condition_variable queueNotEmpty_;
629 mutable std::condition_variable queueNotFull_;
631 std::chrono::steady_clock::time_point readyTime_;
645 size_t elementsDropped_;
657 bool insertIfPossible(T
const& item);
672 bool removeHeadIfPossible(
ValueType& item);
687 void assignItem(T& item,
const T& element);
689 void assignItem(std::pair<T, size_t>& item,
const T& element);
709 template <
class T,
class EnqPolicy>
716 , readyTime_(std::chrono::steady_clock::now())
717 , readerReady_(
false)
723 , elementsDropped_(0) {}
725 template <
class T,
class EnqPolicy>
728 LockType lock(protectElements_);
732 elementsDropped_ = 0;
738 template <
class T,
class EnqPolicy>
741 TLOG(12) <<
"enqNowait enter size=" << size_ <<
" capacity=" << capacity_ <<
" used=" << used_ <<
" memory=" << memory_ << TLOG_ENDL;
742 LockType lock(protectElements_);
743 auto retval = EnqPolicy::doEnq(item, elements_, size_, capacity_, used_, memory_,
744 elementsDropped_, queueNotEmpty_);
745 TLOG(12) <<
"enqNowait returning " << retval << TLOG_ENDL;
749 template <
class T,
class EnqPolicy>
752 TLOG(13) <<
"enqWait enter" << TLOG_ENDL;
753 WaitLockType lock(protectElements_);
754 while (isFull()) { queueNotFull_.wait(lock); }
755 EnqPolicy::doInsert(item, elements_, size_,
757 TLOG(13) <<
"enqWait returning" << TLOG_ENDL;
760 template <
class T,
class EnqPolicy>
763 TLOG(14) <<
"ConcurrentQueue<T,EnqPolicy>::enqTimedWait enter with waitTime=" << std::chrono::duration_cast<std::chrono::milliseconds>(waitTime).count() <<
" ms size=" << size_
764 <<
" capacity=" << capacity_ <<
" used=" << used_ <<
" memory=" << memory_ << TLOG_ENDL;
765 WaitLockType lock(protectElements_);
768 queueNotFull_.wait_for(lock, waitTime);
770 bool retval = insertIfPossible(item);
771 TLOG(14) <<
"ConcurrentQueue<T,EnqPolicy>::enqTimedWait returning " << retval << TLOG_ENDL;
778 template <
class T,
class EnqPolicy>
781 TLOG(15) <<
"ConcurrentQueue<T, EnqPolicy>::deqNowait enter" << TLOG_ENDL;
782 LockType lock(protectElements_);
783 bool retval = removeHeadIfPossible(item);
784 TLOG(15) <<
"ConcurrentQueue<T, EnqPolicy>::deqNowait returning " << retval << TLOG_ENDL;
788 template <
class T,
class EnqPolicy>
791 TLOG(16) <<
"ConcurrentQueue<T, EnqPolicy>::deqWait enter" << TLOG_ENDL;
792 WaitLockType lock(protectElements_);
793 while (size_ == 0) { queueNotEmpty_.wait(lock); }
795 TLOG(16) <<
"ConcurrentQueue<T, EnqPolicy>::deqWait returning" << TLOG_ENDL;
798 template <
class T,
class EnqPolicy>
801 TLOG(17) <<
"ConcurrentQueue<T, EnqPolicy>::deqTimedWait enter with waitTime=" << std::chrono::duration_cast<std::chrono::milliseconds>(waitTime).count() <<
" ms size=" << size_ << TLOG_ENDL;
802 WaitLockType lock(protectElements_);
805 queueNotEmpty_.wait_for(lock, waitTime);
807 bool retval = removeHeadIfPossible(item);
808 TLOG(17) <<
"ConcurrentQueue<T, EnqPolicy>::deqTimedWait returning " << retval <<
" size=" << size_ << TLOG_ENDL;
813 template <
class T,
class EnqPolicy>
821 template <
class T,
class EnqPolicy>
825 LockType lock(protectElements_);
829 template <
class T,
class EnqPolicy>
837 template <
class T,
class EnqPolicy>
845 template <
class T,
class EnqPolicy>
849 LockType lock(protectElements_);
850 bool isEmpty = (size_ == 0);
851 if (isEmpty) { capacity_ = newcapacity; }
855 template <
class T,
class EnqPolicy>
863 template <
class T,
class EnqPolicy>
871 template <
class T,
class EnqPolicy>
875 LockType lock(protectElements_);
876 bool isEmpty = (size_ == 0);
877 if (isEmpty) { memory_ = newmemory; }
881 template <
class T,
class EnqPolicy>
885 LockType lock(protectElements_);
887 elementsDropped_ += size_;
891 return clearedEvents;
894 template <
class T,
class EnqPolicy>
898 LockType lock(protectElements_);
899 elementsDropped_ += n;
906 template <
class T,
class EnqPolicy>
917 EnqPolicy::doInsert(item, elements_, size_,
923 template <
class T,
class EnqPolicy>
925 ConcurrentQueue<T, EnqPolicy>::removeHeadIfPossible(ValueType& item)
927 if (size_ == 0) {
return false; }
932 template <
class T,
class EnqPolicy>
934 ConcurrentQueue<T, EnqPolicy>::removeHead(ValueType& item)
938 holder.splice(holder.begin(), elements_, elements_.begin());
941 queueNotFull_.notify_one();
942 assignItem(item, holder.front());
946 template <
class T,
class EnqPolicy>
948 ConcurrentQueue<T, EnqPolicy>::assignItem(T& item,
const T& element)
953 template <
class T,
class EnqPolicy>
955 ConcurrentQueue<T, EnqPolicy>::assignItem(std::pair<T, size_t>& item,
const T& element)
957 item.first = element;
958 item.second = elementsDropped_;
959 elementsDropped_ = 0;
962 template <
class T,
class EnqPolicy>
964 ConcurrentQueue<T, EnqPolicy>::isFull()
const
966 if (size_ >= capacity_ || used_ >= memory_) {
return true; }
std::chrono::steady_clock::time_point getReadyTime() const
Gets the time at which the queue became ready.
std::list< T > SequenceType
Type of sequences of items.
T ValueType
Type of values stored in queue.
static ReturnType doEnq(T const &item, SequenceType &elements, SizeType &size, SizeType &capacity, detail::MemoryType &used, detail::MemoryType &memory, size_t &elementsDropped, std::condition_variable &nonempty)
Attempts to enqueue an item.
SizeType clear()
Remove all items from the queue. This changes the size to zero but does not change the capacity...
detail::MemoryType used() const
Return the memory in bytes used by items in the queue.
std::pair< T, size_t > ValueType
Type of elements stored in the queue.
virtual const char * what() const
Describe exception.
detail::MemoryType memory() const
Return the memory of the queue in bytes, that is, the maximum memory the items in the queue may occup...
SequenceType::size_type SizeType
Size type of seqeuences.
MemoryType memoryUsage(const std::pair< T, size_t > &t)
Returns the memory used by an object.
std::pair< T, size_t > ValueType
Type of elements stored in the queue.
std::chrono::duration< double > seconds
EnqPolicy::SequenceType SequenceType
Type of sequence used by ConcurrentQueue.
EnqPolicy::ReturnType enqNowait(T const &item)
Add a copy if item to the queue, according to the rules determined by the EnqPolicy.
SequenceType::size_type SizeType
Size type of seqeuences.
static void doInsert(T const &item, SequenceType &elements, SizeType &size, detail::MemoryType const &itemSize, detail::MemoryType &used, std::condition_variable &nonempty)
Inserts element into the ConcurrentQueue.
std::list< T > SequenceType
Type of sequences of items.
bool setCapacity(SizeType capacity)
SizeType ReturnType
Type returned by doEnq.
SequenceType::size_type SizeType
Size type of seqeuences.
ConcurrentQueue policy to throw an exception when the queue is full.
size_t MemoryType
Basic unit of data storage and pointer types.
Exception thrown by FailIfFull policy when an enqueue operation is attempted on a full queue...
ConcurrentQueue policy to discard oldest elements when the queue is full.
static const bool value
Use SFINAE to figure out if the class used to instantiate the ConcurrentQueue template has a method m...
ConcurrentQueue policy to discard new elements when the queue is full.
static void doInsert(T const &item, SequenceType &elements, SizeType &size, detail::MemoryType const &itemSize, detail::MemoryType &used, std::condition_variable &nonempty)
Inserts element into the ConcurrentQueue.
bool deqNowait(ValueType &item)
Assign the value at the head of the queue to item and then remove the head of the queue...
bool setMemory(detail::MemoryType maxMemory)
Reset the memory usage in bytes of the queue. A value of 0 disabled the memory check. This can only be done if the queue is empty.
SizeType ReturnType
Type returned by doEnq.
artdaq::FailIfFull::QueueIsFull queueIsFull
Instance of QueueIsFull exception.
ConcurrentQueue(SizeType maxSize=std::numeric_limits< SizeType >::max(), detail::MemoryType maxMemory=std::numeric_limits< detail::MemoryType >::max())
ConcurrentQueue is always bounded. By default, the bound is absurdly large.
static ReturnType doEnq(T const &item, SequenceType &elements, SizeType &size, SizeType &capacity, detail::MemoryType &used, detail::MemoryType &memory, size_t &elementsDropped, std::condition_variable &nonempty)
Attempts to enqueue an item.
void enqWait(T const &item)
Add a copy of item to the queue.
bool deqTimedWait(ValueType &item, detail::seconds const &wait)
Assign the value at the head of the queue to item and then remove the head of the queue...
bool enqTimedWait(T const &item, detail::seconds const &wait)
Add a copy of item to the queue, waiting for the queue to be non-full.
std::list< T > SequenceType
Type of seqeuences of values.
void addExternallyDroppedEvents(SizeType dropped)
Adds the passed count to the counter of dropped events.
static void doInsert(T const &item, SequenceType &elements, SizeType &size, detail::MemoryType const &itemSize, detail::MemoryType &used, std::condition_variable &nonempty)
Inserts element into the ConcurrentQueue.
void deqWait(ValueType &item)
Assign the value of the head of the queue to item and then remove the head of the queue...
SizeType capacity() const
Return the capacity of the queue, that is, the maximum number of items it can contain.
void setReaderIsReady(bool rdy=true)
Set the ready flag for the reader.
EnqPolicy::ValueType ValueType
Type of values stored in ConcurrentQueue.
SequenceType::size_type SizeType
Type for indexes in sequence.
static ReturnType doEnq(T const &item, SequenceType &elements, SizeType &size, SizeType &capacity, detail::MemoryType &used, detail::MemoryType &memory, size_t &elementsDropped, std::condition_variable &nonempty)
Attempts to enqueue an item.
bool queueReaderIsReady() const
Is the reader connected and ready for items to appear on the queue?
bool ReturnType
Type returned by doEnq.