Clementine
timer_queue.hpp
1 //
2 // detail/timer_queue.hpp
3 // ~~~~~~~~~~~~~~~~~~~~~~
4 //
5 // Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff dot com)
6 //
7 // Distributed under the Boost Software License, Version 1.0. (See accompanying
8 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
9 //
10 
11 #ifndef ASIO_DETAIL_TIMER_QUEUE_HPP
12 #define ASIO_DETAIL_TIMER_QUEUE_HPP
13 
14 #if defined(_MSC_VER) && (_MSC_VER >= 1200)
15 # pragma once
16 #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
17 
18 #include "asio/detail/config.hpp"
19 #include <cstddef>
20 #include <vector>
21 #include "asio/detail/cstdint.hpp"
22 #include "asio/detail/date_time_fwd.hpp"
23 #include "asio/detail/limits.hpp"
24 #include "asio/detail/op_queue.hpp"
25 #include "asio/detail/timer_queue_base.hpp"
26 #include "asio/detail/wait_op.hpp"
27 #include "asio/error.hpp"
28 
29 #include "asio/detail/push_options.hpp"
30 
31 namespace asio {
32 namespace detail {
33 
34 template <typename Time_Traits>
36  : public timer_queue_base
37 {
38 public:
39  // The time type.
40  typedef typename Time_Traits::time_type time_type;
41 
42  // The duration type.
43  typedef typename Time_Traits::duration_type duration_type;
44 
45  // Per-timer data.
47  {
48  public:
49  per_timer_data() :
50  heap_index_((std::numeric_limits<std::size_t>::max)()),
51  next_(0), prev_(0)
52  {
53  }
54 
55  private:
56  friend class timer_queue;
57 
58  // The operations waiting on the timer.
59  op_queue<wait_op> op_queue_;
60 
61  // The index of the timer in the heap.
62  std::size_t heap_index_;
63 
64  // Pointers to adjacent timers in a linked list.
65  per_timer_data* next_;
66  per_timer_data* prev_;
67  };
68 
69  // Constructor.
70  timer_queue()
71  : timers_(),
72  heap_()
73  {
74  }
75 
76  // Add a new timer to the queue. Returns true if this is the timer that is
77  // earliest in the queue, in which case the reactor's event demultiplexing
78  // function call may need to be interrupted and restarted.
79  bool enqueue_timer(const time_type& time, per_timer_data& timer, wait_op* op)
80  {
81  // Enqueue the timer object.
82  if (timer.prev_ == 0 && &timer != timers_)
83  {
84  if (this->is_positive_infinity(time))
85  {
86  // No heap entry is required for timers that never expire.
87  timer.heap_index_ = (std::numeric_limits<std::size_t>::max)();
88  }
89  else
90  {
91  // Put the new timer at the correct position in the heap. This is done
92  // first since push_back() can throw due to allocation failure.
93  timer.heap_index_ = heap_.size();
94  heap_entry entry = { time, &timer };
95  heap_.push_back(entry);
96  up_heap(heap_.size() - 1);
97  }
98 
99  // Insert the new timer into the linked list of active timers.
100  timer.next_ = timers_;
101  timer.prev_ = 0;
102  if (timers_)
103  timers_->prev_ = &timer;
104  timers_ = &timer;
105  }
106 
107  // Enqueue the individual timer operation.
108  timer.op_queue_.push(op);
109 
110  // Interrupt reactor only if newly added timer is first to expire.
111  return timer.heap_index_ == 0 && timer.op_queue_.front() == op;
112  }
113 
114  // Whether there are no timers in the queue.
115  virtual bool empty() const
116  {
117  return timers_ == 0;
118  }
119 
120  // Get the time for the timer that is earliest in the queue.
121  virtual long wait_duration_msec(long max_duration) const
122  {
123  if (heap_.empty())
124  return max_duration;
125 
126  return this->to_msec(
127  Time_Traits::to_posix_duration(
128  Time_Traits::subtract(heap_[0].time_, Time_Traits::now())),
129  max_duration);
130  }
131 
132  // Get the time for the timer that is earliest in the queue.
133  virtual long wait_duration_usec(long max_duration) const
134  {
135  if (heap_.empty())
136  return max_duration;
137 
138  return this->to_usec(
139  Time_Traits::to_posix_duration(
140  Time_Traits::subtract(heap_[0].time_, Time_Traits::now())),
141  max_duration);
142  }
143 
144  // Dequeue all timers not later than the current time.
145  virtual void get_ready_timers(op_queue<operation>& ops)
146  {
147  if (!heap_.empty())
148  {
149  const time_type now = Time_Traits::now();
150  while (!heap_.empty() && !Time_Traits::less_than(now, heap_[0].time_))
151  {
152  per_timer_data* timer = heap_[0].timer_;
153  ops.push(timer->op_queue_);
154  remove_timer(*timer);
155  }
156  }
157  }
158 
159  // Dequeue all timers.
160  virtual void get_all_timers(op_queue<operation>& ops)
161  {
162  while (timers_)
163  {
164  per_timer_data* timer = timers_;
165  timers_ = timers_->next_;
166  ops.push(timer->op_queue_);
167  timer->next_ = 0;
168  timer->prev_ = 0;
169  }
170 
171  heap_.clear();
172  }
173 
174  // Cancel and dequeue operations for the given timer.
175  std::size_t cancel_timer(per_timer_data& timer, op_queue<operation>& ops,
176  std::size_t max_cancelled = (std::numeric_limits<std::size_t>::max)())
177  {
178  std::size_t num_cancelled = 0;
179  if (timer.prev_ != 0 || &timer == timers_)
180  {
181  while (wait_op* op = (num_cancelled != max_cancelled)
182  ? timer.op_queue_.front() : 0)
183  {
184  op->ec_ = asio::error::operation_aborted;
185  timer.op_queue_.pop();
186  ops.push(op);
187  ++num_cancelled;
188  }
189  if (timer.op_queue_.empty())
190  remove_timer(timer);
191  }
192  return num_cancelled;
193  }
194 
195  // Move operations from one timer to another, empty timer.
196  void move_timer(per_timer_data& target, per_timer_data& source)
197  {
198  target.op_queue_.push(source.op_queue_);
199 
200  target.heap_index_ = source.heap_index_;
201  source.heap_index_ = (std::numeric_limits<std::size_t>::max)();
202 
203  if (target.heap_index_ < heap_.size())
204  heap_[target.heap_index_].timer_ = &target;
205 
206  if (timers_ == &source)
207  timers_ = &target;
208  if (source.prev_)
209  source.prev_->next_ = &target;
210  if (source.next_)
211  source.next_->prev_= &target;
212  target.next_ = source.next_;
213  target.prev_ = source.prev_;
214  source.next_ = 0;
215  source.prev_ = 0;
216  }
217 
218 private:
219  // Move the item at the given index up the heap to its correct position.
220  void up_heap(std::size_t index)
221  {
222  while (index > 0)
223  {
224  std::size_t parent = (index - 1) / 2;
225  if (!Time_Traits::less_than(heap_[index].time_, heap_[parent].time_))
226  break;
227  swap_heap(index, parent);
228  index = parent;
229  }
230  }
231 
232  // Move the item at the given index down the heap to its correct position.
233  void down_heap(std::size_t index)
234  {
235  std::size_t child = index * 2 + 1;
236  while (child < heap_.size())
237  {
238  std::size_t min_child = (child + 1 == heap_.size()
239  || Time_Traits::less_than(
240  heap_[child].time_, heap_[child + 1].time_))
241  ? child : child + 1;
242  if (Time_Traits::less_than(heap_[index].time_, heap_[min_child].time_))
243  break;
244  swap_heap(index, min_child);
245  index = min_child;
246  child = index * 2 + 1;
247  }
248  }
249 
250  // Swap two entries in the heap.
251  void swap_heap(std::size_t index1, std::size_t index2)
252  {
253  heap_entry tmp = heap_[index1];
254  heap_[index1] = heap_[index2];
255  heap_[index2] = tmp;
256  heap_[index1].timer_->heap_index_ = index1;
257  heap_[index2].timer_->heap_index_ = index2;
258  }
259 
260  // Remove a timer from the heap and list of timers.
261  void remove_timer(per_timer_data& timer)
262  {
263  // Remove the timer from the heap.
264  std::size_t index = timer.heap_index_;
265  if (!heap_.empty() && index < heap_.size())
266  {
267  if (index == heap_.size() - 1)
268  {
269  timer.heap_index_ = (std::numeric_limits<std::size_t>::max)();
270  heap_.pop_back();
271  }
272  else
273  {
274  swap_heap(index, heap_.size() - 1);
275  timer.heap_index_ = (std::numeric_limits<std::size_t>::max)();
276  heap_.pop_back();
277  if (index > 0 && Time_Traits::less_than(
278  heap_[index].time_, heap_[(index - 1) / 2].time_))
279  up_heap(index);
280  else
281  down_heap(index);
282  }
283  }
284 
285  // Remove the timer from the linked list of active timers.
286  if (timers_ == &timer)
287  timers_ = timer.next_;
288  if (timer.prev_)
289  timer.prev_->next_ = timer.next_;
290  if (timer.next_)
291  timer.next_->prev_= timer.prev_;
292  timer.next_ = 0;
293  timer.prev_ = 0;
294  }
295 
296  // Determine if the specified absolute time is positive infinity.
297  template <typename Time_Type>
298  static bool is_positive_infinity(const Time_Type&)
299  {
300  return false;
301  }
302 
303  // Determine if the specified absolute time is positive infinity.
304  template <typename T, typename TimeSystem>
305  static bool is_positive_infinity(
307  {
308  return time.is_pos_infinity();
309  }
310 
311  // Helper function to convert a duration into milliseconds.
312  template <typename Duration>
313  long to_msec(const Duration& d, long max_duration) const
314  {
315  if (d.ticks() <= 0)
316  return 0;
317  int64_t msec = d.total_milliseconds();
318  if (msec == 0)
319  return 1;
320  if (msec > max_duration)
321  return max_duration;
322  return static_cast<long>(msec);
323  }
324 
325  // Helper function to convert a duration into microseconds.
326  template <typename Duration>
327  long to_usec(const Duration& d, long max_duration) const
328  {
329  if (d.ticks() <= 0)
330  return 0;
331  int64_t usec = d.total_microseconds();
332  if (usec == 0)
333  return 1;
334  if (usec > max_duration)
335  return max_duration;
336  return static_cast<long>(usec);
337  }
338 
339  // The head of a linked list of all active timers.
340  per_timer_data* timers_;
341 
342  struct heap_entry
343  {
344  // The time when the timer should fire.
345  time_type time_;
346 
347  // The associated timer with enqueued operations.
348  per_timer_data* timer_;
349  };
350 
351  // The heap of timers, with the earliest timer at the front.
352  std::vector<heap_entry> heap_;
353 };
354 
355 } // namespace detail
356 } // namespace asio
357 
358 #include "asio/detail/pop_options.hpp"
359 
360 #endif // ASIO_DETAIL_TIMER_QUEUE_HPP
Definition: date_time_fwd.hpp:24
Definition: chrono.h:284
Definition: op_queue.hpp:26
Definition: timer_queue.hpp:35
Definition: timer_queue_base.hpp:28
Definition: wait_op.hpp:26
Definition: timer_queue.hpp:46
Definition: any_io_executor.hpp:28