1 | // |
2 | // deadline_timer.cpp |
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 | |
11 | // Disable autolinking for unit tests. |
12 | #if !defined(BOOST_ALL_NO_LIB) |
13 | #define BOOST_ALL_NO_LIB 1 |
14 | #endif // !defined(BOOST_ALL_NO_LIB) |
15 | |
16 | // Test that header file is self-contained. |
17 | #include <boost/asio/deadline_timer.hpp> |
18 | |
19 | #include "unit_test.hpp" |
20 | |
21 | #if defined(BOOST_ASIO_HAS_BOOST_DATE_TIME) |
22 | |
23 | #include <boost/bind.hpp> |
24 | #include "archetypes/async_result.hpp" |
25 | #include <boost/asio/io_service.hpp> |
26 | #include <boost/asio/placeholders.hpp> |
27 | #include <boost/asio/detail/thread.hpp> |
28 | |
29 | using namespace boost::posix_time; |
30 | |
31 | void increment(int* count) |
32 | { |
33 | ++(*count); |
34 | } |
35 | |
36 | void decrement_to_zero(boost::asio::deadline_timer* t, int* count) |
37 | { |
38 | if (*count > 0) |
39 | { |
40 | --(*count); |
41 | |
42 | int before_value = *count; |
43 | |
44 | t->expires_at(expiry_time: t->expires_at() + seconds(1)); |
45 | t->async_wait(handler: boost::bind(f: decrement_to_zero, a1: t, a2: count)); |
46 | |
47 | // Completion cannot nest, so count value should remain unchanged. |
48 | BOOST_ASIO_CHECK(*count == before_value); |
49 | } |
50 | } |
51 | |
52 | void increment_if_not_cancelled(int* count, |
53 | const boost::system::error_code& ec) |
54 | { |
55 | if (!ec) |
56 | ++(*count); |
57 | } |
58 | |
59 | void cancel_timer(boost::asio::deadline_timer* t) |
60 | { |
61 | std::size_t num_cancelled = t->cancel(); |
62 | BOOST_ASIO_CHECK(num_cancelled == 1); |
63 | } |
64 | |
65 | void cancel_one_timer(boost::asio::deadline_timer* t) |
66 | { |
67 | std::size_t num_cancelled = t->cancel_one(); |
68 | BOOST_ASIO_CHECK(num_cancelled == 1); |
69 | } |
70 | |
71 | ptime now() |
72 | { |
73 | #if defined(BOOST_DATE_TIME_HAS_HIGH_PRECISION_CLOCK) |
74 | return microsec_clock::universal_time(); |
75 | #else // defined(BOOST_DATE_TIME_HAS_HIGH_PRECISION_CLOCK) |
76 | return second_clock::universal_time(); |
77 | #endif // defined(BOOST_DATE_TIME_HAS_HIGH_PRECISION_CLOCK) |
78 | } |
79 | |
80 | void deadline_timer_test() |
81 | { |
82 | boost::asio::io_service ios; |
83 | int count = 0; |
84 | |
85 | ptime start = now(); |
86 | |
87 | boost::asio::deadline_timer t1(ios, seconds(1)); |
88 | t1.wait(); |
89 | |
90 | // The timer must block until after its expiry time. |
91 | ptime end = now(); |
92 | ptime expected_end = start + seconds(1); |
93 | BOOST_ASIO_CHECK(expected_end < end || expected_end == end); |
94 | |
95 | start = now(); |
96 | |
97 | boost::asio::deadline_timer t2(ios, seconds(1) + microseconds(500000)); |
98 | t2.wait(); |
99 | |
100 | // The timer must block until after its expiry time. |
101 | end = now(); |
102 | expected_end = start + seconds(1) + microseconds(500000); |
103 | BOOST_ASIO_CHECK(expected_end < end || expected_end == end); |
104 | |
105 | t2.expires_at(expiry_time: t2.expires_at() + seconds(1)); |
106 | t2.wait(); |
107 | |
108 | // The timer must block until after its expiry time. |
109 | end = now(); |
110 | expected_end += seconds(1); |
111 | BOOST_ASIO_CHECK(expected_end < end || expected_end == end); |
112 | |
113 | start = now(); |
114 | |
115 | t2.expires_from_now(expiry_time: seconds(1) + microseconds(200000)); |
116 | t2.wait(); |
117 | |
118 | // The timer must block until after its expiry time. |
119 | end = now(); |
120 | expected_end = start + seconds(1) + microseconds(200000); |
121 | BOOST_ASIO_CHECK(expected_end < end || expected_end == end); |
122 | |
123 | start = now(); |
124 | |
125 | boost::asio::deadline_timer t3(ios, seconds(5)); |
126 | t3.async_wait(handler: boost::bind(f: increment, a1: &count)); |
127 | |
128 | // No completions can be delivered until run() is called. |
129 | BOOST_ASIO_CHECK(count == 0); |
130 | |
131 | ios.run(); |
132 | |
133 | // The run() call will not return until all operations have finished, and |
134 | // this should not be until after the timer's expiry time. |
135 | BOOST_ASIO_CHECK(count == 1); |
136 | end = now(); |
137 | expected_end = start + seconds(1); |
138 | BOOST_ASIO_CHECK(expected_end < end || expected_end == end); |
139 | |
140 | count = 3; |
141 | start = now(); |
142 | |
143 | boost::asio::deadline_timer t4(ios, seconds(1)); |
144 | t4.async_wait(handler: boost::bind(f: decrement_to_zero, a1: &t4, a2: &count)); |
145 | |
146 | // No completions can be delivered until run() is called. |
147 | BOOST_ASIO_CHECK(count == 3); |
148 | |
149 | ios.reset(); |
150 | ios.run(); |
151 | |
152 | // The run() call will not return until all operations have finished, and |
153 | // this should not be until after the timer's final expiry time. |
154 | BOOST_ASIO_CHECK(count == 0); |
155 | end = now(); |
156 | expected_end = start + seconds(3); |
157 | BOOST_ASIO_CHECK(expected_end < end || expected_end == end); |
158 | |
159 | count = 0; |
160 | start = now(); |
161 | |
162 | boost::asio::deadline_timer t5(ios, seconds(10)); |
163 | t5.async_wait(handler: boost::bind(f: increment_if_not_cancelled, a1: &count, |
164 | a2: boost::asio::placeholders::error)); |
165 | boost::asio::deadline_timer t6(ios, seconds(1)); |
166 | t6.async_wait(handler: boost::bind(f: cancel_timer, a1: &t5)); |
167 | |
168 | // No completions can be delivered until run() is called. |
169 | BOOST_ASIO_CHECK(count == 0); |
170 | |
171 | ios.reset(); |
172 | ios.run(); |
173 | |
174 | // The timer should have been cancelled, so count should not have changed. |
175 | // The total run time should not have been much more than 1 second (and |
176 | // certainly far less than 10 seconds). |
177 | BOOST_ASIO_CHECK(count == 0); |
178 | end = now(); |
179 | expected_end = start + seconds(2); |
180 | BOOST_ASIO_CHECK(end < expected_end); |
181 | |
182 | // Wait on the timer again without cancelling it. This time the asynchronous |
183 | // wait should run to completion and increment the counter. |
184 | t5.async_wait(handler: boost::bind(f: increment_if_not_cancelled, a1: &count, |
185 | a2: boost::asio::placeholders::error)); |
186 | |
187 | ios.reset(); |
188 | ios.run(); |
189 | |
190 | // The timer should not have been cancelled, so count should have changed. |
191 | // The total time since the timer was created should be more than 10 seconds. |
192 | BOOST_ASIO_CHECK(count == 1); |
193 | end = now(); |
194 | expected_end = start + seconds(10); |
195 | BOOST_ASIO_CHECK(expected_end < end || expected_end == end); |
196 | |
197 | count = 0; |
198 | start = now(); |
199 | |
200 | // Start two waits on a timer, one of which will be cancelled. The one |
201 | // which is not cancelled should still run to completion and increment the |
202 | // counter. |
203 | boost::asio::deadline_timer t7(ios, seconds(3)); |
204 | t7.async_wait(handler: boost::bind(f: increment_if_not_cancelled, a1: &count, |
205 | a2: boost::asio::placeholders::error)); |
206 | t7.async_wait(handler: boost::bind(f: increment_if_not_cancelled, a1: &count, |
207 | a2: boost::asio::placeholders::error)); |
208 | boost::asio::deadline_timer t8(ios, seconds(1)); |
209 | t8.async_wait(handler: boost::bind(f: cancel_one_timer, a1: &t7)); |
210 | |
211 | ios.reset(); |
212 | ios.run(); |
213 | |
214 | // One of the waits should not have been cancelled, so count should have |
215 | // changed. The total time since the timer was created should be more than 3 |
216 | // seconds. |
217 | BOOST_ASIO_CHECK(count == 1); |
218 | end = now(); |
219 | expected_end = start + seconds(3); |
220 | BOOST_ASIO_CHECK(expected_end < end || expected_end == end); |
221 | } |
222 | |
223 | void timer_handler(const boost::system::error_code&) |
224 | { |
225 | } |
226 | |
227 | void deadline_timer_cancel_test() |
228 | { |
229 | static boost::asio::io_service io_service; |
230 | struct timer |
231 | { |
232 | boost::asio::deadline_timer t; |
233 | timer() : t(io_service) { t.expires_at(expiry_time: boost::posix_time::pos_infin); } |
234 | } timers[50]; |
235 | |
236 | timers[2].t.async_wait(handler: &timer_handler); |
237 | timers[41].t.async_wait(handler: &timer_handler); |
238 | for (int i = 10; i < 20; ++i) |
239 | timers[i].t.async_wait(handler: &timer_handler); |
240 | |
241 | BOOST_ASIO_CHECK(timers[2].t.cancel() == 1); |
242 | BOOST_ASIO_CHECK(timers[41].t.cancel() == 1); |
243 | for (int i = 10; i < 20; ++i) |
244 | BOOST_ASIO_CHECK(timers[i].t.cancel() == 1); |
245 | } |
246 | |
247 | struct custom_allocation_timer_handler |
248 | { |
249 | custom_allocation_timer_handler(int* count) : count_(count) {} |
250 | void operator()(const boost::system::error_code&) {} |
251 | int* count_; |
252 | }; |
253 | |
254 | void* asio_handler_allocate(std::size_t size, |
255 | custom_allocation_timer_handler* handler) |
256 | { |
257 | ++(*handler->count_); |
258 | return ::operator new(size); |
259 | } |
260 | |
261 | void asio_handler_deallocate(void* pointer, std::size_t, |
262 | custom_allocation_timer_handler* handler) |
263 | { |
264 | --(*handler->count_); |
265 | ::operator delete(pointer); |
266 | } |
267 | |
268 | void deadline_timer_custom_allocation_test() |
269 | { |
270 | static boost::asio::io_service io_service; |
271 | struct timer |
272 | { |
273 | boost::asio::deadline_timer t; |
274 | timer() : t(io_service) {} |
275 | } timers[100]; |
276 | |
277 | int allocation_count = 0; |
278 | |
279 | for (int i = 0; i < 50; ++i) |
280 | { |
281 | timers[i].t.expires_at(expiry_time: boost::posix_time::pos_infin); |
282 | timers[i].t.async_wait(handler: custom_allocation_timer_handler(&allocation_count)); |
283 | } |
284 | |
285 | for (int i = 50; i < 100; ++i) |
286 | { |
287 | timers[i].t.expires_at(expiry_time: boost::posix_time::neg_infin); |
288 | timers[i].t.async_wait(handler: custom_allocation_timer_handler(&allocation_count)); |
289 | } |
290 | |
291 | for (int i = 0; i < 50; ++i) |
292 | timers[i].t.cancel(); |
293 | |
294 | io_service.run(); |
295 | |
296 | BOOST_ASIO_CHECK(allocation_count == 0); |
297 | } |
298 | |
299 | void io_service_run(boost::asio::io_service* ios) |
300 | { |
301 | ios->run(); |
302 | } |
303 | |
304 | void deadline_timer_thread_test() |
305 | { |
306 | boost::asio::io_service ios; |
307 | boost::asio::io_service::work w(ios); |
308 | boost::asio::deadline_timer t1(ios); |
309 | boost::asio::deadline_timer t2(ios); |
310 | int count = 0; |
311 | |
312 | boost::asio::detail::thread th(boost::bind(f: io_service_run, a1: &ios)); |
313 | |
314 | t2.expires_from_now(expiry_time: boost::posix_time::seconds(2)); |
315 | t2.wait(); |
316 | |
317 | t1.expires_from_now(expiry_time: boost::posix_time::seconds(2)); |
318 | t1.async_wait(handler: boost::bind(f: increment, a1: &count)); |
319 | |
320 | t2.expires_from_now(expiry_time: boost::posix_time::seconds(4)); |
321 | t2.wait(); |
322 | |
323 | ios.stop(); |
324 | th.join(); |
325 | |
326 | BOOST_ASIO_CHECK(count == 1); |
327 | } |
328 | |
329 | void deadline_timer_async_result_test() |
330 | { |
331 | boost::asio::io_service ios; |
332 | boost::asio::deadline_timer t1(ios); |
333 | |
334 | t1.expires_from_now(expiry_time: boost::posix_time::seconds(1)); |
335 | int i = t1.async_wait(handler: archetypes::lazy_handler()); |
336 | BOOST_ASIO_CHECK(i == 42); |
337 | |
338 | ios.run(); |
339 | } |
340 | |
341 | BOOST_ASIO_TEST_SUITE |
342 | ( |
343 | "deadline_timer" , |
344 | BOOST_ASIO_TEST_CASE(deadline_timer_test) |
345 | BOOST_ASIO_TEST_CASE(deadline_timer_cancel_test) |
346 | BOOST_ASIO_TEST_CASE(deadline_timer_custom_allocation_test) |
347 | BOOST_ASIO_TEST_CASE(deadline_timer_thread_test) |
348 | BOOST_ASIO_TEST_CASE(deadline_timer_async_result_test) |
349 | ) |
350 | #else // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME) |
351 | BOOST_ASIO_TEST_SUITE |
352 | ( |
353 | "deadline_timer" , |
354 | BOOST_ASIO_TEST_CASE(null_test) |
355 | ) |
356 | #endif // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME) |
357 | |