From b8ddff61b7a61c66599b2a14d7977e0af41fffc1 Mon Sep 17 00:00:00 2001 From: Stefano Petrilli Date: Sun, 29 Sep 2024 23:08:32 +0200 Subject: [PATCH] Makes task_timer generic (#897) * make task_timer tick length configurable --------- Co-authored-by: Gulliver --- include/crow/task_timer.h | 90 ++++++++++++++++++++++++--------------- tests/unittest.cpp | 11 +++-- 2 files changed, 61 insertions(+), 40 deletions(-) diff --git a/include/crow/task_timer.h b/include/crow/task_timer.h index 15b65b639..5d7518ca7 100644 --- a/include/crow/task_timer.h +++ b/include/crow/task_timer.h @@ -29,7 +29,9 @@ namespace crow namespace detail { - /// A class for scheduling functions to be called after a specific amount of ticks. A tick is equal to 1 second. + /// A class for scheduling functions to be called after a specific + /// amount of ticks. Ther tick length can be handed over in constructor, + /// the default tick length is equal to 1 second. class task_timer { public: @@ -39,66 +41,82 @@ namespace crow private: using clock_type = std::chrono::steady_clock; using time_type = clock_type::time_point; - public: - task_timer(asio::io_service& io_service): - io_service_(io_service), timer_(io_service_) + task_timer(asio::io_service& io_service, + const std::chrono::milliseconds tick_length = + std::chrono::seconds(1)) : + io_service_(io_service), timer_(io_service_), + tick_length_ms_(tick_length) { - timer_.expires_after(std::chrono::seconds(1)); + timer_.expires_after(tick_length_ms_); timer_.async_wait( - std::bind(&task_timer::tick_handler, this, std::placeholders::_1)); + std::bind(&task_timer::tick_handler, this, + std::placeholders::_1)); } ~task_timer() { timer_.cancel(); } + /// Cancel the scheduling of the given task + /// + /// \param identifier_type task identifier of the task to cancel. void cancel(identifier_type id) { tasks_.erase(id); - CROW_LOG_DEBUG << "task_timer cancelled: " << this << ' ' << id; + CROW_LOG_DEBUG << "task_timer task cancelled: " << this << ' ' << id; } - /// Schedule the given task to be executed after the default amount of ticks. + /// Schedule the given task to be executed after the default amount + /// of ticks. /// /// \return identifier_type Used to cancel the thread. - /// It is not bound to this task_timer instance and in some cases could lead to - /// undefined behavior if used with other task_timer objects or after the task - /// has been successfully executed. + /// It is not bound to this task_timer instance and in some cases + /// could lead to undefined behavior if used with other task_timer + /// objects or after the task has been successfully executed. identifier_type schedule(const task_type& task) { - tasks_.insert( - {++highest_id_, - {clock_type::now() + std::chrono::seconds(get_default_timeout()), - task}}); - CROW_LOG_DEBUG << "task_timer scheduled: " << this << ' ' << highest_id_; - return highest_id_; + return schedule(task, get_default_timeout()); } /// Schedule the given task to be executed after the given time. /// - /// \param timeout The amount of ticks (seconds) to wait before execution. + /// \param timeout The amount of ticks to wait before execution. /// /// \return identifier_type Used to cancel the thread. - /// It is not bound to this task_timer instance and in some cases could lead to - /// undefined behavior if used with other task_timer objects or after the task - /// has been successfully executed. - identifier_type schedule(const task_type& task, std::uint8_t timeout) + /// It is not bound to this task_timer instance and in some cases + /// could lead to undefined behavior if used with other task_timer + /// objects or after the task has been successfully executed. + identifier_type schedule(const task_type& task, uint8_t timeout) { tasks_.insert({++highest_id_, - {clock_type::now() + std::chrono::seconds(timeout), task}}); - CROW_LOG_DEBUG << "task_timer scheduled: " << this << ' ' << highest_id_; + {clock_type::now() + (timeout * tick_length_ms_), + task}}); + CROW_LOG_DEBUG << "task_timer scheduled: " << this << ' ' << + highest_id_; return highest_id_; } - /// Set the default timeout for this task_timer instance. (Default: 5) + /// Set the default timeout for this task_timer instance. + /// (Default: 5) /// - /// \param timeout The amount of ticks (seconds) to wait before execution. - void set_default_timeout(std::uint8_t timeout) { default_timeout_ = timeout; } + /// \param timeout The amount of ticks to wait before + /// execution. + /// For tick length \see tick_length_ms_ + void set_default_timeout(uint8_t timeout) { + default_timeout_ = timeout; + } /// Get the default timeout. (Default: 5) - std::uint8_t get_default_timeout() const { return default_timeout_; } + uint8_t get_default_timeout() const { + return default_timeout_; + } + + /// returns the length of one tick. + std::chrono::milliseconds get_tick_length() const { + return tick_length_ms_; + } private: void process_tasks() @@ -112,14 +130,16 @@ namespace crow { (task.second.second)(); finished_tasks.push_back(task.first); - CROW_LOG_DEBUG << "task_timer called: " << this << ' ' << task.first; + CROW_LOG_DEBUG << "task_timer called: " << this << + ' ' << task.first; } } for (const auto& task : finished_tasks) tasks_.erase(task); - // If no task is currently scheduled, reset the issued ids back to 0. + // If no task is currently scheduled, reset the issued ids back + // to 0. if (tasks_.empty()) highest_id_ = 0; } @@ -129,20 +149,22 @@ namespace crow process_tasks(); - timer_.expires_after(std::chrono::seconds(1)); + timer_.expires_after(tick_length_ms_); timer_.async_wait( std::bind(&task_timer::tick_handler, this, std::placeholders::_1)); } private: - std::uint8_t default_timeout_{5}; asio::io_service& io_service_; asio::basic_waitable_timer timer_; std::map> tasks_; - // A continuosly increasing number to be issued to threads to identify them. - // If no tasks are scheduled, it will be reset to 0. + // A continuously increasing number to be issued to threads to + // identify them. If no tasks are scheduled, it will be reset to 0. identifier_type highest_id_{0}; + std::chrono::milliseconds tick_length_ms_; + uint8_t default_timeout_{5}; + }; } // namespace detail } // namespace crow diff --git a/tests/unittest.cpp b/tests/unittest.cpp index 901113bd3..6cf9fa832 100644 --- a/tests/unittest.cpp +++ b/tests/unittest.cpp @@ -3908,26 +3908,25 @@ TEST_CASE("task_timer") bool a = false; bool b = false; - crow::detail::task_timer timer(io_service); + crow::detail::task_timer timer(io_service, std::chrono::milliseconds(100)); CHECK(timer.get_default_timeout() == 5); timer.set_default_timeout(7); CHECK(timer.get_default_timeout() == 7); timer.schedule([&a]() { a = true; - }, - 5); + }, 5); timer.schedule([&b]() { b = true; }); - this_thread::sleep_for(chrono::seconds(4)); + this_thread::sleep_for(4 * timer.get_tick_length()); CHECK(a == false); CHECK(b == false); - this_thread::sleep_for(chrono::seconds(2)); + this_thread::sleep_for(2 * timer.get_tick_length()); CHECK(a == true); CHECK(b == false); - this_thread::sleep_for(chrono::seconds(2)); + this_thread::sleep_for(2 * timer.get_tick_length()); CHECK(a == true); CHECK(b == true);