Support deferred events in synchronyzed_queue & remove timeout methods
This commit is contained in:
parent
4fa7e1f824
commit
a8a1f44a89
1 changed files with 158 additions and 63 deletions
|
|
@ -1,11 +1,12 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <deque>
|
#include <deque>
|
||||||
|
#include <vector>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <thread>
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
namespace psemek::util
|
namespace psemek::util
|
||||||
{
|
{
|
||||||
|
|
@ -13,6 +14,8 @@ namespace psemek::util
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct synchronized_queue
|
struct synchronized_queue
|
||||||
{
|
{
|
||||||
|
using clock = std::chrono::high_resolution_clock;
|
||||||
|
|
||||||
synchronized_queue(std::size_t max_size = std::numeric_limits<std::size_t>::max()) noexcept
|
synchronized_queue(std::size_t max_size = std::numeric_limits<std::size_t>::max()) noexcept
|
||||||
: max_size_(max_size)
|
: max_size_(max_size)
|
||||||
{}
|
{}
|
||||||
|
|
@ -24,15 +27,16 @@ namespace psemek::util
|
||||||
|
|
||||||
void push(T const & x);
|
void push(T const & x);
|
||||||
void push(T && x);
|
void push(T && x);
|
||||||
|
|
||||||
|
void push_at(clock::time_point time, T const & x);
|
||||||
|
void push_at(clock::time_point time, T && x);
|
||||||
|
|
||||||
T pop();
|
T pop();
|
||||||
|
|
||||||
bool try_push(T const & x);
|
bool try_push(T const & x);
|
||||||
template <typename Rep, typename Period>
|
bool try_push(T && x);
|
||||||
bool try_push(T const & x, std::chrono::duration<Rep, Period> const & timeout);
|
|
||||||
|
|
||||||
std::optional<T> try_pop();
|
std::optional<T> try_pop();
|
||||||
template <typename Rep, typename Period>
|
|
||||||
std::optional<T> try_pop(std::chrono::duration<Rep, Period> const & timeout);
|
|
||||||
|
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
|
|
@ -41,116 +45,207 @@ namespace psemek::util
|
||||||
void wait();
|
void wait();
|
||||||
|
|
||||||
std::size_t size() const;
|
std::size_t size() const;
|
||||||
|
bool empty() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
mutable std::mutex mutex;
|
struct deferred
|
||||||
std::condition_variable push_cv, pop_cv;
|
{
|
||||||
std::deque<T> queue;
|
clock::time_point time;
|
||||||
std::size_t const max_size_;
|
T value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
mutable std::mutex mutex_;
|
||||||
|
std::condition_variable push_cv_, pop_cv_;
|
||||||
|
std::deque<T> queue_;
|
||||||
|
std::vector<deferred> deferred_heap_;
|
||||||
|
std::size_t const max_size_;
|
||||||
|
|
||||||
|
static auto heap_compare();
|
||||||
|
|
||||||
|
void flush_deferred();
|
||||||
|
|
||||||
|
std::size_t size_internal() const;
|
||||||
|
bool empty_internal() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
auto synchronized_queue<T>::heap_compare()
|
||||||
|
{
|
||||||
|
return [](deferred const & d1, deferred const & d2){ return d1.time > d2.time; };
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void synchronized_queue<T>::push(T const & x)
|
void synchronized_queue<T>::push(T const & x)
|
||||||
{
|
{
|
||||||
std::unique_lock lock{mutex};
|
std::unique_lock lock{mutex_};
|
||||||
push_cv.wait(lock, [this]{ return queue.size() < max_size(); });
|
push_cv_.wait(lock, [this]{ return size_internal() < max_size(); });
|
||||||
queue.push_back(x);
|
queue_.push_back(x);
|
||||||
pop_cv.notify_one();
|
lock.unlock();
|
||||||
|
pop_cv_.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void synchronized_queue<T>::push (T && x)
|
void synchronized_queue<T>::push (T && x)
|
||||||
{
|
{
|
||||||
std::unique_lock lock{mutex};
|
std::unique_lock lock{mutex_};
|
||||||
push_cv.wait(lock, [this]{ return queue.size() < max_size(); });
|
push_cv_.wait(lock, [this]{ return size_internal() < max_size(); });
|
||||||
queue.push_back(std::move(x));
|
queue_.push_back(std::move(x));
|
||||||
pop_cv.notify_one();
|
lock.unlock();
|
||||||
|
pop_cv_.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void synchronized_queue<T>::push_at(clock::time_point time, T const & x)
|
||||||
|
{
|
||||||
|
std::unique_lock lock{mutex_};
|
||||||
|
push_cv_.wait(lock, [this]{ return size_internal() < max_size(); });
|
||||||
|
if (time > clock::now())
|
||||||
|
{
|
||||||
|
deferred_heap_.push_back({time, x});
|
||||||
|
std::push_heap(deferred_heap_.begin(), deferred_heap_.end(), heap_compare());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
queue_.push_back(x);
|
||||||
|
}
|
||||||
|
lock.unlock();
|
||||||
|
pop_cv_.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void synchronized_queue<T>::push_at(clock::time_point time, T && x)
|
||||||
|
{
|
||||||
|
std::unique_lock lock{mutex_};
|
||||||
|
push_cv_.wait(lock, [this]{ return size_internal() < max_size(); });
|
||||||
|
if (time > clock::now())
|
||||||
|
{
|
||||||
|
deferred_heap_.push_back({time, std::move(x)});
|
||||||
|
std::push_heap(deferred_heap_.begin(), deferred_heap_.end(), heap_compare());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
queue_.push_back(std::move(x));
|
||||||
|
}
|
||||||
|
lock.unlock();
|
||||||
|
pop_cv_.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T synchronized_queue<T>::pop()
|
T synchronized_queue<T>::pop()
|
||||||
{
|
{
|
||||||
std::unique_lock lock{mutex};
|
std::unique_lock lock{mutex_};
|
||||||
pop_cv.wait(lock, [this]{ return !queue.empty(); });
|
while (true)
|
||||||
T x = std::move(queue.front());
|
{
|
||||||
queue.pop_front();
|
pop_cv_.wait(lock, [this]{ return !empty_internal(); });
|
||||||
push_cv.notify_one();
|
flush_deferred();
|
||||||
|
if (!queue_.empty())
|
||||||
|
{
|
||||||
|
T x = std::move(queue_.front());
|
||||||
|
queue_.pop_front();
|
||||||
|
lock.unlock();
|
||||||
|
push_cv_.notify_one();
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pop_cv_.wait_until(lock, deferred_heap_.front().time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
bool synchronized_queue<T>::try_push(T const & x)
|
bool synchronized_queue<T>::try_push(T const & x)
|
||||||
{
|
{
|
||||||
std::lock_guard lock{mutex};
|
std::unique_lock lock{mutex_};
|
||||||
if (queue.size() >= max_size())
|
if (size_internal() >= max_size())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
queue.push_back(x);
|
queue_.push_back(x);
|
||||||
pop_cv.notify_one();
|
lock.unlock();
|
||||||
|
pop_cv_.notify_one();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
template <typename Rep, typename Period>
|
bool synchronized_queue<T>::try_push(T && x)
|
||||||
bool synchronized_queue<T>::try_push(T const & x, std::chrono::duration<Rep, Period> const & timeout)
|
|
||||||
{
|
{
|
||||||
std::unique_lock lock{mutex};
|
std::unique_lock lock{mutex_};
|
||||||
if (push_cv.wait_for(lock, timeout, [this]{ return queue.size() < max_size(); }))
|
if (size_internal() >= max_size())
|
||||||
{
|
|
||||||
queue.push_back(std::move(x));
|
|
||||||
pop_cv.notify_one();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
queue_.push_back(std::move(x));
|
||||||
|
lock.unlock();
|
||||||
|
pop_cv_.notify_one();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
std::optional<T> synchronized_queue<T>::try_pop()
|
std::optional<T> synchronized_queue<T>::try_pop()
|
||||||
{
|
{
|
||||||
std::lock_guard lock{mutex};
|
std::unique_lock lock{mutex_};
|
||||||
if (queue.empty())
|
flush_deferred();
|
||||||
|
if (queue_.empty())
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
T x = std::move(queue.front());
|
T x = std::move(queue_.front());
|
||||||
queue.pop_front();
|
queue_.pop_front();
|
||||||
push_cv.notify_one();
|
lock.unlock();
|
||||||
|
push_cv_.notify_one();
|
||||||
return { std::move(x) };
|
return { std::move(x) };
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
template <typename Rep, typename Period>
|
|
||||||
std::optional<T> synchronized_queue<T>::try_pop(std::chrono::duration<Rep, Period> const & timeout)
|
|
||||||
{
|
|
||||||
std::unique_lock lock{mutex};
|
|
||||||
if (pop_cv.wait_for(lock, timeout, [this]{ return !queue.empty(); }))
|
|
||||||
{
|
|
||||||
T x = std::move(queue.front());
|
|
||||||
queue.pop_front();
|
|
||||||
push_cv.notify_one();
|
|
||||||
return {std::move(x)};
|
|
||||||
}
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void synchronized_queue<T>::clear()
|
void synchronized_queue<T>::clear()
|
||||||
{
|
{
|
||||||
std::lock_guard lock{mutex};
|
std::unique_lock lock{mutex_};
|
||||||
queue.clear();
|
queue_.clear();
|
||||||
push_cv.notify_all();
|
deferred_heap_.clear();
|
||||||
|
lock.unlock();
|
||||||
|
push_cv_.notify_all();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void synchronized_queue<T>::wait()
|
void synchronized_queue<T>::wait()
|
||||||
{
|
{
|
||||||
std::unique_lock lock{mutex};
|
std::unique_lock lock{mutex_};
|
||||||
push_cv.wait(lock, [this]{ return queue.empty(); });
|
push_cv_.wait(lock, [this]{ return empty_internal(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
std::size_t synchronized_queue<T>::size() const
|
std::size_t synchronized_queue<T>::size() const
|
||||||
{
|
{
|
||||||
std::lock_guard lock{mutex};
|
std::lock_guard lock{mutex_};
|
||||||
return queue.size();
|
return size_internal();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool synchronized_queue<T>::empty() const
|
||||||
|
{
|
||||||
|
std::lock_guard lock{mutex_};
|
||||||
|
return empty_internal();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void synchronized_queue<T>::flush_deferred()
|
||||||
|
{
|
||||||
|
auto const now = clock::now();
|
||||||
|
while (!deferred_heap_.empty() && deferred_heap_.front().time <= now)
|
||||||
|
{
|
||||||
|
queue_.push_back(std::move(deferred_heap_.front().value));
|
||||||
|
std::pop_heap(deferred_heap_.begin(), deferred_heap_.end(), heap_compare());
|
||||||
|
deferred_heap_.pop_back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
std::size_t synchronized_queue<T>::size_internal() const
|
||||||
|
{
|
||||||
|
return queue_.size() + deferred_heap_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool synchronized_queue<T>::empty_internal() const
|
||||||
|
{
|
||||||
|
return queue_.empty() && deferred_heap_.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue