artdaq_core  v1_06_00
 All Classes Namespaces Functions
ConcurrentQueue.hh
1 
2 #ifndef artdaq_core_Core_ConcurrentQueue_hh
3 #define artdaq_core_Core_ConcurrentQueue_hh
4 
5 #include <algorithm>
6 #include <cstddef>
7 #include <exception>
8 #include <limits>
9 #include <list>
10 
11 #include <iostream> // debugging
12 
13 #include <chrono>
14 #include <condition_variable>
15 #include <mutex>
16 #include <type_traits>
17 
18 // #include <boost/date_time/posix_time/posix_time_types.hpp>
19 // #include <boost/utility/enable_if.hpp>
20 // #include <boost/thread/condition.hpp>
21 // #include <boost/thread/mutex.hpp>
22 // #include <boost/thread/xtime.hpp>
23 
24 namespace daqrate {
25  // We shall use daqrate::seconds as our "standard" duration
26  // type. Note that this differs from std::chrono::seconds, which has
27  // a representation in some integer type of at least 35 bits.
28  //
29  // daqrate::duration dur(1.0) represents a duration of 1 second.
30  // daqrate::duration dur2(0.001) represents a duration of 1
31  // millisecond.
32  typedef std::chrono::duration<double> seconds;
33 
58  namespace detail {
59  typedef size_t MemoryType;
60 
61  /*
62  This template is using SFINAE to figure out if the class used to
63  instantiate the ConcurrentQueue template has a method memoryUsed
64  returning the number of bytes occupied by the class itself.
65  */
66  template <typename T>
67  class hasMemoryUsed {
68  typedef char TrueType;
69  struct FalseType { TrueType _[2]; };
70 
71  template <MemoryType(T:: *)() const>
72  struct TestConst;
73 
74  template <typename C>
75  static TrueType test(TestConst<&C::memoryUsed> *);
76  template <typename C>
77  static FalseType test(...);
78 
79  public:
80  static const bool value = (sizeof(test<T>(0)) == sizeof(TrueType));
81  };
82 
83  template <typename T>
84  MemoryType
85  memoryUsage(const std::pair<T, size_t> & t)
86  {
87  MemoryType usage(0UL);
88  try {
89  usage = t.first.memoryUsed();
90  }
91  catch (...)
92  {}
93  return usage;
94  }
95 
96  template <typename T>
97  typename std::enable_if<hasMemoryUsed<T>::value, MemoryType>::type
98  memoryUsage(const T & t)
99  {
100  MemoryType usage(0UL);
101  try {
102  usage = t.memoryUsed();
103  }
104  catch (...)
105  {}
106  return usage;
107  }
108 
109  template <typename T>
110  typename std::enable_if < !hasMemoryUsed<T>::value, MemoryType >::type
111  memoryUsage(const T & t)
112  { return sizeof(t); }
113 
114  }// end namespace detail
115 
116 
117  template <class T>
118  struct FailIfFull {
119  typedef void ReturnType;
120 
121  typedef T ValueType;
122  typedef std::list<T> SequenceType;
123  typedef typename SequenceType::size_type SizeType;
124 
125  static struct QueueIsFull : public std::exception {
126  virtual const char * what() const throw() {
127  return "Cannot add item to a full queue";
128  }
129  } queueIsFull;
130 
131  static void doInsert
132  (
133  T const & item,
134  SequenceType & elements,
135  SizeType & size,
136  detail::MemoryType const & itemSize,
137  detail::MemoryType & used,
138  std::condition_variable & nonempty
139  ) {
140  elements.push_back(item);
141  ++size;
142  used += itemSize;
143  nonempty.notify_one();
144  }
145 
146  static ReturnType doEnq
147  (
148  T const & item,
149  SequenceType & elements,
150  SizeType & size,
151  SizeType & capacity,
152  detail::MemoryType & used,
153  detail::MemoryType & memory,
154  size_t & elementsDropped,
155  std::condition_variable & nonempty
156  ) {
157  detail::MemoryType itemSize = detail::memoryUsage(item);
158  if (size >= capacity || used + itemSize > memory) {
159  ++elementsDropped;
160  throw queueIsFull;
161  }
162  else {
163  doInsert(item, elements, size, itemSize, used, nonempty);
164  }
165  }
166  };
167 
168  template<typename T>
169  typename FailIfFull<T>::QueueIsFull FailIfFull<T>::queueIsFull {};
170 
171  template <class T>
172  struct KeepNewest {
173  typedef std::pair<T, size_t> ValueType;
174  typedef std::list<T> SequenceType;
175  typedef typename SequenceType::size_type SizeType;
176  typedef SizeType ReturnType;
177 
178  static void doInsert
179  (
180  T const & item,
181  SequenceType & elements,
182  SizeType & size,
183  detail::MemoryType const & itemSize,
184  detail::MemoryType & used,
185  std::condition_variable & nonempty
186  ) {
187  elements.push_back(item);
188  ++size;
189  used += itemSize;
190  nonempty.notify_one();
191  }
192 
193  static ReturnType doEnq
194  (
195  T const & item,
196  SequenceType & elements,
197  SizeType & size,
198  SizeType & capacity,
199  detail::MemoryType & used,
200  detail::MemoryType & memory,
201  size_t & elementsDropped,
202  std::condition_variable & nonempty
203  ) {
204  SizeType elementsRemoved(0);
205  detail::MemoryType itemSize = detail::memoryUsage(item);
206  while ((size == capacity || used + itemSize > memory) && !elements.empty()) {
207  SequenceType holder;
208  // Move the item out of elements in a manner that will not throw.
209  holder.splice(holder.begin(), elements, elements.begin());
210  // Record the change in the length of elements.
211  --size;
212  used -= detail::memoryUsage(holder.front());
213  ++elementsRemoved;
214  }
215  if (size < capacity && used + itemSize <= memory)
216  // we succeeded to make enough room for the new element
217  {
218  doInsert(item, elements, size, itemSize, used, nonempty);
219  }
220  else {
221  // we cannot add the new element
222  ++elementsRemoved;
223  }
224  elementsDropped += elementsRemoved;
225  return elementsRemoved;
226  }
227  };
228 
229 
230  template <class T>
231  struct RejectNewest {
232  typedef std::pair<T, size_t> ValueType;
233  typedef std::list<T> SequenceType;
234  typedef typename SequenceType::size_type SizeType;
235  typedef SizeType ReturnType;
236 
237  static void doInsert
238  (
239  T const & item,
240  SequenceType & elements,
241  SizeType & size,
242  detail::MemoryType const & itemSize,
243  detail::MemoryType & used,
244  std::condition_variable & nonempty
245  ) {
246  elements.push_back(item);
247  ++size;
248  used += itemSize;
249  nonempty.notify_one();
250  }
251 
252  static ReturnType doEnq
253  (
254  T const & item,
255  SequenceType & elements,
256  SizeType & size,
257  SizeType & capacity,
258  detail::MemoryType & used,
259  detail::MemoryType & memory,
260  size_t & elementsDropped,
261  std::condition_variable & nonempty
262  ) {
263  detail::MemoryType itemSize = detail::memoryUsage(item);
264  if (size < capacity && used + itemSize <= memory) {
265  doInsert(item, elements, size, itemSize, used, nonempty);
266  return 0;
267  }
268  ++elementsDropped;
269  return 1;
270  }
271  };
272 
277  template <class T, class EnqPolicy = FailIfFull<T> >
279  public:
280  typedef typename EnqPolicy::ValueType ValueType;
281  typedef typename EnqPolicy::SequenceType SequenceType;
282  typedef typename SequenceType::size_type SizeType;
283 
288  explicit ConcurrentQueue
289  (
290  SizeType maxSize = std::numeric_limits<SizeType>::max(),
291  detail::MemoryType maxMemory = std::numeric_limits<detail::MemoryType>::max()
292  );
293 
301 
314  typename EnqPolicy::ReturnType enqNowait(T const & item);
315 
321  void enqWait(T const & p);
322 
330  bool enqTimedWait(T const & p, seconds const &);
331 
339  bool deqNowait(ValueType &);
340 
347  void deqWait(ValueType &);
348 
357  bool deqTimedWait(ValueType &, seconds const &);
358 
362  bool empty() const;
363 
367  bool full() const;
368 
373  SizeType size() const;
374 
379  SizeType capacity() const;
380 
386  bool setCapacity(SizeType n);
387 
391  detail::MemoryType used() const;
392 
397  detail::MemoryType memory() const;
398 
405  bool setMemory(detail::MemoryType n);
406 
412  SizeType clear();
413 
417  void addExternallyDroppedEvents(SizeType);
418 
419 
420  private:
421  typedef std::lock_guard<std::mutex> LockType;
422  typedef std::unique_lock<std::mutex> WaitLockType;
423 
424  mutable std::mutex protectElements_;
425  mutable std::condition_variable queueNotEmpty_;
426  mutable std::condition_variable queueNotFull_;
427 
428  SequenceType elements_;
429  SizeType capacity_;
430  SizeType size_;
431  /*
432  N.B.: we rely on SizeType *not* being some synthesized large
433  type, so that reading the value is an atomic action, as is
434  incrementing or decrementing the value. We do *not* assume that
435  there is any atomic getAndIncrement or getAndDecrement
436  operation.
437  */
438  detail::MemoryType memory_;
439  detail::MemoryType used_;
440  size_t elementsDropped_;
441 
442  /*
443  These private member functions assume that whatever locks
444  necessary for safe operation have already been obtained.
445  */
446 
447  /*
448  Insert the given item into the list, if it is not already full,
449  and increment size. Return true if the item is inserted, and
450  false if not.
451  */
452  bool insertIfPossible(T const & item);
453 
454  /*
455  Remove the object at the head of the queue, if there is one, and
456  assign item the value of this object.The assignment may throw an
457  exception; even if it does, the head will have been removed from
458  the queue, and the size appropriately adjusted. It is assumed
459  the queue is nonempty. Return true if the queue was nonempty,
460  and false if the queue was empty.
461  */
462  bool removeHeadIfPossible(ValueType & item);
463 
464  /*
465  Remove the object at the head of the queue, and assign item the
466  value of this object. The assignment may throw an exception;
467  even if it does, the head will have been removed from the queue,
468  and the size appropriately adjusted. It is assumed the queue is
469  nonempty.
470  */
471  void removeHead(ValueType & item);
472 
473  void assignItem(T & item, const T & element);
474  void assignItem(std::pair<T, size_t> & item, const T & element);
475 
476  /*
477  Return false if the queue can accept new entries.
478  */
479  bool isFull() const;
480 
481  /*
482  These functions are declared private and not implemented to
483  prevent their use.
484  */
486  ConcurrentQueue & operator=(ConcurrentQueue<T, EnqPolicy> const &);
487  };
488 
489  //------------------------------------------------------------------
490  // Implementation follows
491  //------------------------------------------------------------------
492 
493  template <class T, class EnqPolicy>
495  (
496  SizeType maxSize,
497  detail::MemoryType maxMemory
498  ) :
499  protectElements_(),
500  elements_(),
501  capacity_(maxSize),
502  size_(0),
503  memory_(maxMemory),
504  used_(0),
505  elementsDropped_(0)
506  {}
507 
508  template <class T, class EnqPolicy>
510  {
511  LockType lock(protectElements_);
512  elements_.clear();
513  size_ = 0;
514  used_ = 0;
515  elementsDropped_ = 0;
516  }
517 
518  template <class T, class EnqPolicy>
519  typename EnqPolicy::ReturnType
521  {
522  LockType lock(protectElements_);
523  return EnqPolicy::doEnq
524  (item, elements_, size_, capacity_, used_, memory_,
525  elementsDropped_, queueNotEmpty_);
526  }
527 
528  template <class T, class EnqPolicy>
529  void
531  {
532  WaitLockType lock(protectElements_);
533  while (isFull()) { queueNotFull_.wait(lock); }
534  EnqPolicy::doInsert(item, elements_, size_,
535  detail::memoryUsage(item), used_, queueNotEmpty_);
536  }
537 
538  template <class T, class EnqPolicy>
539  bool
541  (
542  T const & item,
543  seconds const & waitTime
544  )
545  {
546  WaitLockType lock(protectElements_);
547  if (isFull()) {
548  queueNotFull_.wait_for(lock, waitTime);
549  }
550  return insertIfPossible(item);
551  }
552 
553  template <class T, class EnqPolicy>
554  bool
556  {
557  LockType lock(protectElements_);
558  return removeHeadIfPossible(item);
559  }
560 
561  template <class T, class EnqPolicy>
562  void
564  {
565  WaitLockType lock(protectElements_);
566  while (size_ == 0) { queueNotEmpty_.wait(lock); }
567  removeHead(item);
568  }
569 
570  template <class T, class EnqPolicy>
571  bool
573  (
574  ValueType & item,
575  seconds const & waitTime
576  )
577  {
578  WaitLockType lock(protectElements_);
579  if (size_ == 0) {
580  queueNotEmpty_.wait_for(lock, waitTime);
581  }
582  return removeHeadIfPossible(item);
583  }
584 
585  template <class T, class EnqPolicy>
586  bool
588  {
589  // No lock is necessary: the read is atomic.
590  return size_ == 0;
591  }
592 
593  template <class T, class EnqPolicy>
594  bool
596  {
597  LockType lock(protectElements_);
598  return isFull();
599  }
600 
601  template <class T, class EnqPolicy>
602  typename ConcurrentQueue<T, EnqPolicy>::SizeType
604  {
605  // No lock is necessary: the read is atomic.
606  return size_;
607  }
608 
609  template <class T, class EnqPolicy>
610  typename ConcurrentQueue<T, EnqPolicy>::SizeType
612  {
613  // No lock is necessary: the read is atomic.
614  return capacity_;
615  }
616 
617  template <class T, class EnqPolicy>
618  bool
620  {
621  LockType lock(protectElements_);
622  bool isEmpty = (size_ == 0);
623  if (isEmpty) { capacity_ = newcapacity; }
624  return isEmpty;
625  }
626 
627  template <class T, class EnqPolicy>
628  detail::MemoryType
630  {
631  // No lock is necessary: the read is atomic.
632  return used_;
633  }
634 
635  template <class T, class EnqPolicy>
636  detail::MemoryType
638  {
639  // No lock is necessary: the read is atomic.
640  return memory_;
641  }
642 
643  template <class T, class EnqPolicy>
644  bool
645  ConcurrentQueue<T, EnqPolicy>::setMemory(detail::MemoryType newmemory)
646  {
647  LockType lock(protectElements_);
648  bool isEmpty = (size_ == 0);
649  if (isEmpty) { memory_ = newmemory; }
650  return isEmpty;
651  }
652 
653  template <class T, class EnqPolicy>
654  typename ConcurrentQueue<T, EnqPolicy>::SizeType
656  {
657  LockType lock(protectElements_);
658  SizeType clearedEvents = size_;
659  elementsDropped_ += size_;
660  elements_.clear();
661  size_ = 0;
662  used_ = 0;
663  return clearedEvents;
664  }
665 
666  template <class T, class EnqPolicy>
667  void
669  {
670  LockType lock(protectElements_);
671  elementsDropped_ += n;
672  }
673 
674  //-----------------------------------------------------------
675  // Private member functions
676  //-----------------------------------------------------------
677 
678  template <class T, class EnqPolicy>
679  bool
681  {
682  if (isFull()) {
683  ++elementsDropped_;
684  return false;
685  }
686  else {
687  EnqPolicy::doInsert(item, elements_, size_,
688  detail::memoryUsage(item), used_, queueNotEmpty_);
689  return true;
690  }
691  }
692 
693  template <class T, class EnqPolicy>
694  bool
695  ConcurrentQueue<T, EnqPolicy>::removeHeadIfPossible(ValueType & item)
696  {
697  if (size_ == 0) { return false; }
698  removeHead(item);
699  return true;
700  }
701 
702  template <class T, class EnqPolicy>
703  void
704  ConcurrentQueue<T, EnqPolicy>::removeHead(ValueType & item)
705  {
706  SequenceType holder;
707  // Move the item out of elements_ in a manner that will not throw.
708  holder.splice(holder.begin(), elements_, elements_.begin());
709  // Record the change in the length of elements_.
710  --size_;
711  queueNotFull_.notify_one();
712  assignItem(item, holder.front());
713  used_ -= detail::memoryUsage(item);
714  }
715 
716  template <class T, class EnqPolicy>
717  void
718  ConcurrentQueue<T, EnqPolicy>::assignItem(T & item, const T & element)
719  {
720  item = element;
721  }
722 
723  template <class T, class EnqPolicy>
724  void
725  ConcurrentQueue<T, EnqPolicy>::assignItem(std::pair<T, size_t> & item, const T & element)
726  {
727  item.first = element;
728  item.second = elementsDropped_;
729  elementsDropped_ = 0;
730  }
731 
732  template <class T, class EnqPolicy>
733  bool
734  ConcurrentQueue<T, EnqPolicy>::isFull() const
735  {
736  if (size_ >= capacity_ || used_ >= memory_) { return true; }
737  return false;
738  }
739 
740 } // namespace daqrate
741 
742 #endif /* artdaq_core_Core_ConcurrentQueue_hh */
743 
745 
751 
bool enqTimedWait(T const &p, seconds const &)
bool setMemory(detail::MemoryType n)
void addExternallyDroppedEvents(SizeType)
ConcurrentQueue(SizeType maxSize=std::numeric_limits< SizeType >::max(), detail::MemoryType maxMemory=std::numeric_limits< detail::MemoryType >::max())
detail::MemoryType memory() const
bool deqNowait(ValueType &)
EnqPolicy::ReturnType enqNowait(T const &item)
bool deqTimedWait(ValueType &, seconds const &)
SizeType capacity() const
void deqWait(ValueType &)
bool setCapacity(SizeType n)
detail::MemoryType used() const