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)
38 namespace bindns = boost;
39 #else // defined(BOOST_ASIO_HAS_BOOST_BIND)
40 namespace bindns = std;
41 #endif // defined(BOOST_ASIO_HAS_BOOST_BIND)
42 
43 namespace chronons = std::chrono;
44 
increment(int * count)45 void increment(int* count)
46 {
47   ++(*count);
48 }
49 
decrement_to_zero(boost::asio::system_timer * t,int * count)50 void 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(t->expires_at() + chronons::seconds(1));
59     t->async_wait(bindns::bind(decrement_to_zero, t, count));
60 
61     // Completion cannot nest, so count value should remain unchanged.
62     BOOST_ASIO_CHECK(*count == before_value);
63   }
64 }
65 
increment_if_not_cancelled(int * count,const boost::system::error_code & ec)66 void increment_if_not_cancelled(int* count,
67     const boost::system::error_code& ec)
68 {
69   if (!ec)
70     ++(*count);
71 }
72 
cancel_timer(boost::asio::system_timer * t)73 void 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 
cancel_one_timer(boost::asio::system_timer * t)79 void 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 
now()85 boost::asio::system_timer::time_point now()
86 {
87   return boost::asio::system_timer::clock_type::now();
88 }
89 
system_timer_test()90 void 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(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(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(bindns::bind(increment, &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(bindns::bind(decrement_to_zero, &t4, &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(bindns::bind(increment_if_not_cancelled, &count, _1));
181   boost::asio::system_timer t6(ios, seconds(1));
182   t6.async_wait(bindns::bind(cancel_timer, &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(bindns::bind(increment_if_not_cancelled, &count, _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(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(ios, seconds(1));
222   t8.async_wait(bindns::bind(cancel_one_timer, &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 
timer_handler(const boost::system::error_code &)236 void timer_handler(const boost::system::error_code&)
237 {
238 }
239 
system_timer_cancel_test()240 void 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((boost::asio::system_timer::time_point::max)());
249     }
250   } timers[50];
251 
252   timers[2].t.async_wait(&timer_handler);
253   timers[41].t.async_wait(&timer_handler);
254   for (int i = 10; i < 20; ++i)
255     timers[i].t.async_wait(&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 
263 struct custom_allocation_timer_handler
264 {
custom_allocation_timer_handlercustom_allocation_timer_handler265   custom_allocation_timer_handler(int* count) : count_(count) {}
operator ()custom_allocation_timer_handler266   void operator()(const boost::system::error_code&) {}
267   int* count_;
268 };
269 
asio_handler_allocate(std::size_t size,custom_allocation_timer_handler * handler)270 void* asio_handler_allocate(std::size_t size,
271     custom_allocation_timer_handler* handler)
272 {
273   ++(*handler->count_);
274   return ::operator new(size);
275 }
276 
asio_handler_deallocate(void * pointer,std::size_t,custom_allocation_timer_handler * handler)277 void asio_handler_deallocate(void* pointer, std::size_t,
278     custom_allocation_timer_handler* handler)
279 {
280   --(*handler->count_);
281   ::operator delete(pointer);
282 }
283 
system_timer_custom_allocation_test()284 void 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((boost::asio::system_timer::time_point::max)());
298     timers[i].t.async_wait(custom_allocation_timer_handler(&allocation_count));
299   }
300 
301   for (int i = 50; i < 100; ++i)
302   {
303     timers[i].t.expires_at((boost::asio::system_timer::time_point::min)());
304     timers[i].t.async_wait(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 
io_service_run(boost::asio::io_service * ios)315 void io_service_run(boost::asio::io_service* ios)
316 {
317   ios->run();
318 }
319 
system_timer_thread_test()320 void 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(io_service_run, &ios));
329 
330   t2.expires_from_now(chronons::seconds(2));
331   t2.wait();
332 
333   t1.expires_from_now(chronons::seconds(2));
334   t1.async_wait(bindns::bind(increment, &count));
335 
336   t2.expires_from_now(chronons::seconds(4));
337   t2.wait();
338 
339   ios.stop();
340   th.join();
341 
342   BOOST_ASIO_CHECK(count == 1);
343 }
344 
345 BOOST_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)
354 BOOST_ASIO_TEST_SUITE
355 (
356   "system_timer",
357   BOOST_ASIO_TEST_CASE(null_test)
358 )
359 #endif // defined(BOOST_ASIO_HAS_STD_CHRONO)
360