You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
181 lines
4.6 KiB
C++
181 lines
4.6 KiB
C++
4 years ago
|
// This file is under GNU General Public License 3.0
|
||
|
// see LICENSE.txt
|
||
![]()
5 years ago
|
|
||
2 years ago
|
#ifndef LIBPEPADAPTER_LOCKED_QUEUE_HH
|
||
|
#define LIBPEPADAPTER_LOCKED_QUEUE_HH
|
||
![]()
5 years ago
|
|
||
3 years ago
|
#include <deque>
|
||
5 years ago
|
#include <condition_variable>
|
||
![]()
5 years ago
|
#include <mutex>
|
||
|
|
||
2 years ago
|
namespace utility {
|
||
|
template<class T, void (*Deleter)(T) = nullptr>
|
||
|
class locked_queue {
|
||
|
typedef std::recursive_mutex Mutex;
|
||
|
typedef std::unique_lock<Mutex> Lock;
|
||
|
|
||
|
int _waiting = 0;
|
||
|
Mutex _mtx;
|
||
|
std::condition_variable_any _cv;
|
||
3 years ago
|
std::deque<T> _q;
|
||
![]()
5 years ago
|
|
||
|
public:
|
||
5 years ago
|
~locked_queue()
|
||
|
{
|
||
|
clear();
|
||
|
}
|
||
|
|
||
|
void clear()
|
||
|
{
|
||
|
Lock L(_mtx);
|
||
2 years ago
|
if (Deleter != nullptr) {
|
||
|
for (auto q : _q) {
|
||
5 years ago
|
Deleter(q);
|
||
|
}
|
||
|
}
|
||
5 years ago
|
_q.clear();
|
||
|
}
|
||
|
|
||
3 years ago
|
// defined behavior when queue empty
|
||
3 years ago
|
T back()
|
||
![]()
5 years ago
|
{
|
||
5 years ago
|
Lock lg(_mtx);
|
||
3 years ago
|
if (_q.empty())
|
||
|
throw std::underflow_error("queue empty");
|
||
![]()
5 years ago
|
return _q.back();
|
||
|
}
|
||
5 years ago
|
|
||
3 years ago
|
// defined behavior when queue empty
|
||
3 years ago
|
T front()
|
||
![]()
5 years ago
|
{
|
||
5 years ago
|
Lock lg(_mtx);
|
||
3 years ago
|
if (_q.empty())
|
||
|
throw std::underflow_error("queue empty");
|
||
![]()
5 years ago
|
return _q.front();
|
||
|
}
|
||
5 years ago
|
|
||
|
// returns a copy of the last element.
|
||
|
// blocks when queue is empty
|
||
![]()
5 years ago
|
T pop_back()
|
||
|
{
|
||
5 years ago
|
Lock L(_mtx);
|
||
2 years ago
|
_cv.wait(L, [&] { return !_q.empty(); });
|
||
5 years ago
|
T r{std::move(_q.back())};
|
||
![]()
5 years ago
|
_q.pop_back();
|
||
|
return r;
|
||
|
}
|
||
5 years ago
|
|
||
|
// returns a copy of the first element.
|
||
|
// blocks when queue is empty
|
||
![]()
5 years ago
|
T pop_front()
|
||
|
{
|
||
5 years ago
|
Lock L(_mtx);
|
||
2 years ago
|
_cv.wait(L, [&] { return !_q.empty(); });
|
||
5 years ago
|
T r{std::move(_q.front())};
|
||
![]()
5 years ago
|
_q.pop_front();
|
||
|
return r;
|
||
|
}
|
||
5 years ago
|
|
||
|
// returns true and set a copy of the last element and pop it from queue if there is any
|
||
|
// returns false and leaves 'out' untouched if queue is empty even after 'end_time'
|
||
|
bool try_pop_back(T& out, std::chrono::steady_clock::time_point end_time)
|
||
|
{
|
||
|
Lock L(_mtx);
|
||
3 years ago
|
++_waiting;
|
||
2 years ago
|
if (!_cv.wait_until(L, end_time, [this] { return !_q.empty(); })) {
|
||
3 years ago
|
--_waiting;
|
||
5 years ago
|
return false;
|
||
|
}
|
||
2 years ago
|
|
||
3 years ago
|
--_waiting;
|
||
5 years ago
|
out = std::move(_q.back());
|
||
|
_q.pop_back();
|
||
|
return true;
|
||
|
}
|
||
2 years ago
|
|
||
5 years ago
|
// returns true and set a copy of the first element and pop it from queue if there is any
|
||
|
// returns false and leaves 'out' untouched if queue is empty even after 'end_time'
|
||
|
bool try_pop_front(T& out, std::chrono::steady_clock::time_point end_time)
|
||
|
{
|
||
|
Lock L(_mtx);
|
||
3 years ago
|
++_waiting;
|
||
2 years ago
|
if (!_cv.wait_until(L, end_time, [this] { return !_q.empty(); })) {
|
||
3 years ago
|
--_waiting;
|
||
5 years ago
|
return false;
|
||
|
}
|
||
2 years ago
|
|
||
3 years ago
|
--_waiting;
|
||
5 years ago
|
out = std::move(_q.front());
|
||
|
_q.pop_front();
|
||
|
return true;
|
||
|
}
|
||
|
|
||
5 years ago
|
// returns true and set a copy of the first element and pop it from queue if there is any
|
||
|
// returns false and leaves 'out' untouched if queue is empty even after 'duration'
|
||
|
bool try_pop_front(T& out, std::chrono::seconds duration)
|
||
|
{
|
||
|
Lock L(_mtx);
|
||
3 years ago
|
++_waiting;
|
||
2 years ago
|
if (!_cv.wait_for(L, duration, [this] { return !_q.empty(); })) {
|
||
3 years ago
|
--_waiting;
|
||
5 years ago
|
return false;
|
||
|
}
|
||
2 years ago
|
|
||
3 years ago
|
--_waiting;
|
||
5 years ago
|
out = std::move(_q.front());
|
||
|
_q.pop_front();
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
![]()
5 years ago
|
void push_back(const T& data)
|
||
|
{
|
||
5 years ago
|
{
|
||
|
Lock L(_mtx);
|
||
|
_q.push_back(data);
|
||
|
}
|
||
|
_cv.notify_one();
|
||
![]()
5 years ago
|
}
|
||
5 years ago
|
|
||
![]()
5 years ago
|
void emplace_back(const T& data)
|
||
|
{
|
||
|
{
|
||
|
Lock L(_mtx);
|
||
|
_q.emplace_back(data);
|
||
|
}
|
||
|
_cv.notify_one();
|
||
|
}
|
||
5 years ago
|
|
||
![]()
5 years ago
|
void push_front(const T& data)
|
||
|
{
|
||
5 years ago
|
{
|
||
|
Lock L(_mtx);
|
||
|
_q.push_front(data);
|
||
|
}
|
||
|
_cv.notify_one();
|
||
![]()
5 years ago
|
}
|
||
5 years ago
|
|
||
![]()
5 years ago
|
size_t size()
|
||
|
{
|
||
5 years ago
|
Lock lg(_mtx);
|
||
![]()
5 years ago
|
return _q.size();
|
||
|
}
|
||
5 years ago
|
|
||
![]()
5 years ago
|
bool empty()
|
||
|
{
|
||
5 years ago
|
Lock lg(_mtx);
|
||
![]()
5 years ago
|
return _q.empty();
|
||
|
}
|
||
3 years ago
|
|
||
|
// returns the number of threads that are waiting in pop_...() or try_pop_...()
|
||
|
int waiting()
|
||
|
{
|
||
|
Lock L(_mtx);
|
||
|
return _waiting;
|
||
|
}
|
||
![]()
5 years ago
|
};
|
||
![]()
5 years ago
|
|
||
3 years ago
|
} // end of namespace utility
|
||
2 years ago
|
|
||
|
#endif // LIBPEPADAPTER_LOCKED_QUEUE_HH
|