Last active
March 7, 2024 18:12
-
-
Save b-epelbaum/088f8862faa1aa8ff7050e18c78c4ac4 to your computer and use it in GitHub Desktop.
Stackable Qt task queue with single timer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <functional> | |
| #include <queue> | |
| #include <chrono> | |
| #include <memory> | |
| #include <tuple> | |
| #include <QTimer> | |
| #include <QDateTime> | |
| namespace Helpers | |
| { | |
| class priorityTasker : public QObject | |
| { | |
| Q_OBJECT | |
| class baseTask; | |
| QTimer _timer{}; | |
| std::vector<std::shared_ptr<baseTask>> _tasks; | |
| class baseTask | |
| { | |
| public: | |
| baseTask(const int timeout, const bool is_periodic) | |
| : _timeout(timeout), | |
| _is_periodic(is_periodic), | |
| _interval(_timeout), | |
| _expiration_time(QDateTime::currentMSecsSinceEpoch() + _timeout) | |
| {} | |
| baseTask(const baseTask&) = delete; | |
| baseTask(baseTask&&) = delete; | |
| baseTask& operator =(const baseTask&) = delete; | |
| baseTask& operator = (baseTask&&) = delete; | |
| virtual ~baseTask() = default; | |
| virtual void run() = 0; | |
| [[nodiscard]] uint64_t get_expiration_time() const { return _expiration_time; } | |
| void reset_expiration_time() | |
| { | |
| _expiration_time = QDateTime::currentMSecsSinceEpoch() + _interval; | |
| } | |
| int _timeout{}; | |
| bool _is_periodic{}; | |
| uint64_t _interval{}; | |
| uint64_t _expiration_time{}; | |
| }; | |
| template<typename... Args> | |
| class priorityTask : public baseTask | |
| { | |
| public: | |
| priorityTask(const int timeout, const bool is_periodic, std::function<void(Args...)> callback, Args... args) | |
| : baseTask(timeout, is_periodic) | |
| , _callback(std::move(callback)) | |
| , _args(std::make_tuple(std::forward<Args>(args)...)) | |
| {} | |
| void run() override | |
| { | |
| std::apply(_callback, _args); | |
| if (_is_periodic) | |
| reset_expiration_time(); | |
| else | |
| _timeout = INT_MAX; | |
| } | |
| private: | |
| std::function<void(Args...)> _callback{}; | |
| std::tuple<Args...> _args; | |
| }; | |
| template<> | |
| class priorityTask<> : public baseTask | |
| { | |
| public: | |
| priorityTask(const int timeout, const bool is_periodic, std::function<void()> callback) | |
| : baseTask(timeout, is_periodic) | |
| , _callback(std::move(callback)) | |
| {} | |
| void run() override | |
| { | |
| _callback(); | |
| if (_is_periodic) | |
| reset_expiration_time(); | |
| else | |
| _timeout = INT_MAX; | |
| } | |
| private: | |
| std::function<void()> _callback; | |
| }; | |
| public: | |
| explicit priorityTasker(QObject* parent = nullptr) : QObject(parent) | |
| { | |
| connect(&_timer, &QTimer::timeout, [this]() | |
| { | |
| if (!_tasks.empty()) | |
| { | |
| std::ranges::pop_heap(_tasks, compareTask); | |
| const auto task = _tasks.back(); | |
| _tasks.pop_back(); | |
| task->run(); | |
| if (task->_is_periodic) | |
| { | |
| _tasks.push_back(task); | |
| std::ranges::push_heap(_tasks, compareTask); | |
| } | |
| update_timer(); | |
| } | |
| }); | |
| } | |
| priorityTasker(const priorityTasker&) = delete; | |
| priorityTasker(priorityTasker&&) = delete; | |
| priorityTasker& operator =(const priorityTasker&) = delete; | |
| priorityTasker& operator = (priorityTasker&&) = delete; | |
| ~priorityTasker() override = default; | |
| static bool compareTask(const std::shared_ptr<baseTask>& task1, const std::shared_ptr<baseTask>& task2) | |
| { | |
| return task1->get_expiration_time() > task2->get_expiration_time(); | |
| } | |
| template<typename... Args> | |
| void add_task(const int timeout, const bool is_periodic, std::function<void(Args...)> callback, Args... args) | |
| { | |
| auto task = std::make_shared<priorityTask<Args...>> | |
| ( | |
| timeout, | |
| is_periodic, | |
| std::move(callback), | |
| std::forward<Args>(args)... | |
| ); | |
| _tasks.push_back(task); | |
| std::ranges::push_heap(_tasks, compareTask); | |
| update_timer(); | |
| } | |
| void add_task(const int timeout, const bool is_periodic, std::function<void()> callback) | |
| { | |
| auto expiration_time = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeout); | |
| const auto task = std::make_shared<priorityTask<>> | |
| ( | |
| timeout, | |
| is_periodic, | |
| std::move(callback) | |
| ); | |
| _tasks.push_back(task); | |
| std::ranges::push_heap(_tasks, compareTask); | |
| update_timer(); | |
| } | |
| private: | |
| void update_timer() | |
| { | |
| _timer.stop(); | |
| if (!_tasks.empty()) | |
| { | |
| const int64_t current_time = QDateTime::currentMSecsSinceEpoch(); | |
| const int64_t next_time = static_cast<int64_t>(_tasks.front()->_expiration_time); | |
| if (const int64_t interval = next_time > current_time ? next_time - current_time : 0; interval > 0) | |
| _timer.start(static_cast<int>(interval)); | |
| } | |
| else | |
| _timer.stop(); | |
| } | |
| }; | |
| } | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment