From b8ff0da6934d9229e7c64c0c84bceb3ec61530f6 Mon Sep 17 00:00:00 2001 From: Michael Carlberg Date: Tue, 20 Dec 2016 12:54:17 +0100 Subject: [PATCH] feat: Add taskqueue component --- include/components/taskqueue.hpp | 55 +++++++++++++++++++ src/components/taskqueue.cpp | 91 ++++++++++++++++++++++++++++++++ 2 files changed, 146 insertions(+) create mode 100644 include/components/taskqueue.hpp create mode 100644 src/components/taskqueue.cpp diff --git a/include/components/taskqueue.hpp b/include/components/taskqueue.hpp new file mode 100644 index 00000000..33fb806f --- /dev/null +++ b/include/components/taskqueue.hpp @@ -0,0 +1,55 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "common.hpp" +#include "utils/mixins.hpp" + +POLYBAR_NS + +namespace chrono = std::chrono; +using namespace std::chrono_literals; + +class taskqueue : non_copyable_mixin { + public: + struct deferred : non_copyable_mixin { + using timepoint = chrono::time_point; + using callback = function; + + explicit deferred(string&& id, timepoint::time_point&& tp, callback&& fn) + : id(forward(id)), when(forward(tp)), func(forward(fn)) {} + + const string id; + const timepoint when; + const callback func; + }; + + public: + using make_type = unique_ptr; + static make_type make(); + + explicit taskqueue(); + ~taskqueue(); + + void defer(string&& id, deferred::timepoint::duration&& ms, deferred::callback&& fn); + void defer_unique(string&& id, deferred::timepoint::duration&& ms, deferred::callback&& fn); + + bool has_deferred(string&& id); + + protected: + void tick(); + + private: + std::thread m_thread; + std::mutex m_lock; + std::condition_variable m_hold; + std::atomic_bool m_active{true}; + + vector> m_deferred; +}; + +POLYBAR_NS_END diff --git a/src/components/taskqueue.cpp b/src/components/taskqueue.cpp new file mode 100644 index 00000000..e8c4b9a0 --- /dev/null +++ b/src/components/taskqueue.cpp @@ -0,0 +1,91 @@ +#include + +#include "components/taskqueue.hpp" +#include "utils/factory.hpp" + +POLYBAR_NS + +taskqueue::make_type taskqueue::make() { + return factory_util::unique(); +} + +taskqueue::taskqueue() { + m_thread = std::thread([&] { + while (m_active) { + std::unique_lock guard(m_lock); + + if (m_deferred.empty()) { + m_hold.wait(guard); + } else { + auto now = deferred::timepoint::clock::now(); + auto wait = m_deferred.front()->when; + for (auto&& task : m_deferred) { + if (task->when < wait) { + wait = task->when; + } + } + if (wait > now) { + m_hold.wait_for(guard, wait - now); + } + } + if (!m_deferred.empty()) { + guard.unlock(); + tick(); + } + } + }); +} + +taskqueue::~taskqueue() { + if (m_active && m_thread.joinable()) { + m_active = false; + m_hold.notify_all(); + m_thread.join(); + } +} + +void taskqueue::defer(string&& id, deferred::timepoint::duration&& ms, deferred::callback&& fn) { + std::unique_lock guard(m_lock); + auto when = chrono::time_point_cast(deferred::timepoint::clock::now() + ms); + m_deferred.emplace_back(make_unique(forward(id), move(when), forward(fn))); + guard.unlock(); + m_hold.notify_one(); +} + +void taskqueue::defer_unique(string&& id, deferred::timepoint::duration&& ms, deferred::callback&& fn) { + std::unique_lock guard(m_lock); + for (auto it = m_deferred.rbegin(); it != m_deferred.rend(); ++it) { + if ((*it)->id == id) { + m_deferred.erase(std::remove(m_deferred.begin(), m_deferred.end(), (*it)), m_deferred.end()); + } + } + auto when = chrono::time_point_cast(deferred::timepoint::clock::now() + ms); + m_deferred.emplace_back(make_unique(forward(id), move(when), forward(fn))); + guard.unlock(); + m_hold.notify_one(); +} + +void taskqueue::tick() { + if (m_lock.try_lock()) { + std::lock_guard guard(m_lock, std::adopt_lock); + auto now = deferred::timepoint::clock::now(); + for (auto it = m_deferred.rbegin(); it != m_deferred.rend(); ++it) { + if ((*it)->when <= now) { + (*it)->func(); + m_deferred.erase(std::remove(m_deferred.begin(), m_deferred.end(), (*it)), m_deferred.end()); + } + } + } +} + +bool taskqueue::has_deferred(string&& id) { + std::lock_guard guard(m_lock); + for (const auto& task : m_deferred) { + if (task->id == id) { + return true; + } + } + return false; +} + +POLYBAR_NS_END