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