]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // |
2 | // deadline_timer.cpp | |
3 | // ~~~~~~~~~~~~~~~~~~ | |
4 | // | |
5 | // Copyright (c) 2003-2016 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 | ||
29 | using namespace boost::posix_time; | |
30 | ||
31 | void increment(int* count) | |
32 | { | |
33 | ++(*count); | |
34 | } | |
35 | ||
36 | void 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(t->expires_at() + seconds(1)); | |
45 | t->async_wait(boost::bind(decrement_to_zero, t, count)); | |
46 | ||
47 | // Completion cannot nest, so count value should remain unchanged. | |
48 | BOOST_ASIO_CHECK(*count == before_value); | |
49 | } | |
50 | } | |
51 | ||
52 | void increment_if_not_cancelled(int* count, | |
53 | const boost::system::error_code& ec) | |
54 | { | |
55 | if (!ec) | |
56 | ++(*count); | |
57 | } | |
58 | ||
59 | void 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 | ||
65 | void 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 | ||
71 | ptime 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 | ||
80 | void 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(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(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(boost::bind(increment, &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(boost::bind(decrement_to_zero, &t4, &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(boost::bind(increment_if_not_cancelled, &count, | |
164 | boost::asio::placeholders::error)); | |
165 | boost::asio::deadline_timer t6(ios, seconds(1)); | |
166 | t6.async_wait(boost::bind(cancel_timer, &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(boost::bind(increment_if_not_cancelled, &count, | |
185 | 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(boost::bind(increment_if_not_cancelled, &count, | |
205 | boost::asio::placeholders::error)); | |
206 | t7.async_wait(boost::bind(increment_if_not_cancelled, &count, | |
207 | boost::asio::placeholders::error)); | |
208 | boost::asio::deadline_timer t8(ios, seconds(1)); | |
209 | t8.async_wait(boost::bind(cancel_one_timer, &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 | ||
223 | void timer_handler(const boost::system::error_code&) | |
224 | { | |
225 | } | |
226 | ||
227 | void 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(boost::posix_time::pos_infin); } | |
234 | } timers[50]; | |
235 | ||
236 | timers[2].t.async_wait(&timer_handler); | |
237 | timers[41].t.async_wait(&timer_handler); | |
238 | for (int i = 10; i < 20; ++i) | |
239 | timers[i].t.async_wait(&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 | ||
247 | struct 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 | ||
254 | void* asio_handler_allocate(std::size_t size, | |
255 | custom_allocation_timer_handler* handler) | |
256 | { | |
257 | ++(*handler->count_); | |
258 | return ::operator new(size); | |
259 | } | |
260 | ||
261 | void asio_handler_deallocate(void* pointer, std::size_t, | |
262 | custom_allocation_timer_handler* handler) | |
263 | { | |
264 | --(*handler->count_); | |
265 | ::operator delete(pointer); | |
266 | } | |
267 | ||
268 | void 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(boost::posix_time::pos_infin); | |
282 | timers[i].t.async_wait(custom_allocation_timer_handler(&allocation_count)); | |
283 | } | |
284 | ||
285 | for (int i = 50; i < 100; ++i) | |
286 | { | |
287 | timers[i].t.expires_at(boost::posix_time::neg_infin); | |
288 | timers[i].t.async_wait(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 | ||
299 | void io_service_run(boost::asio::io_service* ios) | |
300 | { | |
301 | ios->run(); | |
302 | } | |
303 | ||
304 | void 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(io_service_run, &ios)); | |
313 | ||
314 | t2.expires_from_now(boost::posix_time::seconds(2)); | |
315 | t2.wait(); | |
316 | ||
317 | t1.expires_from_now(boost::posix_time::seconds(2)); | |
318 | t1.async_wait(boost::bind(increment, &count)); | |
319 | ||
320 | t2.expires_from_now(boost::posix_time::seconds(4)); | |
321 | t2.wait(); | |
322 | ||
323 | ios.stop(); | |
324 | th.join(); | |
325 | ||
326 | BOOST_ASIO_CHECK(count == 1); | |
327 | } | |
328 | ||
329 | void 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(boost::posix_time::seconds(1)); | |
335 | int i = t1.async_wait(archetypes::lazy_handler()); | |
336 | BOOST_ASIO_CHECK(i == 42); | |
337 | ||
338 | ios.run(); | |
339 | } | |
340 | ||
341 | BOOST_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) | |
351 | BOOST_ASIO_TEST_SUITE | |
352 | ( | |
353 | "deadline_timer", | |
354 | BOOST_ASIO_TEST_CASE(null_test) | |
355 | ) | |
356 | #endif // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME) |