Crow  1.1
A C++ microframework for the web
task_timer.h
1 #pragma once
2 
3 #ifdef CROW_USE_BOOST
4 #include <boost/asio.hpp>
5 #include <boost/asio/basic_waitable_timer.hpp>
6 #else
7 #ifndef ASIO_STANDALONE
8 #define ASIO_STANDALONE
9 #endif
10 #include <asio.hpp>
11 #include <asio/basic_waitable_timer.hpp>
12 #endif
13 
14 #include <chrono>
15 #include <functional>
16 #include <map>
17 #include <vector>
18 
19 #include "crow/logging.h"
20 
21 namespace crow
22 {
23 #ifdef CROW_USE_BOOST
24  namespace asio = boost::asio;
25  using error_code = boost::system::error_code;
26 #else
27  using error_code = asio::error_code;
28 #endif
29  namespace detail
30  {
31 
32  /// A class for scheduling functions to be called after a specific
33  /// amount of ticks. Ther tick length can be handed over in constructor,
34  /// the default tick length is equal to 1 second.
35  class task_timer
36  {
37  public:
38  using task_type = std::function<void()>;
39  using identifier_type = size_t;
40 
41  private:
42  using clock_type = std::chrono::steady_clock;
43  using time_type = clock_type::time_point;
44  public:
45  task_timer(asio::io_service& io_service,
46  const std::chrono::milliseconds tick_length =
47  std::chrono::seconds(1)) :
48  io_service_(io_service), timer_(io_service_),
49  tick_length_ms_(tick_length)
50  {
51  timer_.expires_after(tick_length_ms_);
52  timer_.async_wait(
53  std::bind(&task_timer::tick_handler, this,
54  std::placeholders::_1));
55  }
56 
57  ~task_timer() { timer_.cancel(); }
58 
59  /// Cancel the scheduling of the given task
60  ///
61  /// \param identifier_type task identifier of the task to cancel.
62  void cancel(identifier_type id)
63  {
64  tasks_.erase(id);
65  CROW_LOG_DEBUG << "task_timer task cancelled: " << this << ' ' << id;
66  }
67 
68  /// Schedule the given task to be executed after the default amount
69  /// of ticks.
70 
71  ///
72  /// \return identifier_type Used to cancel the thread.
73  /// It is not bound to this task_timer instance and in some cases
74  /// could lead to undefined behavior if used with other task_timer
75  /// objects or after the task has been successfully executed.
76  identifier_type schedule(const task_type& task)
77  {
78  return schedule(task, get_default_timeout());
79  }
80 
81  /// Schedule the given task to be executed after the given time.
82 
83  ///
84  /// \param timeout The amount of ticks to wait before execution.
85  ///
86  /// \return identifier_type Used to cancel the thread.
87  /// It is not bound to this task_timer instance and in some cases
88  /// could lead to undefined behavior if used with other task_timer
89  /// objects or after the task has been successfully executed.
90  identifier_type schedule(const task_type& task, uint8_t timeout)
91  {
92  tasks_.insert({++highest_id_,
93  {clock_type::now() + (timeout * tick_length_ms_),
94  task}});
95  CROW_LOG_DEBUG << "task_timer scheduled: " << this << ' ' <<
96  highest_id_;
97  return highest_id_;
98  }
99 
100  /// Set the default timeout for this task_timer instance.
101  /// (Default: 5)
102 
103  ///
104  /// \param timeout The amount of ticks to wait before
105  /// execution.
106  /// For tick length \see tick_length_ms_
107  void set_default_timeout(uint8_t timeout) {
108  default_timeout_ = timeout;
109  }
110 
111  /// Get the default timeout. (Default: 5)
112  uint8_t get_default_timeout() const {
113  return default_timeout_;
114  }
115 
116  /// returns the length of one tick.
117  std::chrono::milliseconds get_tick_length() const {
118  return tick_length_ms_;
119  }
120 
121  private:
122  void process_tasks()
123  {
124  time_type current_time = clock_type::now();
125  std::vector<identifier_type> finished_tasks;
126 
127  for (const auto& task : tasks_)
128  {
129  if (task.second.first < current_time)
130  {
131  (task.second.second)();
132  finished_tasks.push_back(task.first);
133  CROW_LOG_DEBUG << "task_timer called: " << this <<
134  ' ' << task.first;
135  }
136  }
137 
138  for (const auto& task : finished_tasks)
139  tasks_.erase(task);
140 
141  // If no task is currently scheduled, reset the issued ids back
142  // to 0.
143  if (tasks_.empty()) highest_id_ = 0;
144  }
145 
146  void tick_handler(const error_code& ec)
147  {
148  if (ec) return;
149 
150  process_tasks();
151 
152  timer_.expires_after(tick_length_ms_);
153  timer_.async_wait(
154  std::bind(&task_timer::tick_handler, this, std::placeholders::_1));
155  }
156 
157  private:
158  asio::io_service& io_service_;
159  asio::basic_waitable_timer<clock_type> timer_;
160  std::map<identifier_type, std::pair<time_type, task_type>> tasks_;
161 
162  // A continuously increasing number to be issued to threads to
163  // identify them. If no tasks are scheduled, it will be reset to 0.
164  identifier_type highest_id_{0};
165  std::chrono::milliseconds tick_length_ms_;
166  uint8_t default_timeout_{5};
167 
168  };
169  } // namespace detail
170 } // namespace crow
Definition: task_timer.h:36
void cancel(identifier_type id)
Definition: task_timer.h:62
uint8_t get_default_timeout() const
Get the default timeout. (Default: 5)
Definition: task_timer.h:112
identifier_type schedule(const task_type &task, uint8_t timeout)
Schedule the given task to be executed after the given time.
Definition: task_timer.h:90
std::chrono::milliseconds get_tick_length() const
returns the length of one tick.
Definition: task_timer.h:117
void set_default_timeout(uint8_t timeout)
Definition: task_timer.h:107
identifier_type schedule(const task_type &task)
Definition: task_timer.h:76
The main namespace of the library. In this namespace is defined the most important classes and functi...