|
#ifndef __PROTECTEDQUEUE_H__
|
|
#define __PROTECTEDQUEUE_H__
|
|
|
|
#include <condition_variable>
|
|
#include <functional>
|
|
#include <iostream>
|
|
#include <queue>
|
|
#include <mutex>
|
|
|
|
|
|
template<class T>
|
|
class ProtectedQueue
|
|
{
|
|
public:
|
|
|
|
ProtectedQueue()
|
|
: m_terminateFlag(false),
|
|
p_itemAddedCallback(nullptr),
|
|
p_itemRemovedCallback(nullptr)
|
|
{}
|
|
|
|
virtual ~ProtectedQueue() {}
|
|
|
|
/**
|
|
* Add a new element to the back of the queue
|
|
*/
|
|
inline void push_back(T newElement)
|
|
{
|
|
{
|
|
std::lock_guard<std::mutex> lock(m_mutex);
|
|
m_deque.push_back(newElement);
|
|
|
|
// Call item added callback if one exists
|
|
if (p_itemAddedCallback != nullptr)
|
|
{
|
|
(*p_itemAddedCallback)(m_deque.size());
|
|
}
|
|
}
|
|
m_CV.notify_one();
|
|
}
|
|
|
|
inline void push_front(T newElement)
|
|
{
|
|
{
|
|
std::lock_guard<std::mutex> lock(m_mutex);
|
|
m_deque.push_front(newElement);
|
|
|
|
// Call item added callback if one exists
|
|
if (p_itemAddedCallback != nullptr)
|
|
{
|
|
(*p_itemAddedCallback)(m_deque.size());
|
|
}
|
|
}
|
|
m_CV.notify_one();
|
|
}
|
|
|
|
/**
|
|
* Retrieve the first element of the queue and remove it from the queue
|
|
*/
|
|
inline T pop_front()
|
|
{
|
|
// Wait on queue blocking
|
|
std::unique_lock<std::mutex> muxLock(m_mutex);
|
|
while (m_deque.empty())
|
|
{
|
|
m_CV.wait_for(muxLock, std::chrono::milliseconds(100));
|
|
|
|
// If we've been asked to terminate, break out and return
|
|
// NOTE: the return value is garbage if terminate has been
|
|
// called prior this call returning
|
|
if (m_terminateFlag)
|
|
{
|
|
T elementToReturn{};
|
|
return elementToReturn;
|
|
}
|
|
}
|
|
|
|
T elementToReturn = m_deque.front();
|
|
m_deque.pop_front();
|
|
|
|
// Call item removed callback if one exists
|
|
if (p_itemRemovedCallback != nullptr)
|
|
{
|
|
(*p_itemRemovedCallback)(m_deque.size());
|
|
}
|
|
|
|
return elementToReturn;
|
|
}
|
|
|
|
/**
|
|
* Retrieve the first element of the queue and remove it from the queue
|
|
*/
|
|
inline T pop_front(bool waitOnEmptyQueue, bool& elementReturned)
|
|
{
|
|
// Wait on queue blocking
|
|
std::unique_lock<std::mutex> muxLock(m_mutex);
|
|
if (waitOnEmptyQueue)
|
|
{
|
|
while (m_deque.empty())
|
|
{
|
|
m_CV.wait_for(muxLock, std::chrono::milliseconds(100));
|
|
|
|
// If we've been asked to terminate, break out and return
|
|
// NOTE: the return value is garbage if terminate has been
|
|
// called prior this call returning
|
|
if (m_terminateFlag)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
T elementToReturn{};
|
|
if (m_deque.size() > 0)
|
|
{
|
|
elementReturned = true;
|
|
elementToReturn = m_deque.front();
|
|
m_deque.pop_front();
|
|
|
|
// Call item removed callback if one exists
|
|
if (p_itemRemovedCallback != nullptr)
|
|
{
|
|
(*p_itemRemovedCallback)(m_deque.size());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
elementReturned = false;
|
|
}
|
|
|
|
return elementToReturn;
|
|
}
|
|
|
|
/**
|
|
* Terminate any waiting pop_front() requests
|
|
*/
|
|
void terminate()
|
|
{
|
|
m_terminateFlag = true;
|
|
m_CV.notify_all();
|
|
}
|
|
|
|
/*
|
|
* Resets the terminate flag to allow the queue to function again after
|
|
* being terminated previously
|
|
*/
|
|
void unTerminate()
|
|
{
|
|
m_terminateFlag = false;
|
|
}
|
|
|
|
void cleanup(std::function<void(T)>& callback)
|
|
{
|
|
// Call the callback on all remaining items in the queue purging
|
|
// each afterwards
|
|
{
|
|
std::lock_guard<std::mutex> lock(m_mutex);
|
|
while (! m_deque.empty())
|
|
{
|
|
callback(m_deque.front());
|
|
m_deque.pop_front();
|
|
}
|
|
}
|
|
m_CV.notify_all();
|
|
}
|
|
|
|
inline bool empty() const
|
|
{
|
|
std::lock_guard<std::mutex> lock(m_mutex);
|
|
return m_deque.empty();
|
|
}
|
|
|
|
inline uint32_t size() const
|
|
{
|
|
std::lock_guard<std::mutex> lock(m_mutex);
|
|
return m_deque.size();
|
|
}
|
|
|
|
inline void setItemAddedCallback(std::function<void(uint32_t)>& callback)
|
|
{
|
|
p_itemAddedCallback = &callback;
|
|
}
|
|
|
|
inline void setItemRemovedCallback(std::function<void(uint32_t)>& callback)
|
|
{
|
|
p_itemRemovedCallback = &callback;
|
|
}
|
|
|
|
/**
|
|
* Get the position of item in the queue
|
|
*
|
|
* @return position of item in queue if it exists, else UINT32_MAX
|
|
*/
|
|
uint32_t getPositionOf(T item) const
|
|
{
|
|
std::lock_guard<std::mutex> lock(m_mutex);
|
|
uint32_t position = 0;
|
|
for (auto queueItem : m_deque)
|
|
{
|
|
if (item == queueItem)
|
|
{
|
|
return position;
|
|
}
|
|
position ++;
|
|
}
|
|
|
|
return UINT32_MAX;
|
|
}
|
|
|
|
/**
|
|
* Removes item from queue. If item does not exist in queue, nothing
|
|
* will happen
|
|
*
|
|
* @param item Item to remove
|
|
*/
|
|
void removeItem(T item)
|
|
{
|
|
std::lock_guard<std::mutex> lock(m_mutex);
|
|
|
|
// Erase item from deque
|
|
auto iter = m_deque.begin();
|
|
while (iter != m_deque.end())
|
|
{
|
|
if (*iter == item)
|
|
{
|
|
m_deque.erase(iter);
|
|
return;
|
|
}
|
|
++iter;
|
|
}
|
|
}
|
|
|
|
uint32_t count(T item)
|
|
{
|
|
std::lock_guard<std::mutex> lock(m_mutex);
|
|
|
|
uint32_t count = 0;
|
|
for (auto queueItem : m_deque)
|
|
{
|
|
if (item == queueItem)
|
|
{
|
|
count++;
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
private:
|
|
|
|
bool m_terminateFlag;
|
|
|
|
std::deque<T> m_deque;
|
|
mutable std::mutex m_mutex;
|
|
std::condition_variable m_CV;
|
|
|
|
std::function<void(uint32_t)>* p_itemAddedCallback;
|
|
std::function<void(uint32_t)>* p_itemRemovedCallback;
|
|
|
|
};
|
|
|
|
#endif
|