#pragma once #include #include #include #include #include #include "crow/logging.h" namespace crow { namespace detail { /** * @brief A class for scheduling functions to be called after a specific amount * of ticks. A tick is equal to 1 second. * */ class task_timer { public: using task_type = std::function; using identifier_type = size_t; private: using clock_type = std::chrono::steady_clock; using time_type = clock_type::time_point; public: task_timer(boost::asio::io_service& io_service) : io_service_(io_service), deadline_timer_(io_service_) { deadline_timer_.expires_from_now(boost::posix_time::seconds(1)); deadline_timer_.async_wait( std::bind(&task_timer::tick_handler, this, std::placeholders::_1)); } ~task_timer() { deadline_timer_.cancel(); } void cancel(identifier_type id) { tasks_.erase(id); CROW_LOG_DEBUG << "timer cancelled: " << this << ' ' << id; } /** * @brief 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. */ identifier_type set_timeout(const task_type& task) { tasks_.insert( {++highest_id_, {clock_type::now() + std::chrono::seconds(get_default_timeout()), task}}); CROW_LOG_DEBUG << "timer add inside: " << this << ' ' << highest_id_; return highest_id_; } /** * @brief Schedule the given task to be executed after the given time. * * @param timeout The amount of ticks (seconds) 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 set_timeout(const task_type& task, std::uint8_t timeout) { tasks_.insert({++highest_id_, {clock_type::now() + std::chrono::seconds(timeout), task}}); CROW_LOG_DEBUG << "timer add inside: " << this << ' ' << highest_id_; return highest_id_; } /** * @brief 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; } /** * @brief Get the default timeout. (Default: 5) * * @return std::uint8_t */ std::uint8_t get_default_timeout() const { return default_timeout_; } private: void process_tasks() { time_type current_time = clock_type::now(); std::vector finished_tasks; for (const auto& task : tasks_) { if (task.second.first < current_time) { (task.second.second)(); finished_tasks.push_back(task.first); CROW_LOG_DEBUG << "timer call: " << 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 (tasks_.empty()) highest_id_ = 0; } void tick_handler(const boost::system::error_code& ec) { if (ec) return; process_tasks(); deadline_timer_.expires_from_now(boost::posix_time::seconds(1)); deadline_timer_.async_wait( std::bind(&task_timer::tick_handler, this, std::placeholders::_1)); } private: std::uint8_t default_timeout_{5}; boost::asio::io_service& io_service_; boost::asio::deadline_timer deadline_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. identifier_type highest_id_{0}; }; } // namespace detail } // namespace crow