]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // |
2 | // deadline_timer.cpp | |
3 | // ~~~~~~~~~~~~~~~~~~ | |
4 | // | |
1e59de90 | 5 | // Copyright (c) 2003-2022 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 | // 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 | ||
f67539c2 | 23 | #include <boost/bind/bind.hpp> |
7c673cae | 24 | #include "archetypes/async_result.hpp" |
b32b8144 FG |
25 | #include <boost/asio/executor_work_guard.hpp> |
26 | #include <boost/asio/io_context.hpp> | |
7c673cae FG |
27 | #include <boost/asio/placeholders.hpp> |
28 | #include <boost/asio/detail/thread.hpp> | |
29 | ||
30 | using namespace boost::posix_time; | |
31 | ||
32 | void increment(int* count) | |
33 | { | |
34 | ++(*count); | |
35 | } | |
36 | ||
37 | void decrement_to_zero(boost::asio::deadline_timer* t, int* count) | |
38 | { | |
39 | if (*count > 0) | |
40 | { | |
41 | --(*count); | |
42 | ||
43 | int before_value = *count; | |
44 | ||
45 | t->expires_at(t->expires_at() + seconds(1)); | |
46 | t->async_wait(boost::bind(decrement_to_zero, t, count)); | |
47 | ||
48 | // Completion cannot nest, so count value should remain unchanged. | |
49 | BOOST_ASIO_CHECK(*count == before_value); | |
50 | } | |
51 | } | |
52 | ||
53 | void increment_if_not_cancelled(int* count, | |
54 | const boost::system::error_code& ec) | |
55 | { | |
56 | if (!ec) | |
57 | ++(*count); | |
58 | } | |
59 | ||
60 | void cancel_timer(boost::asio::deadline_timer* t) | |
61 | { | |
62 | std::size_t num_cancelled = t->cancel(); | |
63 | BOOST_ASIO_CHECK(num_cancelled == 1); | |
64 | } | |
65 | ||
66 | void cancel_one_timer(boost::asio::deadline_timer* t) | |
67 | { | |
68 | std::size_t num_cancelled = t->cancel_one(); | |
69 | BOOST_ASIO_CHECK(num_cancelled == 1); | |
70 | } | |
71 | ||
72 | ptime now() | |
73 | { | |
74 | #if defined(BOOST_DATE_TIME_HAS_HIGH_PRECISION_CLOCK) | |
75 | return microsec_clock::universal_time(); | |
76 | #else // defined(BOOST_DATE_TIME_HAS_HIGH_PRECISION_CLOCK) | |
77 | return second_clock::universal_time(); | |
78 | #endif // defined(BOOST_DATE_TIME_HAS_HIGH_PRECISION_CLOCK) | |
79 | } | |
80 | ||
81 | void deadline_timer_test() | |
82 | { | |
b32b8144 | 83 | boost::asio::io_context ioc; |
7c673cae FG |
84 | int count = 0; |
85 | ||
86 | ptime start = now(); | |
87 | ||
b32b8144 | 88 | boost::asio::deadline_timer t1(ioc, seconds(1)); |
7c673cae FG |
89 | t1.wait(); |
90 | ||
91 | // The timer must block until after its expiry time. | |
92 | ptime end = now(); | |
93 | ptime expected_end = start + seconds(1); | |
94 | BOOST_ASIO_CHECK(expected_end < end || expected_end == end); | |
95 | ||
96 | start = now(); | |
97 | ||
b32b8144 | 98 | boost::asio::deadline_timer t2(ioc, seconds(1) + microseconds(500000)); |
7c673cae FG |
99 | t2.wait(); |
100 | ||
101 | // The timer must block until after its expiry time. | |
102 | end = now(); | |
103 | expected_end = start + seconds(1) + microseconds(500000); | |
104 | BOOST_ASIO_CHECK(expected_end < end || expected_end == end); | |
105 | ||
106 | t2.expires_at(t2.expires_at() + seconds(1)); | |
107 | t2.wait(); | |
108 | ||
109 | // The timer must block until after its expiry time. | |
110 | end = now(); | |
111 | expected_end += seconds(1); | |
112 | BOOST_ASIO_CHECK(expected_end < end || expected_end == end); | |
113 | ||
114 | start = now(); | |
115 | ||
116 | t2.expires_from_now(seconds(1) + microseconds(200000)); | |
117 | t2.wait(); | |
118 | ||
119 | // The timer must block until after its expiry time. | |
120 | end = now(); | |
121 | expected_end = start + seconds(1) + microseconds(200000); | |
122 | BOOST_ASIO_CHECK(expected_end < end || expected_end == end); | |
123 | ||
124 | start = now(); | |
125 | ||
b32b8144 | 126 | boost::asio::deadline_timer t3(ioc, seconds(5)); |
7c673cae FG |
127 | t3.async_wait(boost::bind(increment, &count)); |
128 | ||
129 | // No completions can be delivered until run() is called. | |
130 | BOOST_ASIO_CHECK(count == 0); | |
131 | ||
b32b8144 | 132 | ioc.run(); |
7c673cae FG |
133 | |
134 | // The run() call will not return until all operations have finished, and | |
135 | // this should not be until after the timer's expiry time. | |
136 | BOOST_ASIO_CHECK(count == 1); | |
137 | end = now(); | |
138 | expected_end = start + seconds(1); | |
139 | BOOST_ASIO_CHECK(expected_end < end || expected_end == end); | |
140 | ||
141 | count = 3; | |
142 | start = now(); | |
143 | ||
b32b8144 | 144 | boost::asio::deadline_timer t4(ioc, seconds(1)); |
7c673cae FG |
145 | t4.async_wait(boost::bind(decrement_to_zero, &t4, &count)); |
146 | ||
147 | // No completions can be delivered until run() is called. | |
148 | BOOST_ASIO_CHECK(count == 3); | |
149 | ||
b32b8144 FG |
150 | ioc.restart(); |
151 | ioc.run(); | |
7c673cae FG |
152 | |
153 | // The run() call will not return until all operations have finished, and | |
154 | // this should not be until after the timer's final expiry time. | |
155 | BOOST_ASIO_CHECK(count == 0); | |
156 | end = now(); | |
157 | expected_end = start + seconds(3); | |
158 | BOOST_ASIO_CHECK(expected_end < end || expected_end == end); | |
159 | ||
160 | count = 0; | |
161 | start = now(); | |
162 | ||
b32b8144 | 163 | boost::asio::deadline_timer t5(ioc, seconds(10)); |
7c673cae FG |
164 | t5.async_wait(boost::bind(increment_if_not_cancelled, &count, |
165 | boost::asio::placeholders::error)); | |
b32b8144 | 166 | boost::asio::deadline_timer t6(ioc, seconds(1)); |
7c673cae FG |
167 | t6.async_wait(boost::bind(cancel_timer, &t5)); |
168 | ||
169 | // No completions can be delivered until run() is called. | |
170 | BOOST_ASIO_CHECK(count == 0); | |
171 | ||
b32b8144 FG |
172 | ioc.restart(); |
173 | ioc.run(); | |
7c673cae FG |
174 | |
175 | // The timer should have been cancelled, so count should not have changed. | |
176 | // The total run time should not have been much more than 1 second (and | |
177 | // certainly far less than 10 seconds). | |
178 | BOOST_ASIO_CHECK(count == 0); | |
179 | end = now(); | |
180 | expected_end = start + seconds(2); | |
181 | BOOST_ASIO_CHECK(end < expected_end); | |
182 | ||
183 | // Wait on the timer again without cancelling it. This time the asynchronous | |
184 | // wait should run to completion and increment the counter. | |
185 | t5.async_wait(boost::bind(increment_if_not_cancelled, &count, | |
186 | boost::asio::placeholders::error)); | |
187 | ||
b32b8144 FG |
188 | ioc.restart(); |
189 | ioc.run(); | |
7c673cae FG |
190 | |
191 | // The timer should not have been cancelled, so count should have changed. | |
192 | // The total time since the timer was created should be more than 10 seconds. | |
193 | BOOST_ASIO_CHECK(count == 1); | |
194 | end = now(); | |
195 | expected_end = start + seconds(10); | |
196 | BOOST_ASIO_CHECK(expected_end < end || expected_end == end); | |
197 | ||
198 | count = 0; | |
199 | start = now(); | |
200 | ||
201 | // Start two waits on a timer, one of which will be cancelled. The one | |
202 | // which is not cancelled should still run to completion and increment the | |
203 | // counter. | |
b32b8144 | 204 | boost::asio::deadline_timer t7(ioc, seconds(3)); |
7c673cae FG |
205 | t7.async_wait(boost::bind(increment_if_not_cancelled, &count, |
206 | boost::asio::placeholders::error)); | |
207 | t7.async_wait(boost::bind(increment_if_not_cancelled, &count, | |
208 | boost::asio::placeholders::error)); | |
b32b8144 | 209 | boost::asio::deadline_timer t8(ioc, seconds(1)); |
7c673cae FG |
210 | t8.async_wait(boost::bind(cancel_one_timer, &t7)); |
211 | ||
b32b8144 FG |
212 | ioc.restart(); |
213 | ioc.run(); | |
7c673cae FG |
214 | |
215 | // One of the waits should not have been cancelled, so count should have | |
216 | // changed. The total time since the timer was created should be more than 3 | |
217 | // seconds. | |
218 | BOOST_ASIO_CHECK(count == 1); | |
219 | end = now(); | |
220 | expected_end = start + seconds(3); | |
221 | BOOST_ASIO_CHECK(expected_end < end || expected_end == end); | |
222 | } | |
223 | ||
224 | void timer_handler(const boost::system::error_code&) | |
225 | { | |
226 | } | |
227 | ||
228 | void deadline_timer_cancel_test() | |
229 | { | |
b32b8144 | 230 | static boost::asio::io_context io_context; |
7c673cae FG |
231 | struct timer |
232 | { | |
233 | boost::asio::deadline_timer t; | |
b32b8144 | 234 | timer() : t(io_context) { t.expires_at(boost::posix_time::pos_infin); } |
7c673cae FG |
235 | } timers[50]; |
236 | ||
237 | timers[2].t.async_wait(&timer_handler); | |
238 | timers[41].t.async_wait(&timer_handler); | |
239 | for (int i = 10; i < 20; ++i) | |
240 | timers[i].t.async_wait(&timer_handler); | |
241 | ||
242 | BOOST_ASIO_CHECK(timers[2].t.cancel() == 1); | |
243 | BOOST_ASIO_CHECK(timers[41].t.cancel() == 1); | |
244 | for (int i = 10; i < 20; ++i) | |
245 | BOOST_ASIO_CHECK(timers[i].t.cancel() == 1); | |
246 | } | |
247 | ||
248 | struct custom_allocation_timer_handler | |
249 | { | |
250 | custom_allocation_timer_handler(int* count) : count_(count) {} | |
251 | void operator()(const boost::system::error_code&) {} | |
252 | int* count_; | |
7c673cae | 253 | |
20effc67 TL |
254 | template <typename T> |
255 | struct allocator | |
256 | { | |
257 | typedef size_t size_type; | |
258 | typedef ptrdiff_t difference_type; | |
259 | typedef T* pointer; | |
260 | typedef const T* const_pointer; | |
261 | typedef T& reference; | |
262 | typedef const T& const_reference; | |
263 | typedef T value_type; | |
264 | ||
265 | template <typename U> | |
266 | struct rebind | |
267 | { | |
268 | typedef allocator<U> other; | |
269 | }; | |
270 | ||
271 | explicit allocator(int* count) BOOST_ASIO_NOEXCEPT | |
272 | : count_(count) | |
273 | { | |
274 | } | |
275 | ||
276 | allocator(const allocator& other) BOOST_ASIO_NOEXCEPT | |
277 | : count_(other.count_) | |
278 | { | |
279 | } | |
280 | ||
281 | template <typename U> | |
282 | allocator(const allocator<U>& other) BOOST_ASIO_NOEXCEPT | |
283 | : count_(other.count_) | |
284 | { | |
285 | } | |
286 | ||
287 | pointer allocate(size_type n, const void* = 0) | |
288 | { | |
289 | ++(*count_); | |
290 | return static_cast<T*>(::operator new(sizeof(T) * n)); | |
291 | } | |
292 | ||
293 | void deallocate(pointer p, size_type) | |
294 | { | |
295 | --(*count_); | |
296 | ::operator delete(p); | |
297 | } | |
298 | ||
299 | size_type max_size() const | |
300 | { | |
301 | return ~size_type(0); | |
302 | } | |
303 | ||
304 | void construct(pointer p, const T& v) | |
305 | { | |
306 | new (p) T(v); | |
307 | } | |
308 | ||
309 | void destroy(pointer p) | |
310 | { | |
311 | p->~T(); | |
312 | } | |
313 | ||
314 | int* count_; | |
315 | }; | |
316 | ||
317 | typedef allocator<int> allocator_type; | |
318 | ||
319 | allocator_type get_allocator() const BOOST_ASIO_NOEXCEPT | |
320 | { | |
321 | return allocator_type(count_); | |
322 | } | |
323 | }; | |
7c673cae FG |
324 | |
325 | void deadline_timer_custom_allocation_test() | |
326 | { | |
b32b8144 | 327 | static boost::asio::io_context io_context; |
7c673cae FG |
328 | struct timer |
329 | { | |
330 | boost::asio::deadline_timer t; | |
b32b8144 | 331 | timer() : t(io_context) {} |
7c673cae FG |
332 | } timers[100]; |
333 | ||
334 | int allocation_count = 0; | |
335 | ||
336 | for (int i = 0; i < 50; ++i) | |
337 | { | |
338 | timers[i].t.expires_at(boost::posix_time::pos_infin); | |
339 | timers[i].t.async_wait(custom_allocation_timer_handler(&allocation_count)); | |
340 | } | |
341 | ||
342 | for (int i = 50; i < 100; ++i) | |
343 | { | |
344 | timers[i].t.expires_at(boost::posix_time::neg_infin); | |
345 | timers[i].t.async_wait(custom_allocation_timer_handler(&allocation_count)); | |
346 | } | |
347 | ||
348 | for (int i = 0; i < 50; ++i) | |
349 | timers[i].t.cancel(); | |
350 | ||
b32b8144 | 351 | io_context.run(); |
7c673cae FG |
352 | |
353 | BOOST_ASIO_CHECK(allocation_count == 0); | |
354 | } | |
355 | ||
b32b8144 | 356 | void io_context_run(boost::asio::io_context* ioc) |
7c673cae | 357 | { |
b32b8144 | 358 | ioc->run(); |
7c673cae FG |
359 | } |
360 | ||
361 | void deadline_timer_thread_test() | |
362 | { | |
b32b8144 FG |
363 | boost::asio::io_context ioc; |
364 | boost::asio::executor_work_guard<boost::asio::io_context::executor_type> work | |
365 | = boost::asio::make_work_guard(ioc); | |
366 | boost::asio::deadline_timer t1(ioc); | |
367 | boost::asio::deadline_timer t2(ioc); | |
7c673cae FG |
368 | int count = 0; |
369 | ||
b32b8144 | 370 | boost::asio::detail::thread th(boost::bind(io_context_run, &ioc)); |
7c673cae FG |
371 | |
372 | t2.expires_from_now(boost::posix_time::seconds(2)); | |
373 | t2.wait(); | |
374 | ||
375 | t1.expires_from_now(boost::posix_time::seconds(2)); | |
376 | t1.async_wait(boost::bind(increment, &count)); | |
377 | ||
378 | t2.expires_from_now(boost::posix_time::seconds(4)); | |
379 | t2.wait(); | |
380 | ||
b32b8144 | 381 | ioc.stop(); |
7c673cae FG |
382 | th.join(); |
383 | ||
384 | BOOST_ASIO_CHECK(count == 1); | |
385 | } | |
386 | ||
387 | void deadline_timer_async_result_test() | |
388 | { | |
b32b8144 FG |
389 | boost::asio::io_context ioc; |
390 | boost::asio::deadline_timer t1(ioc); | |
7c673cae FG |
391 | |
392 | t1.expires_from_now(boost::posix_time::seconds(1)); | |
393 | int i = t1.async_wait(archetypes::lazy_handler()); | |
394 | BOOST_ASIO_CHECK(i == 42); | |
395 | ||
b32b8144 FG |
396 | ioc.run(); |
397 | } | |
398 | ||
399 | #if defined(BOOST_ASIO_HAS_MOVE) | |
400 | boost::asio::deadline_timer make_timer(boost::asio::io_context& ioc, int* count) | |
401 | { | |
402 | boost::asio::deadline_timer t(ioc); | |
403 | t.expires_from_now(boost::posix_time::seconds(1)); | |
404 | t.async_wait(boost::bind(increment, count)); | |
405 | return t; | |
406 | } | |
407 | #endif // defined(BOOST_ASIO_HAS_MOVE) | |
408 | ||
409 | void deadline_timer_move_test() | |
410 | { | |
411 | #if defined(BOOST_ASIO_HAS_MOVE) | |
412 | boost::asio::io_context io_context1; | |
413 | boost::asio::io_context io_context2; | |
414 | int count = 0; | |
415 | ||
416 | boost::asio::deadline_timer t1 = make_timer(io_context1, &count); | |
417 | boost::asio::deadline_timer t2 = make_timer(io_context2, &count); | |
418 | boost::asio::deadline_timer t3 = std::move(t1); | |
419 | ||
420 | t2 = std::move(t1); | |
421 | ||
422 | io_context2.run(); | |
423 | ||
424 | BOOST_ASIO_CHECK(count == 1); | |
425 | ||
426 | io_context1.run(); | |
427 | ||
428 | BOOST_ASIO_CHECK(count == 2); | |
429 | #endif // defined(BOOST_ASIO_HAS_MOVE) | |
7c673cae FG |
430 | } |
431 | ||
432 | BOOST_ASIO_TEST_SUITE | |
433 | ( | |
434 | "deadline_timer", | |
435 | BOOST_ASIO_TEST_CASE(deadline_timer_test) | |
436 | BOOST_ASIO_TEST_CASE(deadline_timer_cancel_test) | |
437 | BOOST_ASIO_TEST_CASE(deadline_timer_custom_allocation_test) | |
438 | BOOST_ASIO_TEST_CASE(deadline_timer_thread_test) | |
439 | BOOST_ASIO_TEST_CASE(deadline_timer_async_result_test) | |
b32b8144 | 440 | BOOST_ASIO_TEST_CASE(deadline_timer_move_test) |
7c673cae FG |
441 | ) |
442 | #else // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME) | |
443 | BOOST_ASIO_TEST_SUITE | |
444 | ( | |
445 | "deadline_timer", | |
446 | BOOST_ASIO_TEST_CASE(null_test) | |
447 | ) | |
448 | #endif // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME) |