From edba43927925cf3ae5c3ec890a040d2220f8bf71 Mon Sep 17 00:00:00 2001 From: Roker Date: Thu, 18 Oct 2018 16:30:16 +0200 Subject: [PATCH] added try_pop_front() and try_pop_back(). normal pop_*() now blocks instead of Undefined Behavior. --- locked_queue.hh | 107 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 90 insertions(+), 17 deletions(-) diff --git a/locked_queue.hh b/locked_queue.hh index af93958..6f7af2b 100644 --- a/locked_queue.hh +++ b/locked_queue.hh @@ -4,60 +4,133 @@ #pragma once #include +#include #include namespace utility { - using namespace std; - - template class locked_queue + template + class locked_queue { - mutex _mtx; - list _q; + typedef std::recursive_mutex Mutex; + typedef std::unique_lock Lock; + + Mutex _mtx; + std::condition_variable_any _cv; + std::list _q; public: + ~locked_queue() + { + clear(); + } + + void clear() + { + Lock L(_mtx); + for(auto& element : _q) + { + Deleter(element); + } + _q.clear(); + } + + // undefined behavior when queue empty T& back() { - lock_guard lg(_mtx); + Lock lg(_mtx); return _q.back(); } + + // undefined behavior when queue empty T& front() { - lock_guard lg(_mtx); + Lock lg(_mtx); return _q.front(); } + + // returns a copy of the last element. + // blocks when queue is empty T pop_back() { - lock_guard lg(_mtx); - T r = _q.back(); + Lock L(_mtx); + _cv.wait(L, [&]{ return !_q.empty(); } ); + T ret{std::move(_q.back())}; _q.pop_back(); return r; } + + // returns a copy of the first element. + // blocks when queue is empty T pop_front() { - lock_guard lg(_mtx); - T r = _q.front(); + Lock L(_mtx); + _cv.wait(L, [&]{ return !_q.empty(); } ); + T ret{std::move(_q.front())}; _q.pop_front(); return r; } + + + // 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); + if(! _cv.wait_until(L, end_time, [this]{ return !_q.empty(); } ) ) + { + return false; + } + + out = std::move(_q.back()); + _q.pop_back(); + return true; + } + + + // 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); + if(! _cv.wait_until(L, end_time, [this]{ return !_q.empty(); } ) ) + { + return false; + } + + out = std::move(_q.front()); + _q.pop_front(); + return true; + } + void push_back(const T& data) { - lock_guard lg(_mtx); - _q.push_back(data); + { + Lock L(_mtx); + _q.push_back(data); + } + _cv.notify_one(); } + + void push_front(const T& data) { - lock_guard lg(_mtx); - _q.push_front(data); + { + Lock L(_mtx); + _q.push_front(data); + } + _cv.notify_one(); } + size_t size() { - lock_guard lg(_mtx); + Lock lg(_mtx); return _q.size(); } + bool empty() { - lock_guard lg(_mtx); + Lock lg(_mtx); return _q.empty(); } };