]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // |
2 | // system_timer.cpp | |
3 | // ~~~~~~~~~~~~~~~~ | |
4 | // | |
11fdf7f2 | 5 | // Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) |
7c673cae FG |
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 | // Prevent link dependency on the Boost.System library. | |
17 | #if !defined(BOOST_SYSTEM_NO_DEPRECATED) | |
18 | #define BOOST_SYSTEM_NO_DEPRECATED | |
19 | #endif // !defined(BOOST_SYSTEM_NO_DEPRECATED) | |
20 | ||
21 | // Test that header file is self-contained. | |
22 | #include <boost/asio/system_timer.hpp> | |
23 | ||
24 | #include "unit_test.hpp" | |
25 | ||
26 | #if defined(BOOST_ASIO_HAS_STD_CHRONO) | |
27 | ||
b32b8144 FG |
28 | #include <boost/asio/executor_work_guard.hpp> |
29 | #include <boost/asio/io_context.hpp> | |
7c673cae FG |
30 | #include <boost/asio/detail/thread.hpp> |
31 | ||
32 | #if defined(BOOST_ASIO_HAS_BOOST_BIND) | |
33 | # include <boost/bind.hpp> | |
34 | #else // defined(BOOST_ASIO_HAS_BOOST_BIND) | |
35 | # include <functional> | |
36 | #endif // defined(BOOST_ASIO_HAS_BOOST_BIND) | |
37 | ||
38 | #if defined(BOOST_ASIO_HAS_BOOST_BIND) | |
39 | namespace bindns = boost; | |
40 | #else // defined(BOOST_ASIO_HAS_BOOST_BIND) | |
41 | namespace bindns = std; | |
42 | #endif // defined(BOOST_ASIO_HAS_BOOST_BIND) | |
43 | ||
7c673cae FG |
44 | void increment(int* count) |
45 | { | |
46 | ++(*count); | |
47 | } | |
48 | ||
49 | void decrement_to_zero(boost::asio::system_timer* t, int* count) | |
50 | { | |
51 | if (*count > 0) | |
52 | { | |
53 | --(*count); | |
54 | ||
55 | int before_value = *count; | |
56 | ||
b32b8144 | 57 | t->expires_at(t->expiry() + boost::asio::chrono::seconds(1)); |
7c673cae FG |
58 | t->async_wait(bindns::bind(decrement_to_zero, t, count)); |
59 | ||
60 | // Completion cannot nest, so count value should remain unchanged. | |
61 | BOOST_ASIO_CHECK(*count == before_value); | |
62 | } | |
63 | } | |
64 | ||
65 | void increment_if_not_cancelled(int* count, | |
66 | const boost::system::error_code& ec) | |
67 | { | |
68 | if (!ec) | |
69 | ++(*count); | |
70 | } | |
71 | ||
72 | void cancel_timer(boost::asio::system_timer* t) | |
73 | { | |
74 | std::size_t num_cancelled = t->cancel(); | |
75 | BOOST_ASIO_CHECK(num_cancelled == 1); | |
76 | } | |
77 | ||
78 | void cancel_one_timer(boost::asio::system_timer* t) | |
79 | { | |
80 | std::size_t num_cancelled = t->cancel_one(); | |
81 | BOOST_ASIO_CHECK(num_cancelled == 1); | |
82 | } | |
83 | ||
84 | boost::asio::system_timer::time_point now() | |
85 | { | |
86 | return boost::asio::system_timer::clock_type::now(); | |
87 | } | |
88 | ||
89 | void system_timer_test() | |
90 | { | |
b32b8144 FG |
91 | using boost::asio::chrono::seconds; |
92 | using boost::asio::chrono::microseconds; | |
7c673cae FG |
93 | #if !defined(BOOST_ASIO_HAS_BOOST_BIND) |
94 | using std::placeholders::_1; | |
95 | using std::placeholders::_2; | |
96 | #endif // !defined(BOOST_ASIO_HAS_BOOST_BIND) | |
97 | ||
b32b8144 | 98 | boost::asio::io_context ioc; |
7c673cae FG |
99 | int count = 0; |
100 | ||
101 | boost::asio::system_timer::time_point start = now(); | |
102 | ||
b32b8144 | 103 | boost::asio::system_timer t1(ioc, seconds(1)); |
7c673cae FG |
104 | t1.wait(); |
105 | ||
106 | // The timer must block until after its expiry time. | |
107 | boost::asio::system_timer::time_point end = now(); | |
108 | boost::asio::system_timer::time_point expected_end = start + seconds(1); | |
109 | BOOST_ASIO_CHECK(expected_end < end || expected_end == end); | |
110 | ||
111 | start = now(); | |
112 | ||
b32b8144 | 113 | boost::asio::system_timer t2(ioc, seconds(1) + microseconds(500000)); |
7c673cae FG |
114 | t2.wait(); |
115 | ||
116 | // The timer must block until after its expiry time. | |
117 | end = now(); | |
118 | expected_end = start + seconds(1) + microseconds(500000); | |
119 | BOOST_ASIO_CHECK(expected_end < end || expected_end == end); | |
120 | ||
b32b8144 | 121 | t2.expires_at(t2.expiry() + seconds(1)); |
7c673cae FG |
122 | t2.wait(); |
123 | ||
124 | // The timer must block until after its expiry time. | |
125 | end = now(); | |
126 | expected_end += seconds(1); | |
127 | BOOST_ASIO_CHECK(expected_end < end || expected_end == end); | |
128 | ||
129 | start = now(); | |
130 | ||
b32b8144 | 131 | t2.expires_after(seconds(1) + microseconds(200000)); |
7c673cae FG |
132 | t2.wait(); |
133 | ||
134 | // The timer must block until after its expiry time. | |
135 | end = now(); | |
136 | expected_end = start + seconds(1) + microseconds(200000); | |
137 | BOOST_ASIO_CHECK(expected_end < end || expected_end == end); | |
138 | ||
139 | start = now(); | |
140 | ||
b32b8144 | 141 | boost::asio::system_timer t3(ioc, seconds(5)); |
7c673cae FG |
142 | t3.async_wait(bindns::bind(increment, &count)); |
143 | ||
144 | // No completions can be delivered until run() is called. | |
145 | BOOST_ASIO_CHECK(count == 0); | |
146 | ||
b32b8144 | 147 | ioc.run(); |
7c673cae FG |
148 | |
149 | // The run() call will not return until all operations have finished, and | |
150 | // this should not be until after the timer's expiry time. | |
151 | BOOST_ASIO_CHECK(count == 1); | |
152 | end = now(); | |
153 | expected_end = start + seconds(1); | |
154 | BOOST_ASIO_CHECK(expected_end < end || expected_end == end); | |
155 | ||
156 | count = 3; | |
157 | start = now(); | |
158 | ||
b32b8144 | 159 | boost::asio::system_timer t4(ioc, seconds(1)); |
7c673cae FG |
160 | t4.async_wait(bindns::bind(decrement_to_zero, &t4, &count)); |
161 | ||
162 | // No completions can be delivered until run() is called. | |
163 | BOOST_ASIO_CHECK(count == 3); | |
164 | ||
b32b8144 FG |
165 | ioc.restart(); |
166 | ioc.run(); | |
7c673cae FG |
167 | |
168 | // The run() call will not return until all operations have finished, and | |
169 | // this should not be until after the timer's final expiry time. | |
170 | BOOST_ASIO_CHECK(count == 0); | |
171 | end = now(); | |
172 | expected_end = start + seconds(3); | |
173 | BOOST_ASIO_CHECK(expected_end < end || expected_end == end); | |
174 | ||
175 | count = 0; | |
176 | start = now(); | |
177 | ||
b32b8144 | 178 | boost::asio::system_timer t5(ioc, seconds(10)); |
7c673cae | 179 | t5.async_wait(bindns::bind(increment_if_not_cancelled, &count, _1)); |
b32b8144 | 180 | boost::asio::system_timer t6(ioc, seconds(1)); |
7c673cae FG |
181 | t6.async_wait(bindns::bind(cancel_timer, &t5)); |
182 | ||
183 | // No completions can be delivered until run() is called. | |
184 | BOOST_ASIO_CHECK(count == 0); | |
185 | ||
b32b8144 FG |
186 | ioc.restart(); |
187 | ioc.run(); | |
7c673cae FG |
188 | |
189 | // The timer should have been cancelled, so count should not have changed. | |
190 | // The total run time should not have been much more than 1 second (and | |
191 | // certainly far less than 10 seconds). | |
192 | BOOST_ASIO_CHECK(count == 0); | |
193 | end = now(); | |
194 | expected_end = start + seconds(2); | |
195 | BOOST_ASIO_CHECK(end < expected_end); | |
196 | ||
197 | // Wait on the timer again without cancelling it. This time the asynchronous | |
198 | // wait should run to completion and increment the counter. | |
199 | t5.async_wait(bindns::bind(increment_if_not_cancelled, &count, _1)); | |
200 | ||
b32b8144 FG |
201 | ioc.restart(); |
202 | ioc.run(); | |
7c673cae FG |
203 | |
204 | // The timer should not have been cancelled, so count should have changed. | |
205 | // The total time since the timer was created should be more than 10 seconds. | |
206 | BOOST_ASIO_CHECK(count == 1); | |
207 | end = now(); | |
208 | expected_end = start + seconds(10); | |
209 | BOOST_ASIO_CHECK(expected_end < end || expected_end == end); | |
210 | ||
211 | count = 0; | |
212 | start = now(); | |
213 | ||
214 | // Start two waits on a timer, one of which will be cancelled. The one | |
215 | // which is not cancelled should still run to completion and increment the | |
216 | // counter. | |
b32b8144 | 217 | boost::asio::system_timer t7(ioc, seconds(3)); |
7c673cae FG |
218 | t7.async_wait(bindns::bind(increment_if_not_cancelled, &count, _1)); |
219 | t7.async_wait(bindns::bind(increment_if_not_cancelled, &count, _1)); | |
b32b8144 | 220 | boost::asio::system_timer t8(ioc, seconds(1)); |
7c673cae FG |
221 | t8.async_wait(bindns::bind(cancel_one_timer, &t7)); |
222 | ||
b32b8144 FG |
223 | ioc.restart(); |
224 | ioc.run(); | |
7c673cae FG |
225 | |
226 | // One of the waits should not have been cancelled, so count should have | |
227 | // changed. The total time since the timer was created should be more than 3 | |
228 | // seconds. | |
229 | BOOST_ASIO_CHECK(count == 1); | |
230 | end = now(); | |
231 | expected_end = start + seconds(3); | |
232 | BOOST_ASIO_CHECK(expected_end < end || expected_end == end); | |
233 | } | |
234 | ||
b32b8144 | 235 | struct timer_handler |
7c673cae | 236 | { |
b32b8144 FG |
237 | timer_handler() {} |
238 | void operator()(const boost::system::error_code&) {} | |
239 | #if defined(BOOST_ASIO_HAS_MOVE) | |
240 | timer_handler(timer_handler&&) {} | |
241 | private: | |
242 | timer_handler(const timer_handler&); | |
243 | #endif // defined(BOOST_ASIO_HAS_MOVE) | |
244 | }; | |
7c673cae FG |
245 | |
246 | void system_timer_cancel_test() | |
247 | { | |
b32b8144 | 248 | static boost::asio::io_context io_context; |
7c673cae FG |
249 | struct timer |
250 | { | |
251 | boost::asio::system_timer t; | |
b32b8144 | 252 | timer() : t(io_context) |
7c673cae FG |
253 | { |
254 | t.expires_at((boost::asio::system_timer::time_point::max)()); | |
255 | } | |
256 | } timers[50]; | |
257 | ||
b32b8144 FG |
258 | timers[2].t.async_wait(timer_handler()); |
259 | timers[41].t.async_wait(timer_handler()); | |
7c673cae | 260 | for (int i = 10; i < 20; ++i) |
b32b8144 | 261 | timers[i].t.async_wait(timer_handler()); |
7c673cae FG |
262 | |
263 | BOOST_ASIO_CHECK(timers[2].t.cancel() == 1); | |
264 | BOOST_ASIO_CHECK(timers[41].t.cancel() == 1); | |
265 | for (int i = 10; i < 20; ++i) | |
266 | BOOST_ASIO_CHECK(timers[i].t.cancel() == 1); | |
267 | } | |
268 | ||
269 | struct custom_allocation_timer_handler | |
270 | { | |
271 | custom_allocation_timer_handler(int* count) : count_(count) {} | |
272 | void operator()(const boost::system::error_code&) {} | |
273 | int* count_; | |
274 | }; | |
275 | ||
276 | void* asio_handler_allocate(std::size_t size, | |
277 | custom_allocation_timer_handler* handler) | |
278 | { | |
279 | ++(*handler->count_); | |
280 | return ::operator new(size); | |
281 | } | |
282 | ||
283 | void asio_handler_deallocate(void* pointer, std::size_t, | |
284 | custom_allocation_timer_handler* handler) | |
285 | { | |
286 | --(*handler->count_); | |
287 | ::operator delete(pointer); | |
288 | } | |
289 | ||
290 | void system_timer_custom_allocation_test() | |
291 | { | |
b32b8144 | 292 | static boost::asio::io_context io_context; |
7c673cae FG |
293 | struct timer |
294 | { | |
295 | boost::asio::system_timer t; | |
b32b8144 | 296 | timer() : t(io_context) {} |
7c673cae FG |
297 | } timers[100]; |
298 | ||
299 | int allocation_count = 0; | |
300 | ||
301 | for (int i = 0; i < 50; ++i) | |
302 | { | |
303 | timers[i].t.expires_at((boost::asio::system_timer::time_point::max)()); | |
304 | timers[i].t.async_wait(custom_allocation_timer_handler(&allocation_count)); | |
305 | } | |
306 | ||
307 | for (int i = 50; i < 100; ++i) | |
308 | { | |
309 | timers[i].t.expires_at((boost::asio::system_timer::time_point::min)()); | |
310 | timers[i].t.async_wait(custom_allocation_timer_handler(&allocation_count)); | |
311 | } | |
312 | ||
313 | for (int i = 0; i < 50; ++i) | |
314 | timers[i].t.cancel(); | |
315 | ||
b32b8144 | 316 | io_context.run(); |
7c673cae FG |
317 | |
318 | BOOST_ASIO_CHECK(allocation_count == 0); | |
319 | } | |
320 | ||
b32b8144 | 321 | void io_context_run(boost::asio::io_context* ioc) |
7c673cae | 322 | { |
b32b8144 | 323 | ioc->run(); |
7c673cae FG |
324 | } |
325 | ||
326 | void system_timer_thread_test() | |
327 | { | |
b32b8144 FG |
328 | boost::asio::io_context ioc; |
329 | boost::asio::executor_work_guard<boost::asio::io_context::executor_type> work | |
330 | = boost::asio::make_work_guard(ioc); | |
331 | boost::asio::system_timer t1(ioc); | |
332 | boost::asio::system_timer t2(ioc); | |
7c673cae FG |
333 | int count = 0; |
334 | ||
b32b8144 | 335 | boost::asio::detail::thread th(bindns::bind(io_context_run, &ioc)); |
7c673cae | 336 | |
b32b8144 | 337 | t2.expires_after(boost::asio::chrono::seconds(2)); |
7c673cae FG |
338 | t2.wait(); |
339 | ||
b32b8144 | 340 | t1.expires_after(boost::asio::chrono::seconds(2)); |
7c673cae FG |
341 | t1.async_wait(bindns::bind(increment, &count)); |
342 | ||
b32b8144 | 343 | t2.expires_after(boost::asio::chrono::seconds(4)); |
7c673cae FG |
344 | t2.wait(); |
345 | ||
b32b8144 | 346 | ioc.stop(); |
7c673cae FG |
347 | th.join(); |
348 | ||
349 | BOOST_ASIO_CHECK(count == 1); | |
350 | } | |
351 | ||
b32b8144 FG |
352 | #if defined(BOOST_ASIO_HAS_MOVE) |
353 | boost::asio::system_timer make_timer(boost::asio::io_context& ioc, int* count) | |
354 | { | |
355 | boost::asio::system_timer t(ioc); | |
356 | t.expires_after(boost::asio::chrono::seconds(1)); | |
357 | t.async_wait(bindns::bind(increment, count)); | |
358 | return t; | |
359 | } | |
360 | #endif | |
361 | ||
362 | void system_timer_move_test() | |
363 | { | |
364 | #if defined(BOOST_ASIO_HAS_MOVE) | |
365 | boost::asio::io_context io_context1; | |
366 | boost::asio::io_context io_context2; | |
367 | int count = 0; | |
368 | ||
369 | boost::asio::system_timer t1 = make_timer(io_context1, &count); | |
370 | boost::asio::system_timer t2 = make_timer(io_context2, &count); | |
371 | boost::asio::system_timer t3 = std::move(t1); | |
372 | ||
373 | t2 = std::move(t1); | |
374 | ||
375 | io_context2.run(); | |
376 | ||
377 | BOOST_ASIO_CHECK(count == 1); | |
378 | ||
379 | io_context1.run(); | |
380 | ||
381 | BOOST_ASIO_CHECK(count == 2); | |
382 | #endif // defined(BOOST_ASIO_HAS_MOVE) | |
383 | } | |
384 | ||
7c673cae FG |
385 | BOOST_ASIO_TEST_SUITE |
386 | ( | |
387 | "system_timer", | |
388 | BOOST_ASIO_TEST_CASE(system_timer_test) | |
389 | BOOST_ASIO_TEST_CASE(system_timer_cancel_test) | |
390 | BOOST_ASIO_TEST_CASE(system_timer_custom_allocation_test) | |
391 | BOOST_ASIO_TEST_CASE(system_timer_thread_test) | |
b32b8144 | 392 | BOOST_ASIO_TEST_CASE(system_timer_move_test) |
7c673cae FG |
393 | ) |
394 | #else // defined(BOOST_ASIO_HAS_STD_CHRONO) | |
395 | BOOST_ASIO_TEST_SUITE | |
396 | ( | |
397 | "system_timer", | |
398 | BOOST_ASIO_TEST_CASE(null_test) | |
399 | ) | |
400 | #endif // defined(BOOST_ASIO_HAS_STD_CHRONO) |