1//
2// deadline_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// 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
23#include <boost/bind.hpp>
24#include "archetypes/async_result.hpp"
25#include <boost/asio/io_service.hpp>
26#include <boost/asio/placeholders.hpp>
27#include <boost/asio/detail/thread.hpp>
28
29using namespace boost::posix_time;
30
31void increment(int* count)
32{
33 ++(*count);
34}
35
36void decrement_to_zero(boost::asio::deadline_timer* t, int* count)
37{
38 if (*count > 0)
39 {
40 --(*count);
41
42 int before_value = *count;
43
44 t->expires_at(expiry_time: t->expires_at() + seconds(1));
45 t->async_wait(handler: boost::bind(f: decrement_to_zero, a1: t, a2: count));
46
47 // Completion cannot nest, so count value should remain unchanged.
48 BOOST_ASIO_CHECK(*count == before_value);
49 }
50}
51
52void increment_if_not_cancelled(int* count,
53 const boost::system::error_code& ec)
54{
55 if (!ec)
56 ++(*count);
57}
58
59void cancel_timer(boost::asio::deadline_timer* t)
60{
61 std::size_t num_cancelled = t->cancel();
62 BOOST_ASIO_CHECK(num_cancelled == 1);
63}
64
65void cancel_one_timer(boost::asio::deadline_timer* t)
66{
67 std::size_t num_cancelled = t->cancel_one();
68 BOOST_ASIO_CHECK(num_cancelled == 1);
69}
70
71ptime now()
72{
73#if defined(BOOST_DATE_TIME_HAS_HIGH_PRECISION_CLOCK)
74 return microsec_clock::universal_time();
75#else // defined(BOOST_DATE_TIME_HAS_HIGH_PRECISION_CLOCK)
76 return second_clock::universal_time();
77#endif // defined(BOOST_DATE_TIME_HAS_HIGH_PRECISION_CLOCK)
78}
79
80void deadline_timer_test()
81{
82 boost::asio::io_service ios;
83 int count = 0;
84
85 ptime start = now();
86
87 boost::asio::deadline_timer t1(ios, seconds(1));
88 t1.wait();
89
90 // The timer must block until after its expiry time.
91 ptime end = now();
92 ptime expected_end = start + seconds(1);
93 BOOST_ASIO_CHECK(expected_end < end || expected_end == end);
94
95 start = now();
96
97 boost::asio::deadline_timer t2(ios, seconds(1) + microseconds(500000));
98 t2.wait();
99
100 // The timer must block until after its expiry time.
101 end = now();
102 expected_end = start + seconds(1) + microseconds(500000);
103 BOOST_ASIO_CHECK(expected_end < end || expected_end == end);
104
105 t2.expires_at(expiry_time: t2.expires_at() + seconds(1));
106 t2.wait();
107
108 // The timer must block until after its expiry time.
109 end = now();
110 expected_end += seconds(1);
111 BOOST_ASIO_CHECK(expected_end < end || expected_end == end);
112
113 start = now();
114
115 t2.expires_from_now(expiry_time: seconds(1) + microseconds(200000));
116 t2.wait();
117
118 // The timer must block until after its expiry time.
119 end = now();
120 expected_end = start + seconds(1) + microseconds(200000);
121 BOOST_ASIO_CHECK(expected_end < end || expected_end == end);
122
123 start = now();
124
125 boost::asio::deadline_timer t3(ios, seconds(5));
126 t3.async_wait(handler: boost::bind(f: increment, a1: &count));
127
128 // No completions can be delivered until run() is called.
129 BOOST_ASIO_CHECK(count == 0);
130
131 ios.run();
132
133 // The run() call will not return until all operations have finished, and
134 // this should not be until after the timer's expiry time.
135 BOOST_ASIO_CHECK(count == 1);
136 end = now();
137 expected_end = start + seconds(1);
138 BOOST_ASIO_CHECK(expected_end < end || expected_end == end);
139
140 count = 3;
141 start = now();
142
143 boost::asio::deadline_timer t4(ios, seconds(1));
144 t4.async_wait(handler: boost::bind(f: decrement_to_zero, a1: &t4, a2: &count));
145
146 // No completions can be delivered until run() is called.
147 BOOST_ASIO_CHECK(count == 3);
148
149 ios.reset();
150 ios.run();
151
152 // The run() call will not return until all operations have finished, and
153 // this should not be until after the timer's final expiry time.
154 BOOST_ASIO_CHECK(count == 0);
155 end = now();
156 expected_end = start + seconds(3);
157 BOOST_ASIO_CHECK(expected_end < end || expected_end == end);
158
159 count = 0;
160 start = now();
161
162 boost::asio::deadline_timer t5(ios, seconds(10));
163 t5.async_wait(handler: boost::bind(f: increment_if_not_cancelled, a1: &count,
164 a2: boost::asio::placeholders::error));
165 boost::asio::deadline_timer t6(ios, seconds(1));
166 t6.async_wait(handler: boost::bind(f: cancel_timer, a1: &t5));
167
168 // No completions can be delivered until run() is called.
169 BOOST_ASIO_CHECK(count == 0);
170
171 ios.reset();
172 ios.run();
173
174 // The timer should have been cancelled, so count should not have changed.
175 // The total run time should not have been much more than 1 second (and
176 // certainly far less than 10 seconds).
177 BOOST_ASIO_CHECK(count == 0);
178 end = now();
179 expected_end = start + seconds(2);
180 BOOST_ASIO_CHECK(end < expected_end);
181
182 // Wait on the timer again without cancelling it. This time the asynchronous
183 // wait should run to completion and increment the counter.
184 t5.async_wait(handler: boost::bind(f: increment_if_not_cancelled, a1: &count,
185 a2: boost::asio::placeholders::error));
186
187 ios.reset();
188 ios.run();
189
190 // The timer should not have been cancelled, so count should have changed.
191 // The total time since the timer was created should be more than 10 seconds.
192 BOOST_ASIO_CHECK(count == 1);
193 end = now();
194 expected_end = start + seconds(10);
195 BOOST_ASIO_CHECK(expected_end < end || expected_end == end);
196
197 count = 0;
198 start = now();
199
200 // Start two waits on a timer, one of which will be cancelled. The one
201 // which is not cancelled should still run to completion and increment the
202 // counter.
203 boost::asio::deadline_timer t7(ios, seconds(3));
204 t7.async_wait(handler: boost::bind(f: increment_if_not_cancelled, a1: &count,
205 a2: boost::asio::placeholders::error));
206 t7.async_wait(handler: boost::bind(f: increment_if_not_cancelled, a1: &count,
207 a2: boost::asio::placeholders::error));
208 boost::asio::deadline_timer t8(ios, seconds(1));
209 t8.async_wait(handler: boost::bind(f: cancel_one_timer, a1: &t7));
210
211 ios.reset();
212 ios.run();
213
214 // One of the waits should not have been cancelled, so count should have
215 // changed. The total time since the timer was created should be more than 3
216 // seconds.
217 BOOST_ASIO_CHECK(count == 1);
218 end = now();
219 expected_end = start + seconds(3);
220 BOOST_ASIO_CHECK(expected_end < end || expected_end == end);
221}
222
223void timer_handler(const boost::system::error_code&)
224{
225}
226
227void deadline_timer_cancel_test()
228{
229 static boost::asio::io_service io_service;
230 struct timer
231 {
232 boost::asio::deadline_timer t;
233 timer() : t(io_service) { t.expires_at(expiry_time: boost::posix_time::pos_infin); }
234 } timers[50];
235
236 timers[2].t.async_wait(handler: &timer_handler);
237 timers[41].t.async_wait(handler: &timer_handler);
238 for (int i = 10; i < 20; ++i)
239 timers[i].t.async_wait(handler: &timer_handler);
240
241 BOOST_ASIO_CHECK(timers[2].t.cancel() == 1);
242 BOOST_ASIO_CHECK(timers[41].t.cancel() == 1);
243 for (int i = 10; i < 20; ++i)
244 BOOST_ASIO_CHECK(timers[i].t.cancel() == 1);
245}
246
247struct custom_allocation_timer_handler
248{
249 custom_allocation_timer_handler(int* count) : count_(count) {}
250 void operator()(const boost::system::error_code&) {}
251 int* count_;
252};
253
254void* asio_handler_allocate(std::size_t size,
255 custom_allocation_timer_handler* handler)
256{
257 ++(*handler->count_);
258 return ::operator new(size);
259}
260
261void asio_handler_deallocate(void* pointer, std::size_t,
262 custom_allocation_timer_handler* handler)
263{
264 --(*handler->count_);
265 ::operator delete(pointer);
266}
267
268void deadline_timer_custom_allocation_test()
269{
270 static boost::asio::io_service io_service;
271 struct timer
272 {
273 boost::asio::deadline_timer t;
274 timer() : t(io_service) {}
275 } timers[100];
276
277 int allocation_count = 0;
278
279 for (int i = 0; i < 50; ++i)
280 {
281 timers[i].t.expires_at(expiry_time: boost::posix_time::pos_infin);
282 timers[i].t.async_wait(handler: custom_allocation_timer_handler(&allocation_count));
283 }
284
285 for (int i = 50; i < 100; ++i)
286 {
287 timers[i].t.expires_at(expiry_time: boost::posix_time::neg_infin);
288 timers[i].t.async_wait(handler: custom_allocation_timer_handler(&allocation_count));
289 }
290
291 for (int i = 0; i < 50; ++i)
292 timers[i].t.cancel();
293
294 io_service.run();
295
296 BOOST_ASIO_CHECK(allocation_count == 0);
297}
298
299void io_service_run(boost::asio::io_service* ios)
300{
301 ios->run();
302}
303
304void deadline_timer_thread_test()
305{
306 boost::asio::io_service ios;
307 boost::asio::io_service::work w(ios);
308 boost::asio::deadline_timer t1(ios);
309 boost::asio::deadline_timer t2(ios);
310 int count = 0;
311
312 boost::asio::detail::thread th(boost::bind(f: io_service_run, a1: &ios));
313
314 t2.expires_from_now(expiry_time: boost::posix_time::seconds(2));
315 t2.wait();
316
317 t1.expires_from_now(expiry_time: boost::posix_time::seconds(2));
318 t1.async_wait(handler: boost::bind(f: increment, a1: &count));
319
320 t2.expires_from_now(expiry_time: boost::posix_time::seconds(4));
321 t2.wait();
322
323 ios.stop();
324 th.join();
325
326 BOOST_ASIO_CHECK(count == 1);
327}
328
329void deadline_timer_async_result_test()
330{
331 boost::asio::io_service ios;
332 boost::asio::deadline_timer t1(ios);
333
334 t1.expires_from_now(expiry_time: boost::posix_time::seconds(1));
335 int i = t1.async_wait(handler: archetypes::lazy_handler());
336 BOOST_ASIO_CHECK(i == 42);
337
338 ios.run();
339}
340
341BOOST_ASIO_TEST_SUITE
342(
343 "deadline_timer",
344 BOOST_ASIO_TEST_CASE(deadline_timer_test)
345 BOOST_ASIO_TEST_CASE(deadline_timer_cancel_test)
346 BOOST_ASIO_TEST_CASE(deadline_timer_custom_allocation_test)
347 BOOST_ASIO_TEST_CASE(deadline_timer_thread_test)
348 BOOST_ASIO_TEST_CASE(deadline_timer_async_result_test)
349)
350#else // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
351BOOST_ASIO_TEST_SUITE
352(
353 "deadline_timer",
354 BOOST_ASIO_TEST_CASE(null_test)
355)
356#endif // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
357

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