timer_queue.hpp 8.8 KB

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