timer_queue.hpp 8.7 KB

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