1 #ifndef artdaq_core_Core_ConcurrentQueue_hh
2 #define artdaq_core_Core_ConcurrentQueue_hh
14 #include <condition_variable>
16 #include <type_traits>
62 typedef std::chrono::duration<double>
seconds;
74 typedef char TrueType;
81 template <MemoryType(T::*)() const>
85 static TrueType test(TestConst<&C::memoryUsed>*) {
return 0; }
88 static FalseType test(...)
100 static const bool value = (
sizeof(test<T>(
nullptr)) ==
sizeof(TrueType));
109 template <
typename T>
116 usage = t.first.memoryUsed();
128 template <
typename T>
129 typename std::enable_if<hasMemoryUsed<T>::value,
MemoryType>::type
135 usage = t.memoryUsed();
147 template <
typename T>
148 typename std::enable_if<!hasMemoryUsed<T>::value,
MemoryType>::type
178 virtual const char*
what()
const throw()
180 return "Cannot add item to a full queue";
200 std::condition_variable& nonempty
203 elements.push_back(item);
206 nonempty.notify_one();
230 size_t& elementsDropped,
231 std::condition_variable& nonempty
235 if (size >= capacity || used + itemSize > memory)
242 doInsert(item, elements, size, itemSize, used, nonempty);
248 template <
typename T>
279 std::condition_variable& nonempty
282 elements.push_back(item);
285 nonempty.notify_one();
308 size_t& elementsDropped,
309 std::condition_variable& nonempty
314 while ((size == capacity || used + itemSize > memory) && !elements.empty())
318 holder.splice(holder.begin(), elements, elements.begin());
324 if (size < capacity && used + itemSize <= memory)
327 doInsert(item, elements, size, itemSize, used, nonempty);
334 elementsDropped += elementsRemoved;
335 return elementsRemoved;
367 std::condition_variable& nonempty
370 elements.push_back(item);
373 nonempty.notify_one();
396 size_t& elementsDropped,
397 std::condition_variable& nonempty
401 if (size < capacity && used + itemSize <= memory)
403 doInsert(item, elements, size, itemSize, used, nonempty);
414 template <
class T,
class EnqPolicy = FailIfFull<T>>
430 SizeType maxSize = std::numeric_limits<SizeType>::max(),
458 typename EnqPolicy::ReturnType
enqNowait(T
const& item);
611 readyTime_ = std::chrono::steady_clock::now();
619 std::chrono::steady_clock::time_point
getReadyTime()
const {
return readyTime_; }
622 typedef std::lock_guard<std::mutex> LockType;
623 typedef std::unique_lock<std::mutex> WaitLockType;
625 mutable std::mutex protectElements_;
626 mutable std::condition_variable queueNotEmpty_;
627 mutable std::condition_variable queueNotFull_;
629 std::chrono::steady_clock::time_point readyTime_;
643 size_t elementsDropped_;
655 bool insertIfPossible(T
const& item);
670 bool removeHeadIfPossible(
ValueType& item);
685 void assignItem(T& item,
const T& element);
687 void assignItem(std::pair<T, size_t>& item,
const T& element);
707 template <
class T,
class EnqPolicy>
714 , readyTime_(std::chrono::steady_clock::now())
715 , readerReady_(
false)
721 , elementsDropped_(0) {}
723 template <
class T,
class EnqPolicy>
726 LockType lock(protectElements_);
730 elementsDropped_ = 0;
736 template <
class T,
class EnqPolicy>
739 TLOG(12,
"ConcurrentQueue") <<
"enqNowait enter size=" << size_ <<
" capacity=" << capacity_ <<
" used=" << used_ <<
" memory=" << memory_ ;
740 LockType lock(protectElements_);
741 auto retval = EnqPolicy::doEnq(item, elements_, size_, capacity_, used_, memory_,
742 elementsDropped_, queueNotEmpty_);
743 TLOG(12,
"ConcurrentQueue") <<
"enqNowait returning " << retval ;
747 template <
class T,
class EnqPolicy>
750 TLOG(13,
"ConcurrentQueue") <<
"enqWait enter" ;
751 WaitLockType lock(protectElements_);
752 while (isFull()) { queueNotFull_.wait(lock); }
753 EnqPolicy::doInsert(item, elements_, size_,
755 TLOG(13,
"ConcurrentQueue") <<
"enqWait returning" ;
758 template <
class T,
class EnqPolicy>
761 TLOG(14,
"ConcurrentQueue") <<
"ConcurrentQueue<T,EnqPolicy>::enqTimedWait enter with waitTime=" << std::chrono::duration_cast<std::chrono::milliseconds>(waitTime).count() <<
" ms size=" << size_
762 <<
" capacity=" << capacity_ <<
" used=" << used_ <<
" memory=" << memory_ ;
763 WaitLockType lock(protectElements_);
766 queueNotFull_.wait_for(lock, waitTime);
768 bool retval = insertIfPossible(item);
769 TLOG(14,
"ConcurrentQueue") <<
"ConcurrentQueue<T,EnqPolicy>::enqTimedWait returning " << retval ;
776 template <
class T,
class EnqPolicy>
779 TLOG(15,
"ConcurrentQueue") <<
"ConcurrentQueue<T, EnqPolicy>::deqNowait enter" ;
780 LockType lock(protectElements_);
781 bool retval = removeHeadIfPossible(item);
782 TLOG(15,
"ConcurrentQueue") <<
"ConcurrentQueue<T, EnqPolicy>::deqNowait returning " << retval ;
786 template <
class T,
class EnqPolicy>
789 TLOG(16,
"ConcurrentQueue") <<
"ConcurrentQueue<T, EnqPolicy>::deqWait enter" ;
790 WaitLockType lock(protectElements_);
791 while (size_ == 0) { queueNotEmpty_.wait(lock); }
793 TLOG(16,
"ConcurrentQueue") <<
"ConcurrentQueue<T, EnqPolicy>::deqWait returning" ;
796 template <
class T,
class EnqPolicy>
799 TLOG(17,
"ConcurrentQueue") <<
"ConcurrentQueue<T, EnqPolicy>::deqTimedWait enter with waitTime=" << std::chrono::duration_cast<std::chrono::milliseconds>(waitTime).count() <<
" ms size=" << size_ ;
800 WaitLockType lock(protectElements_);
803 queueNotEmpty_.wait_for(lock, waitTime);
805 bool retval = removeHeadIfPossible(item);
806 TLOG(17,
"ConcurrentQueue") <<
"ConcurrentQueue<T, EnqPolicy>::deqTimedWait returning " << retval <<
" size=" << size_ ;
811 template <
class T,
class EnqPolicy>
819 template <
class T,
class EnqPolicy>
823 LockType lock(protectElements_);
827 template <
class T,
class EnqPolicy>
835 template <
class T,
class EnqPolicy>
843 template <
class T,
class EnqPolicy>
847 LockType lock(protectElements_);
848 bool isEmpty = (size_ == 0);
849 if (isEmpty) { capacity_ = newcapacity; }
853 template <
class T,
class EnqPolicy>
861 template <
class T,
class EnqPolicy>
869 template <
class T,
class EnqPolicy>
873 LockType lock(protectElements_);
874 bool isEmpty = (size_ == 0);
875 if (isEmpty) { memory_ = newmemory; }
879 template <
class T,
class EnqPolicy>
883 LockType lock(protectElements_);
885 elementsDropped_ += size_;
889 return clearedEvents;
892 template <
class T,
class EnqPolicy>
896 LockType lock(protectElements_);
897 elementsDropped_ += n;
904 template <
class T,
class EnqPolicy>
915 EnqPolicy::doInsert(item, elements_, size_,
921 template <
class T,
class EnqPolicy>
923 ConcurrentQueue<T, EnqPolicy>::removeHeadIfPossible(ValueType& item)
925 if (size_ == 0) {
return false; }
930 template <
class T,
class EnqPolicy>
932 ConcurrentQueue<T, EnqPolicy>::removeHead(ValueType& item)
936 holder.splice(holder.begin(), elements_, elements_.begin());
939 queueNotFull_.notify_one();
940 assignItem(item, holder.front());
944 template <
class T,
class EnqPolicy>
946 ConcurrentQueue<T, EnqPolicy>::assignItem(T& item,
const T& element)
951 template <
class T,
class EnqPolicy>
953 ConcurrentQueue<T, EnqPolicy>::assignItem(std::pair<T, size_t>& item,
const T& element)
955 item.first = element;
956 item.second = elementsDropped_;
957 elementsDropped_ = 0;
960 template <
class T,
class EnqPolicy>
962 ConcurrentQueue<T, EnqPolicy>::isFull()
const
964 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.