1//
2// system_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// 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
28#include <boost/asio/io_service.hpp>
29#include <boost/asio/detail/thread.hpp>
30
31#if defined(BOOST_ASIO_HAS_BOOST_BIND)
32# include <boost/bind.hpp>
33#else // defined(BOOST_ASIO_HAS_BOOST_BIND)
34# include <functional>
35#endif // defined(BOOST_ASIO_HAS_BOOST_BIND)
36
37#if defined(BOOST_ASIO_HAS_BOOST_BIND)
38namespace bindns = boost;
39#else // defined(BOOST_ASIO_HAS_BOOST_BIND)
40namespace bindns = std;
41#endif // defined(BOOST_ASIO_HAS_BOOST_BIND)
42
43namespace chronons = std::chrono;
44
45void increment(int* count)
46{
47 ++(*count);
48}
49
50void decrement_to_zero(boost::asio::system_timer* t, int* count)
51{
52 if (*count > 0)
53 {
54 --(*count);
55
56 int before_value = *count;
57
58 t->expires_at(expiry_time: t->expires_at() + chronons::seconds(1));
59 t->async_wait(handler: bindns::bind(f: decrement_to_zero, a1: t, a2: count));
60
61 // Completion cannot nest, so count value should remain unchanged.
62 BOOST_ASIO_CHECK(*count == before_value);
63 }
64}
65
66void increment_if_not_cancelled(int* count,
67 const boost::system::error_code& ec)
68{
69 if (!ec)
70 ++(*count);
71}
72
73void cancel_timer(boost::asio::system_timer* t)
74{
75 std::size_t num_cancelled = t->cancel();
76 BOOST_ASIO_CHECK(num_cancelled == 1);
77}
78
79void cancel_one_timer(boost::asio::system_timer* t)
80{
81 std::size_t num_cancelled = t->cancel_one();
82 BOOST_ASIO_CHECK(num_cancelled == 1);
83}
84
85boost::asio::system_timer::time_point now()
86{
87 return boost::asio::system_timer::clock_type::now();
88}
89
90void system_timer_test()
91{
92 using chronons::seconds;
93 using chronons::microseconds;
94#if !defined(BOOST_ASIO_HAS_BOOST_BIND)
95 using std::placeholders::_1;
96 using std::placeholders::_2;
97#endif // !defined(BOOST_ASIO_HAS_BOOST_BIND)
98
99 boost::asio::io_service ios;
100 int count = 0;
101
102 boost::asio::system_timer::time_point start = now();
103
104 boost::asio::system_timer t1(ios, seconds(1));
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
114 boost::asio::system_timer t2(ios, seconds(1) + microseconds(500000));
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
122 t2.expires_at(expiry_time: t2.expires_at() + seconds(1));
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
132 t2.expires_from_now(expiry_time: seconds(1) + microseconds(200000));
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
142 boost::asio::system_timer t3(ios, seconds(5));
143 t3.async_wait(handler: bindns::bind(f: increment, a1: &count));
144
145 // No completions can be delivered until run() is called.
146 BOOST_ASIO_CHECK(count == 0);
147
148 ios.run();
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
160 boost::asio::system_timer t4(ios, seconds(1));
161 t4.async_wait(handler: bindns::bind(f: decrement_to_zero, a1: &t4, a2: &count));
162
163 // No completions can be delivered until run() is called.
164 BOOST_ASIO_CHECK(count == 3);
165
166 ios.reset();
167 ios.run();
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
179 boost::asio::system_timer t5(ios, seconds(10));
180 t5.async_wait(handler: bindns::bind(f: increment_if_not_cancelled, a1: &count, a2: _1));
181 boost::asio::system_timer t6(ios, seconds(1));
182 t6.async_wait(handler: bindns::bind(f: cancel_timer, a1: &t5));
183
184 // No completions can be delivered until run() is called.
185 BOOST_ASIO_CHECK(count == 0);
186
187 ios.reset();
188 ios.run();
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(handler: bindns::bind(f: increment_if_not_cancelled, a1: &count, a2: _1));
201
202 ios.reset();
203 ios.run();
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.
218 boost::asio::system_timer t7(ios, seconds(3));
219 t7.async_wait(handler: bindns::bind(f: increment_if_not_cancelled, a1: &count, a2: _1));
220 t7.async_wait(handler: bindns::bind(f: increment_if_not_cancelled, a1: &count, a2: _1));
221 boost::asio::system_timer t8(ios, seconds(1));
222 t8.async_wait(handler: bindns::bind(f: cancel_one_timer, a1: &t7));
223
224 ios.reset();
225 ios.run();
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
236void timer_handler(const boost::system::error_code&)
237{
238}
239
240void system_timer_cancel_test()
241{
242 static boost::asio::io_service io_service;
243 struct timer
244 {
245 boost::asio::system_timer t;
246 timer() : t(io_service)
247 {
248 t.expires_at(expiry_time: (boost::asio::system_timer::time_point::max)());
249 }
250 } timers[50];
251
252 timers[2].t.async_wait(handler: &timer_handler);
253 timers[41].t.async_wait(handler: &timer_handler);
254 for (int i = 10; i < 20; ++i)
255 timers[i].t.async_wait(handler: &timer_handler);
256
257 BOOST_ASIO_CHECK(timers[2].t.cancel() == 1);
258 BOOST_ASIO_CHECK(timers[41].t.cancel() == 1);
259 for (int i = 10; i < 20; ++i)
260 BOOST_ASIO_CHECK(timers[i].t.cancel() == 1);
261}
262
263struct custom_allocation_timer_handler
264{
265 custom_allocation_timer_handler(int* count) : count_(count) {}
266 void operator()(const boost::system::error_code&) {}
267 int* count_;
268};
269
270void* asio_handler_allocate(std::size_t size,
271 custom_allocation_timer_handler* handler)
272{
273 ++(*handler->count_);
274 return ::operator new(size);
275}
276
277void asio_handler_deallocate(void* pointer, std::size_t,
278 custom_allocation_timer_handler* handler)
279{
280 --(*handler->count_);
281 ::operator delete(pointer);
282}
283
284void system_timer_custom_allocation_test()
285{
286 static boost::asio::io_service io_service;
287 struct timer
288 {
289 boost::asio::system_timer t;
290 timer() : t(io_service) {}
291 } timers[100];
292
293 int allocation_count = 0;
294
295 for (int i = 0; i < 50; ++i)
296 {
297 timers[i].t.expires_at(expiry_time: (boost::asio::system_timer::time_point::max)());
298 timers[i].t.async_wait(handler: custom_allocation_timer_handler(&allocation_count));
299 }
300
301 for (int i = 50; i < 100; ++i)
302 {
303 timers[i].t.expires_at(expiry_time: (boost::asio::system_timer::time_point::min)());
304 timers[i].t.async_wait(handler: custom_allocation_timer_handler(&allocation_count));
305 }
306
307 for (int i = 0; i < 50; ++i)
308 timers[i].t.cancel();
309
310 io_service.run();
311
312 BOOST_ASIO_CHECK(allocation_count == 0);
313}
314
315void io_service_run(boost::asio::io_service* ios)
316{
317 ios->run();
318}
319
320void system_timer_thread_test()
321{
322 boost::asio::io_service ios;
323 boost::asio::io_service::work w(ios);
324 boost::asio::system_timer t1(ios);
325 boost::asio::system_timer t2(ios);
326 int count = 0;
327
328 boost::asio::detail::thread th(bindns::bind(f: io_service_run, a1: &ios));
329
330 t2.expires_from_now(expiry_time: chronons::seconds(2));
331 t2.wait();
332
333 t1.expires_from_now(expiry_time: chronons::seconds(2));
334 t1.async_wait(handler: bindns::bind(f: increment, a1: &count));
335
336 t2.expires_from_now(expiry_time: chronons::seconds(4));
337 t2.wait();
338
339 ios.stop();
340 th.join();
341
342 BOOST_ASIO_CHECK(count == 1);
343}
344
345BOOST_ASIO_TEST_SUITE
346(
347 "system_timer",
348 BOOST_ASIO_TEST_CASE(system_timer_test)
349 BOOST_ASIO_TEST_CASE(system_timer_cancel_test)
350 BOOST_ASIO_TEST_CASE(system_timer_custom_allocation_test)
351 BOOST_ASIO_TEST_CASE(system_timer_thread_test)
352)
353#else // defined(BOOST_ASIO_HAS_STD_CHRONO)
354BOOST_ASIO_TEST_SUITE
355(
356 "system_timer",
357 BOOST_ASIO_TEST_CASE(null_test)
358)
359#endif // defined(BOOST_ASIO_HAS_STD_CHRONO)
360

source code of boost/libs/asio/test/system_timer.cpp