5 // Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
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)
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)
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)
21 // Test that header file is self-contained.
22 #include <boost/asio/system_timer.hpp>
24 #include "unit_test.hpp"
26 #if defined(BOOST_ASIO_HAS_STD_CHRONO)
28 #include <boost/asio/executor_work_guard.hpp>
29 #include <boost/asio/io_context.hpp>
30 #include <boost/asio/detail/thread.hpp>
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)
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)
44 void increment(int* count
)
49 void decrement_to_zero(boost::asio::system_timer
* t
, int* count
)
55 int before_value
= *count
;
57 t
->expires_at(t
->expiry() + boost::asio::chrono::seconds(1));
58 t
->async_wait(bindns::bind(decrement_to_zero
, t
, count
));
60 // Completion cannot nest, so count value should remain unchanged.
61 BOOST_ASIO_CHECK(*count
== before_value
);
65 void increment_if_not_cancelled(int* count
,
66 const boost::system::error_code
& ec
)
72 void cancel_timer(boost::asio::system_timer
* t
)
74 std::size_t num_cancelled
= t
->cancel();
75 BOOST_ASIO_CHECK(num_cancelled
== 1);
78 void cancel_one_timer(boost::asio::system_timer
* t
)
80 std::size_t num_cancelled
= t
->cancel_one();
81 BOOST_ASIO_CHECK(num_cancelled
== 1);
84 boost::asio::system_timer::time_point
now()
86 return boost::asio::system_timer::clock_type::now();
89 void system_timer_test()
91 using boost::asio::chrono::seconds
;
92 using boost::asio::chrono::microseconds
;
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)
98 boost::asio::io_context ioc
;
99 const boost::asio::io_context::executor_type ioc_ex
= ioc
.get_executor();
102 boost::asio::system_timer::time_point start
= now();
104 boost::asio::system_timer
t1(ioc
, seconds(1));
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
);
114 boost::asio::system_timer
t2(ioc_ex
, seconds(1) + microseconds(500000));
117 // The timer must block until after its expiry time.
119 expected_end
= start
+ seconds(1) + microseconds(500000);
120 BOOST_ASIO_CHECK(expected_end
< end
|| expected_end
== end
);
122 t2
.expires_at(t2
.expiry() + seconds(1));
125 // The timer must block until after its expiry time.
127 expected_end
+= seconds(1);
128 BOOST_ASIO_CHECK(expected_end
< end
|| expected_end
== end
);
132 t2
.expires_after(seconds(1) + microseconds(200000));
135 // The timer must block until after its expiry time.
137 expected_end
= start
+ seconds(1) + microseconds(200000);
138 BOOST_ASIO_CHECK(expected_end
< end
|| expected_end
== end
);
142 boost::asio::system_timer
t3(ioc
, seconds(5));
143 t3
.async_wait(bindns::bind(increment
, &count
));
145 // No completions can be delivered until run() is called.
146 BOOST_ASIO_CHECK(count
== 0);
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);
154 expected_end
= start
+ seconds(1);
155 BOOST_ASIO_CHECK(expected_end
< end
|| expected_end
== end
);
160 boost::asio::system_timer
t4(ioc
, seconds(1));
161 t4
.async_wait(bindns::bind(decrement_to_zero
, &t4
, &count
));
163 // No completions can be delivered until run() is called.
164 BOOST_ASIO_CHECK(count
== 3);
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);
173 expected_end
= start
+ seconds(3);
174 BOOST_ASIO_CHECK(expected_end
< end
|| expected_end
== end
);
179 boost::asio::system_timer
t5(ioc
, seconds(10));
180 t5
.async_wait(bindns::bind(increment_if_not_cancelled
, &count
, _1
));
181 boost::asio::system_timer
t6(ioc
, seconds(1));
182 t6
.async_wait(bindns::bind(cancel_timer
, &t5
));
184 // No completions can be delivered until run() is called.
185 BOOST_ASIO_CHECK(count
== 0);
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);
195 expected_end
= start
+ seconds(2);
196 BOOST_ASIO_CHECK(end
< expected_end
);
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
));
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);
209 expected_end
= start
+ seconds(10);
210 BOOST_ASIO_CHECK(expected_end
< end
|| expected_end
== end
);
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
218 boost::asio::system_timer
t7(ioc
, seconds(3));
219 t7
.async_wait(bindns::bind(increment_if_not_cancelled
, &count
, _1
));
220 t7
.async_wait(bindns::bind(increment_if_not_cancelled
, &count
, _1
));
221 boost::asio::system_timer
t8(ioc
, seconds(1));
222 t8
.async_wait(bindns::bind(cancel_one_timer
, &t7
));
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
230 BOOST_ASIO_CHECK(count
== 1);
232 expected_end
= start
+ seconds(3);
233 BOOST_ASIO_CHECK(expected_end
< end
|| expected_end
== end
);
239 void operator()(const boost::system::error_code
&) {}
240 #if defined(BOOST_ASIO_HAS_MOVE)
241 timer_handler(timer_handler
&&) {}
243 timer_handler(const timer_handler
&);
244 #endif // defined(BOOST_ASIO_HAS_MOVE)
247 void system_timer_cancel_test()
249 static boost::asio::io_context io_context
;
252 boost::asio::system_timer t
;
253 timer() : t(io_context
)
255 t
.expires_at((boost::asio::system_timer::time_point::max
)());
259 timers
[2].t
.async_wait(timer_handler());
260 timers
[41].t
.async_wait(timer_handler());
261 for (int i
= 10; i
< 20; ++i
)
262 timers
[i
].t
.async_wait(timer_handler());
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);
270 struct custom_allocation_timer_handler
272 custom_allocation_timer_handler(int* count
) : count_(count
) {}
273 void operator()(const boost::system::error_code
&) {}
277 void* asio_handler_allocate(std::size_t size
,
278 custom_allocation_timer_handler
* handler
)
280 ++(*handler
->count_
);
281 return ::operator new(size
);
284 void asio_handler_deallocate(void* pointer
, std::size_t,
285 custom_allocation_timer_handler
* handler
)
287 --(*handler
->count_
);
288 ::operator delete(pointer
);
291 void system_timer_custom_allocation_test()
293 static boost::asio::io_context io_context
;
296 boost::asio::system_timer t
;
297 timer() : t(io_context
) {}
300 int allocation_count
= 0;
302 for (int i
= 0; i
< 50; ++i
)
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
));
308 for (int i
= 50; i
< 100; ++i
)
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
));
314 for (int i
= 0; i
< 50; ++i
)
315 timers
[i
].t
.cancel();
319 BOOST_ASIO_CHECK(allocation_count
== 0);
322 void io_context_run(boost::asio::io_context
* ioc
)
327 void system_timer_thread_test()
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
);
336 boost::asio::detail::thread
th(bindns::bind(io_context_run
, &ioc
));
338 t2
.expires_after(boost::asio::chrono::seconds(2));
341 t1
.expires_after(boost::asio::chrono::seconds(2));
342 t1
.async_wait(bindns::bind(increment
, &count
));
344 t2
.expires_after(boost::asio::chrono::seconds(4));
350 BOOST_ASIO_CHECK(count
== 1);
353 #if defined(BOOST_ASIO_HAS_MOVE)
354 boost::asio::system_timer
make_timer(boost::asio::io_context
& ioc
, int* count
)
356 boost::asio::system_timer
t(ioc
);
357 t
.expires_after(boost::asio::chrono::seconds(1));
358 t
.async_wait(bindns::bind(increment
, count
));
363 void system_timer_move_test()
365 #if defined(BOOST_ASIO_HAS_MOVE)
366 boost::asio::io_context io_context1
;
367 boost::asio::io_context io_context2
;
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
);
378 BOOST_ASIO_CHECK(count
== 1);
382 BOOST_ASIO_CHECK(count
== 2);
383 #endif // defined(BOOST_ASIO_HAS_MOVE)
386 BOOST_ASIO_TEST_SUITE
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
)
393 BOOST_ASIO_TEST_CASE(system_timer_move_test
)
395 #else // defined(BOOST_ASIO_HAS_STD_CHRONO)
396 BOOST_ASIO_TEST_SUITE
399 BOOST_ASIO_TEST_CASE(null_test
)
401 #endif // defined(BOOST_ASIO_HAS_STD_CHRONO)