File indexing completed on 2024-04-28 04:52:04

0001 /*
0002     SPDX-FileCopyrightText: 2015 Meltytech LLC
0003     SPDX-FileCopyrightText: 2015 Brian Matherly <code@brianmatherly.com>
0004 
0005     SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0006 */
0007 
0008 #pragma once
0009 
0010 #include <QList>
0011 #include <QMutex>
0012 #include <QMutexLocker>
0013 #include <QWaitCondition>
0014 
0015 /*!
0016   \class DataQueue
0017   \brief The DataQueue provides a thread safe container for passing data between
0018   objects.
0019 
0020   threadsafe
0021 
0022   DataQueue provides a limited size container for passing data between objects.
0023   One object can add data to the queue by calling push() while another object
0024   can remove items from the queue by calling pop().
0025 
0026   DataQueue provides configurable behavior for handling overflows. It can
0027   discard the oldest, discard the newest or block the object calling push()
0028   until room has been freed in the queue by another object calling pop().
0029 
0030   DataQueue is threadsafe and is therefore most appropriate when passing data
0031   between objects operating in different thread contexts.
0032 */
0033 
0034 template <class T> class DataQueue
0035 {
0036 public:
0037     //! Overflow behavior modes.
0038     typedef enum {
0039         OverflowModeDiscardOldest = 0, //!< Discard oldest items
0040         OverflowModeDiscardNewest,     //!< Discard newest items
0041         OverflowModeWait               //!< Wait for space to be free
0042     } OverflowMode;
0043 
0044     /*!
0045       Constructs a DataQueue.
0046 
0047       The \a size will be the maximum queue size and the \a mode will dictate
0048       overflow behavior.
0049     */
0050     explicit DataQueue(int maxSize, OverflowMode mode);
0051 
0052     //! Destructs a DataQueue.
0053     virtual ~DataQueue();
0054 
0055     /*!
0056       Pushes an item into the queue.
0057 
0058       If the queue is full and overflow mode is OverflowModeWait then this
0059       function will block until pop() is called.
0060     */
0061     void push(const T &item);
0062 
0063     /*!
0064       Pops an item from the queue.
0065 
0066       If the queue is empty then this  function will block. If blocking is
0067       undesired, then check the return of count() before calling pop().
0068     */
0069     T pop();
0070 
0071     //! Returns the number of items in the queue.
0072     int count() const;
0073 
0074 private:
0075     QList<T> m_queue;
0076     int m_maxSize;
0077     OverflowMode m_mode;
0078     mutable QMutex m_mutex;
0079     QWaitCondition m_notEmptyCondition;
0080     QWaitCondition m_notFullCondition;
0081 };
0082 
0083 template <class T>
0084 DataQueue<T>::DataQueue(int maxSize, OverflowMode mode)
0085     : m_queue()
0086     , m_maxSize(maxSize)
0087     , m_mode(mode)
0088     , m_mutex()
0089     , m_notEmptyCondition()
0090     , m_notFullCondition()
0091 {
0092 }
0093 
0094 template <class T> DataQueue<T>::~DataQueue() = default;
0095 
0096 template <class T> void DataQueue<T>::push(const T &item)
0097 {
0098     m_mutex.lock();
0099     if (m_queue.size() == m_maxSize) {
0100         switch (m_mode) {
0101         case OverflowModeDiscardOldest:
0102             m_queue.removeFirst();
0103             m_queue.append(item);
0104             break;
0105         case OverflowModeDiscardNewest:
0106             // This item is the newest so discard it and exit
0107             break;
0108         case OverflowModeWait:
0109             m_notFullCondition.wait(&m_mutex);
0110             m_queue.append(item);
0111             break;
0112         }
0113     } else {
0114         m_queue.append(item);
0115         if (m_queue.size() == 1) {
0116             m_notEmptyCondition.wakeOne();
0117         }
0118     }
0119     m_mutex.unlock();
0120 }
0121 
0122 template <class T> T DataQueue<T>::pop()
0123 {
0124     T retVal;
0125     m_mutex.lock();
0126     if (m_queue.size() == 0) {
0127         m_notEmptyCondition.wait(&m_mutex);
0128     }
0129     retVal = m_queue.takeFirst();
0130     if (m_mode == OverflowModeWait && m_queue.size() == m_maxSize - 1) {
0131         m_notFullCondition.wakeOne();
0132     }
0133     m_mutex.unlock();
0134     return retVal;
0135 }
0136 
0137 template <class T> int DataQueue<T>::count() const
0138 {
0139     QMutexLocker locker(&m_mutex);
0140     return m_queue.size();
0141 }