]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // |
2 | // system_timer.cpp | |
3 | // ~~~~~~~~~~~~~~~~ | |
4 | // | |
92f5a8d4 | 5 | // Copyright (c) 2003-2019 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; |
92f5a8d4 | 99 | const boost::asio::io_context::executor_type ioc_ex = ioc.get_executor(); |
7c673cae FG |
100 | int count = 0; |
101 | ||
102 | boost::asio::system_timer::time_point start = now(); | |
103 | ||
b32b8144 | 104 | boost::asio::system_timer t1(ioc, seconds(1)); |
7c673cae FG |
105 | t1.wait(); |
106 | ||
107 | // The timer must block until after its expiry time. | |
108 | boost::asio::system_timer::time_point end = now(); | |
109 | boost::asio::system_timer::time_point expected_end = start + seconds(1); | |
110 | BOOST_ASIO_CHECK(expected_end < end || expected_end == end); | |
111 | ||
112 | start = now(); | |
113 | ||
92f5a8d4 | 114 | boost::asio::system_timer t2(ioc_ex, seconds(1) + microseconds(500000)); |
7c673cae FG |
115 | t2.wait(); |
116 | ||
117 | // The timer must block until after its expiry time. | |
118 | end = now(); | |
119 | expected_end = start + seconds(1) + microseconds(500000); | |
120 | BOOST_ASIO_CHECK(expected_end < end || expected_end == end); | |
121 | ||
b32b8144 | 122 | t2.expires_at(t2.expiry() + seconds(1)); |
7c673cae FG |
123 | t2.wait(); |
124 | ||
125 | // The timer must block until after its expiry time. | |
126 | end = now(); | |
127 | expected_end += seconds(1); | |
128 | BOOST_ASIO_CHECK(expected_end < end || expected_end == end); | |
129 | ||
130 | start = now(); | |
131 | ||
b32b8144 | 132 | t2.expires_after(seconds(1) + microseconds(200000)); |
7c673cae FG |
133 | t2.wait(); |
134 | ||
135 | // The timer must block until after its expiry time. | |
136 | end = now(); | |
137 | expected_end = start + seconds(1) + microseconds(200000); | |
138 | BOOST_ASIO_CHECK(expected_end < end || expected_end == end); | |
139 | ||
140 | start = now(); | |
141 | ||
b32b8144 | 142 | boost::asio::system_timer t3(ioc, seconds(5)); |
7c673cae FG |
143 | t3.async_wait(bindns::bind(increment, &count)); |
144 | ||
145 | // No completions can be delivered until run() is called. | |
146 | BOOST_ASIO_CHECK(count == 0); | |
147 | ||
b32b8144 | 148 | ioc.run(); |
7c673cae FG |
149 | |
150 | // The run() call will not return until all operations have finished, and | |
151 | // this should not be until after the timer's expiry time. | |
152 | BOOST_ASIO_CHECK(count == 1); | |
153 | end = now(); | |
154 | expected_end = start + seconds(1); | |
155 | BOOST_ASIO_CHECK(expected_end < end || expected_end == end); | |
156 | ||
157 | count = 3; | |
158 | start = now(); | |
159 | ||
b32b8144 | 160 | boost::asio::system_timer t4(ioc, seconds(1)); |
7c673cae FG |
161 | t4.async_wait(bindns::bind(decrement_to_zero, &t4, &count)); |
162 | ||
163 | // No completions can be delivered until run() is called. | |
164 | BOOST_ASIO_CHECK(count == 3); | |
165 | ||
b32b8144 FG |
166 | ioc.restart(); |
167 | ioc.run(); | |
7c673cae FG |
168 | |
169 | // The run() call will not return until all operations have finished, and | |
170 | // this should not be until after the timer's final expiry time. | |
171 | BOOST_ASIO_CHECK(count == 0); | |
172 | end = now(); | |
173 | expected_end = start + seconds(3); | |
174 | BOOST_ASIO_CHECK(expected_end < end || expected_end == end); | |
175 | ||
176 | count = 0; | |
177 | start = now(); | |
178 | ||
b32b8144 | 179 | boost::asio::system_timer t5(ioc, seconds(10)); |
7c673cae | 180 | t5.async_wait(bindns::bind(increment_if_not_cancelled, &count, _1)); |
b32b8144 | 181 | boost::asio::system_timer t6(ioc, seconds(1)); |
7c673cae FG |
182 | t6.async_wait(bindns::bind(cancel_timer, &t5)); |
183 | ||
184 | // No completions can be delivered until run() is called. | |
185 | BOOST_ASIO_CHECK(count == 0); | |
186 | ||
b32b8144 FG |
187 | ioc.restart(); |
188 | ioc.run(); | |
7c673cae FG |
189 | |
190 | // The timer should have been cancelled, so count should not have changed. | |
191 | // The total run time should not have been much more than 1 second (and | |
192 | // certainly far less than 10 seconds). | |
193 | BOOST_ASIO_CHECK(count == 0); | |
194 | end = now(); | |
195 | expected_end = start + seconds(2); | |
196 | BOOST_ASIO_CHECK(end < expected_end); | |
197 | ||
198 | // Wait on the timer again without cancelling it. This time the asynchronous | |
199 | // wait should run to completion and increment the counter. | |
200 | t5.async_wait(bindns::bind(increment_if_not_cancelled, &count, _1)); | |
201 | ||
b32b8144 FG |
202 | ioc.restart(); |
203 | ioc.run(); | |
7c673cae FG |
204 | |
205 | // The timer should not have been cancelled, so count should have changed. | |
206 | // The total time since the timer was created should be more than 10 seconds. | |
207 | BOOST_ASIO_CHECK(count == 1); | |
208 | end = now(); | |
209 | expected_end = start + seconds(10); | |
210 | BOOST_ASIO_CHECK(expected_end < end || expected_end == end); | |
211 | ||
212 | count = 0; | |
213 | start = now(); | |
214 | ||
215 | // Start two waits on a timer, one of which will be cancelled. The one | |
216 | // which is not cancelled should still run to completion and increment the | |
217 | // counter. | |
b32b8144 | 218 | boost::asio::system_timer t7(ioc, seconds(3)); |
7c673cae FG |
219 | t7.async_wait(bindns::bind(increment_if_not_cancelled, &count, _1)); |
220 | t7.async_wait(bindns::bind(increment_if_not_cancelled, &count, _1)); | |
b32b8144 | 221 | boost::asio::system_timer t8(ioc, seconds(1)); |
7c673cae FG |
222 | t8.async_wait(bindns::bind(cancel_one_timer, &t7)); |
223 | ||
b32b8144 FG |
224 | ioc.restart(); |
225 | ioc.run(); | |
7c673cae FG |
226 | |
227 | // One of the waits should not have been cancelled, so count should have | |
228 | // changed. The total time since the timer was created should be more than 3 | |
229 | // seconds. | |
230 | BOOST_ASIO_CHECK(count == 1); | |
231 | end = now(); | |
232 | expected_end = start + seconds(3); | |
233 | BOOST_ASIO_CHECK(expected_end < end || expected_end == end); | |
234 | } | |
235 | ||
b32b8144 | 236 | struct timer_handler |
7c673cae | 237 | { |
b32b8144 FG |
238 | timer_handler() {} |
239 | void operator()(const boost::system::error_code&) {} | |
240 | #if defined(BOOST_ASIO_HAS_MOVE) | |
241 | timer_handler(timer_handler&&) {} | |
242 | private: | |
243 | timer_handler(const timer_handler&); | |
244 | #endif // defined(BOOST_ASIO_HAS_MOVE) | |
245 | }; | |
7c673cae FG |
246 | |
247 | void system_timer_cancel_test() | |
248 | { | |
b32b8144 | 249 | static boost::asio::io_context io_context; |
7c673cae FG |
250 | struct timer |
251 | { | |
252 | boost::asio::system_timer t; | |
b32b8144 | 253 | timer() : t(io_context) |
7c673cae FG |
254 | { |
255 | t.expires_at((boost::asio::system_timer::time_point::max)()); | |
256 | } | |
257 | } timers[50]; | |
258 | ||
b32b8144 FG |
259 | timers[2].t.async_wait(timer_handler()); |
260 | timers[41].t.async_wait(timer_handler()); | |
7c673cae | 261 | for (int i = 10; i < 20; ++i) |
b32b8144 | 262 | timers[i].t.async_wait(timer_handler()); |
7c673cae FG |
263 | |
264 | BOOST_ASIO_CHECK(timers[2].t.cancel() == 1); | |
265 | BOOST_ASIO_CHECK(timers[41].t.cancel() == 1); | |
266 | for (int i = 10; i < 20; ++i) | |
267 | BOOST_ASIO_CHECK(timers[i].t.cancel() == 1); | |
268 | } | |
269 | ||
270 | struct custom_allocation_timer_handler | |
271 | { | |
272 | custom_allocation_timer_handler(int* count) : count_(count) {} | |
273 | void operator()(const boost::system::error_code&) {} | |
274 | int* count_; | |
275 | }; | |
276 | ||
277 | void* asio_handler_allocate(std::size_t size, | |
278 | custom_allocation_timer_handler* handler) | |
279 | { | |
280 | ++(*handler->count_); | |
281 | return ::operator new(size); | |
282 | } | |
283 | ||
284 | void asio_handler_deallocate(void* pointer, std::size_t, | |
285 | custom_allocation_timer_handler* handler) | |
286 | { | |
287 | --(*handler->count_); | |
288 | ::operator delete(pointer); | |
289 | } | |
290 | ||
291 | void system_timer_custom_allocation_test() | |
292 | { | |
b32b8144 | 293 | static boost::asio::io_context io_context; |
7c673cae FG |
294 | struct timer |
295 | { | |
296 | boost::asio::system_timer t; | |
b32b8144 | 297 | timer() : t(io_context) {} |
7c673cae FG |
298 | } timers[100]; |
299 | ||
300 | int allocation_count = 0; | |
301 | ||
302 | for (int i = 0; i < 50; ++i) | |
303 | { | |
304 | timers[i].t.expires_at((boost::asio::system_timer::time_point::max)()); | |
305 | timers[i].t.async_wait(custom_allocation_timer_handler(&allocation_count)); | |
306 | } | |
307 | ||
308 | for (int i = 50; i < 100; ++i) | |
309 | { | |
310 | timers[i].t.expires_at((boost::asio::system_timer::time_point::min)()); | |
311 | timers[i].t.async_wait(custom_allocation_timer_handler(&allocation_count)); | |
312 | } | |
313 | ||
314 | for (int i = 0; i < 50; ++i) | |
315 | timers[i].t.cancel(); | |
316 | ||
b32b8144 | 317 | io_context.run(); |
7c673cae FG |
318 | |
319 | BOOST_ASIO_CHECK(allocation_count == 0); | |
320 | } | |
321 | ||
b32b8144 | 322 | void io_context_run(boost::asio::io_context* ioc) |
7c673cae | 323 | { |
b32b8144 | 324 | ioc->run(); |
7c673cae FG |
325 | } |
326 | ||
327 | void system_timer_thread_test() | |
328 | { | |
b32b8144 FG |
329 | boost::asio::io_context ioc; |
330 | boost::asio::executor_work_guard<boost::asio::io_context::executor_type> work | |
331 | = boost::asio::make_work_guard(ioc); | |
332 | boost::asio::system_timer t1(ioc); | |
333 | boost::asio::system_timer t2(ioc); | |
7c673cae FG |
334 | int count = 0; |
335 | ||
b32b8144 | 336 | boost::asio::detail::thread th(bindns::bind(io_context_run, &ioc)); |
7c673cae | 337 | |
b32b8144 | 338 | t2.expires_after(boost::asio::chrono::seconds(2)); |
7c673cae FG |
339 | t2.wait(); |
340 | ||
b32b8144 | 341 | t1.expires_after(boost::asio::chrono::seconds(2)); |
7c673cae FG |
342 | t1.async_wait(bindns::bind(increment, &count)); |
343 | ||
b32b8144 | 344 | t2.expires_after(boost::asio::chrono::seconds(4)); |
7c673cae FG |
345 | t2.wait(); |
346 | ||
b32b8144 | 347 | ioc.stop(); |
7c673cae FG |
348 | th.join(); |
349 | ||
350 | BOOST_ASIO_CHECK(count == 1); | |
351 | } | |
352 | ||
b32b8144 FG |
353 | #if defined(BOOST_ASIO_HAS_MOVE) |
354 | boost::asio::system_timer make_timer(boost::asio::io_context& ioc, int* count) | |
355 | { | |
356 | boost::asio::system_timer t(ioc); | |
357 | t.expires_after(boost::asio::chrono::seconds(1)); | |
358 | t.async_wait(bindns::bind(increment, count)); | |
359 | return t; | |
360 | } | |
361 | #endif | |
362 | ||
363 | void system_timer_move_test() | |
364 | { | |
365 | #if defined(BOOST_ASIO_HAS_MOVE) | |
366 | boost::asio::io_context io_context1; | |
367 | boost::asio::io_context io_context2; | |
368 | int count = 0; | |
369 | ||
370 | boost::asio::system_timer t1 = make_timer(io_context1, &count); | |
371 | boost::asio::system_timer t2 = make_timer(io_context2, &count); | |
372 | boost::asio::system_timer t3 = std::move(t1); | |
373 | ||
374 | t2 = std::move(t1); | |
375 | ||
376 | io_context2.run(); | |
377 | ||
378 | BOOST_ASIO_CHECK(count == 1); | |
379 | ||
380 | io_context1.run(); | |
381 | ||
382 | BOOST_ASIO_CHECK(count == 2); | |
383 | #endif // defined(BOOST_ASIO_HAS_MOVE) | |
384 | } | |
385 | ||
7c673cae FG |
386 | BOOST_ASIO_TEST_SUITE |
387 | ( | |
388 | "system_timer", | |
389 | BOOST_ASIO_TEST_CASE(system_timer_test) | |
390 | BOOST_ASIO_TEST_CASE(system_timer_cancel_test) | |
391 | BOOST_ASIO_TEST_CASE(system_timer_custom_allocation_test) | |
392 | BOOST_ASIO_TEST_CASE(system_timer_thread_test) | |
b32b8144 | 393 | BOOST_ASIO_TEST_CASE(system_timer_move_test) |
7c673cae FG |
394 | ) |
395 | #else // defined(BOOST_ASIO_HAS_STD_CHRONO) | |
396 | BOOST_ASIO_TEST_SUITE | |
397 | ( | |
398 | "system_timer", | |
399 | BOOST_ASIO_TEST_CASE(null_test) | |
400 | ) | |
401 | #endif // defined(BOOST_ASIO_HAS_STD_CHRONO) |