summaryrefslogtreecommitdiff
path: root/include/mts/MTS_Queue.h
diff options
context:
space:
mode:
Diffstat (limited to 'include/mts/MTS_Queue.h')
-rw-r--r--include/mts/MTS_Queue.h215
1 files changed, 215 insertions, 0 deletions
diff --git a/include/mts/MTS_Queue.h b/include/mts/MTS_Queue.h
new file mode 100644
index 0000000..aa59e51
--- /dev/null
+++ b/include/mts/MTS_Queue.h
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2015 by Multi-Tech Systems
+ *
+ * This file is part of libmts.
+ *
+ * libmts is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * libmts is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libmts. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/*! \file MTS_Queue.h
+ \brief A queue
+ \date 15JUN11
+ \author Sean Godinez
+
+ A thread-safe queue with condition signaling
+ */
+#ifndef _MTS_QUEUE_H_
+#define _MTS_QUEUE_H_
+
+#include <mts/MTS_NonCopyable.h>
+#include <mts/MTS_AutoPtr.h>
+#include <mts/MTS_Lock.h>
+#include <mts/MTS_Logger.h>
+#include <mts/MTS_Stdint.h>
+#include <queue>
+#include <cassert>
+
+namespace MTS {
+
+ //! A queue
+ /*!
+ A thread-safe, blocking, template queue that signals when it is not empty, and when it is not full.
+
+ \sa AutoPtr, Lock
+ */
+ template<class T> class Queue : public NonCopyable {
+
+ public:
+ explicit Queue(uint32_t capacity = UINT32_MAX); //!< Constructs the queue
+ ~Queue(); //!< Destructs the queue
+
+ bool empty() const; //!< Returns true if queue is empty
+ bool full() const; //!< Returns true if queue is full
+ uint32_t size() const; //!< Returns the number of elements in the queue
+ uint32_t capacity() const; //!< Returns the maximum capacity of the queue
+
+ void push(const T& object); //!< Pushes an object on the queue (BLOCKS IF FULL)
+ void pop(T& object); //!< Pops an object off the queue (BLOCKS IF EMPTY)
+
+ bool offer(const T& object, uint32_t waitMillis);
+ bool poll(T& object, uint32_t waitMillis);
+ bool peek(T& object, uint32_t waitMillis);
+
+ private:
+ const uint32_t m_iMaxSize;
+ std::queue<T> m_qContainer;
+ AutoPtr<Lock> m_apLock;
+ AutoPtr<Condition> m_apNotEmpty;
+ AutoPtr<Condition> m_apNotFull;
+ };
+
+ template<class T> Queue<T>::Queue(uint32_t capacity)
+ : m_iMaxSize(capacity) {
+ m_apLock.reset(new Lock());
+ m_apNotEmpty.reset(m_apLock->createCondition());
+ m_apNotFull.reset(m_apLock->createCondition());
+ }
+
+ template<class T> Queue<T>::~Queue() {
+ m_apNotFull.reset();
+ m_apNotEmpty.reset();
+ m_apLock.reset();
+ }
+
+ template<class T> bool Queue<T>::empty() const {
+ bool result = false;
+ m_apLock->lock();
+ result = m_qContainer.empty();
+ m_apLock->unlock();
+ return result;
+ }
+
+ template<class T> bool Queue<T>::full() const {
+ bool result = false;
+ m_apLock->lock();
+ result = (m_qContainer.size() >= m_iMaxSize);
+ m_apLock->unlock();
+ return result;
+ }
+
+ template<class T> uint32_t Queue<T>::size() const {
+ uint32_t result = 0;
+ m_apLock->lock();
+ result = static_cast<uint32_t>(m_qContainer.size());
+ m_apLock->unlock();
+ return result;
+ }
+
+ template<class T> uint32_t Queue<T>::capacity() const {
+ return m_iMaxSize;
+ }
+
+ template<class T> void Queue<T>::push(const T& object) {
+ // Block forever until there is room on the queue
+ m_apLock->lock();
+ try {
+ while (m_qContainer.size() >= m_iMaxSize) {
+ m_apNotFull->wait();
+ }
+ m_qContainer.push(object);
+ m_apNotEmpty->signal();
+ } catch (...) {
+ printWarning("Queue| failed to push object");
+ assert(false);
+ }
+ m_apLock->unlock();
+ }
+
+ template<class T> void Queue<T>::pop(T& object) {
+ //Block on not-empty condition if empty
+ m_apLock->lock();
+ while (m_qContainer.empty()) {
+ m_apNotEmpty->wait();
+ }
+ object = m_qContainer.front();
+ m_qContainer.pop();
+ m_apNotFull->signal();
+ m_apLock->unlock();
+ }
+
+ //! Attempts to push an object onto the queue within the given milliseconds
+ /*!
+ \param object an object to push onto the queue
+ \param waitMillis the amount of milliseonds to wait if the queue is full
+ \return Returns true if the object was pushed onto the queue, false otherwise
+ \sa push()
+ */
+ template<class T> bool Queue<T>::offer(const T& object,
+ uint32_t waitMillis) {
+ bool result = false;
+ m_apLock->lock();
+ try {
+ if (waitMillis > 0 && m_qContainer.size() >= m_iMaxSize) {
+ m_apNotFull->wait(waitMillis);
+ }
+ if (m_qContainer.size() < m_iMaxSize) {
+ m_qContainer.push(object);
+ m_apNotEmpty->signal();
+ result = true;
+ }
+ } catch (...) {
+ printWarning("Queue| failed to offer object");
+ assert(false);
+ }
+ m_apLock->unlock();
+ return result;
+ }
+
+ //! Attempts to pop an object from the queue within the given milliseconds
+ /*!
+ \param object if successful, a reference to the object that was popped
+ \param waitMillis the amount of milliseonds to wait if the queue is empty
+ \return Returns true if an object was popped from the queue, false otherwise
+ \sa pop(), peek()
+ */
+ template<class T> bool Queue<T>::poll(T& object, uint32_t waitMillis) {
+ bool result = false;
+ m_apLock->lock();
+ if (waitMillis > 0 && m_qContainer.empty()) {
+ m_apNotEmpty->wait(waitMillis);
+ }
+ if (!m_qContainer.empty()) {
+ object = m_qContainer.front();
+ m_qContainer.pop();
+ m_apNotFull->signal();
+ result = true;
+ }
+ m_apLock->unlock();
+ return result;
+ }
+
+ //! Attempts to view the top of the queue within the given milliseconds
+ /*!
+ \param object if successful, a reference to the object that is at the head of the queue
+ \param waitMillis the amount of milliseonds to wait if the queue is empty
+ \return Returns true if there is an object in the queue, false otherwise
+ \sa pop(), poll()
+ */
+ template<class T> bool Queue<T>::peek(T& object, uint32_t waitMillis) {
+ bool result = false;
+ m_apLock->lock();
+ if (waitMillis > 0 && m_qContainer.empty()) {
+ m_apNotEmpty->wait(waitMillis);
+ }
+ if (!m_qContainer.empty()) {
+ object = m_qContainer.front();
+ result = true;
+ }
+ m_apLock->unlock();
+ return result;
+ }
+}
+
+#endif