• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // deadline_timer.cpp
3 // ~~~~~~~~~~~~~~~~~~
4 //
5 // Copyright (c) 2003-2021 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/bind.hpp>
24 #include "archetypes/async_result.hpp"
25 #include <boost/asio/executor_work_guard.hpp>
26 #include <boost/asio/io_context.hpp>
27 #include <boost/asio/placeholders.hpp>
28 #include <boost/asio/detail/thread.hpp>
29 
30 using namespace boost::posix_time;
31 
increment(int * count)32 void increment(int* count)
33 {
34   ++(*count);
35 }
36 
decrement_to_zero(boost::asio::deadline_timer * t,int * count)37 void decrement_to_zero(boost::asio::deadline_timer* t, int* count)
38 {
39   if (*count > 0)
40   {
41     --(*count);
42 
43     int before_value = *count;
44 
45     t->expires_at(t->expires_at() + seconds(1));
46     t->async_wait(boost::bind(decrement_to_zero, t, count));
47 
48     // Completion cannot nest, so count value should remain unchanged.
49     BOOST_ASIO_CHECK(*count == before_value);
50   }
51 }
52 
increment_if_not_cancelled(int * count,const boost::system::error_code & ec)53 void increment_if_not_cancelled(int* count,
54     const boost::system::error_code& ec)
55 {
56   if (!ec)
57     ++(*count);
58 }
59 
cancel_timer(boost::asio::deadline_timer * t)60 void cancel_timer(boost::asio::deadline_timer* t)
61 {
62   std::size_t num_cancelled = t->cancel();
63   BOOST_ASIO_CHECK(num_cancelled == 1);
64 }
65 
cancel_one_timer(boost::asio::deadline_timer * t)66 void cancel_one_timer(boost::asio::deadline_timer* t)
67 {
68   std::size_t num_cancelled = t->cancel_one();
69   BOOST_ASIO_CHECK(num_cancelled == 1);
70 }
71 
now()72 ptime now()
73 {
74 #if defined(BOOST_DATE_TIME_HAS_HIGH_PRECISION_CLOCK)
75   return microsec_clock::universal_time();
76 #else // defined(BOOST_DATE_TIME_HAS_HIGH_PRECISION_CLOCK)
77   return second_clock::universal_time();
78 #endif // defined(BOOST_DATE_TIME_HAS_HIGH_PRECISION_CLOCK)
79 }
80 
deadline_timer_test()81 void deadline_timer_test()
82 {
83   boost::asio::io_context ioc;
84   int count = 0;
85 
86   ptime start = now();
87 
88   boost::asio::deadline_timer t1(ioc, seconds(1));
89   t1.wait();
90 
91   // The timer must block until after its expiry time.
92   ptime end = now();
93   ptime expected_end = start + seconds(1);
94   BOOST_ASIO_CHECK(expected_end < end || expected_end == end);
95 
96   start = now();
97 
98   boost::asio::deadline_timer t2(ioc, seconds(1) + microseconds(500000));
99   t2.wait();
100 
101   // The timer must block until after its expiry time.
102   end = now();
103   expected_end = start + seconds(1) + microseconds(500000);
104   BOOST_ASIO_CHECK(expected_end < end || expected_end == end);
105 
106   t2.expires_at(t2.expires_at() + seconds(1));
107   t2.wait();
108 
109   // The timer must block until after its expiry time.
110   end = now();
111   expected_end += seconds(1);
112   BOOST_ASIO_CHECK(expected_end < end || expected_end == end);
113 
114   start = now();
115 
116   t2.expires_from_now(seconds(1) + microseconds(200000));
117   t2.wait();
118 
119   // The timer must block until after its expiry time.
120   end = now();
121   expected_end = start + seconds(1) + microseconds(200000);
122   BOOST_ASIO_CHECK(expected_end < end || expected_end == end);
123 
124   start = now();
125 
126   boost::asio::deadline_timer t3(ioc, seconds(5));
127   t3.async_wait(boost::bind(increment, &count));
128 
129   // No completions can be delivered until run() is called.
130   BOOST_ASIO_CHECK(count == 0);
131 
132   ioc.run();
133 
134   // The run() call will not return until all operations have finished, and
135   // this should not be until after the timer's expiry time.
136   BOOST_ASIO_CHECK(count == 1);
137   end = now();
138   expected_end = start + seconds(1);
139   BOOST_ASIO_CHECK(expected_end < end || expected_end == end);
140 
141   count = 3;
142   start = now();
143 
144   boost::asio::deadline_timer t4(ioc, seconds(1));
145   t4.async_wait(boost::bind(decrement_to_zero, &t4, &count));
146 
147   // No completions can be delivered until run() is called.
148   BOOST_ASIO_CHECK(count == 3);
149 
150   ioc.restart();
151   ioc.run();
152 
153   // The run() call will not return until all operations have finished, and
154   // this should not be until after the timer's final expiry time.
155   BOOST_ASIO_CHECK(count == 0);
156   end = now();
157   expected_end = start + seconds(3);
158   BOOST_ASIO_CHECK(expected_end < end || expected_end == end);
159 
160   count = 0;
161   start = now();
162 
163   boost::asio::deadline_timer t5(ioc, seconds(10));
164   t5.async_wait(boost::bind(increment_if_not_cancelled, &count,
165         boost::asio::placeholders::error));
166   boost::asio::deadline_timer t6(ioc, seconds(1));
167   t6.async_wait(boost::bind(cancel_timer, &t5));
168 
169   // No completions can be delivered until run() is called.
170   BOOST_ASIO_CHECK(count == 0);
171 
172   ioc.restart();
173   ioc.run();
174 
175   // The timer should have been cancelled, so count should not have changed.
176   // The total run time should not have been much more than 1 second (and
177   // certainly far less than 10 seconds).
178   BOOST_ASIO_CHECK(count == 0);
179   end = now();
180   expected_end = start + seconds(2);
181   BOOST_ASIO_CHECK(end < expected_end);
182 
183   // Wait on the timer again without cancelling it. This time the asynchronous
184   // wait should run to completion and increment the counter.
185   t5.async_wait(boost::bind(increment_if_not_cancelled, &count,
186         boost::asio::placeholders::error));
187 
188   ioc.restart();
189   ioc.run();
190 
191   // The timer should not have been cancelled, so count should have changed.
192   // The total time since the timer was created should be more than 10 seconds.
193   BOOST_ASIO_CHECK(count == 1);
194   end = now();
195   expected_end = start + seconds(10);
196   BOOST_ASIO_CHECK(expected_end < end || expected_end == end);
197 
198   count = 0;
199   start = now();
200 
201   // Start two waits on a timer, one of which will be cancelled. The one
202   // which is not cancelled should still run to completion and increment the
203   // counter.
204   boost::asio::deadline_timer t7(ioc, seconds(3));
205   t7.async_wait(boost::bind(increment_if_not_cancelled, &count,
206         boost::asio::placeholders::error));
207   t7.async_wait(boost::bind(increment_if_not_cancelled, &count,
208         boost::asio::placeholders::error));
209   boost::asio::deadline_timer t8(ioc, seconds(1));
210   t8.async_wait(boost::bind(cancel_one_timer, &t7));
211 
212   ioc.restart();
213   ioc.run();
214 
215   // One of the waits should not have been cancelled, so count should have
216   // changed. The total time since the timer was created should be more than 3
217   // seconds.
218   BOOST_ASIO_CHECK(count == 1);
219   end = now();
220   expected_end = start + seconds(3);
221   BOOST_ASIO_CHECK(expected_end < end || expected_end == end);
222 }
223 
timer_handler(const boost::system::error_code &)224 void timer_handler(const boost::system::error_code&)
225 {
226 }
227 
deadline_timer_cancel_test()228 void deadline_timer_cancel_test()
229 {
230   static boost::asio::io_context io_context;
231   struct timer
232   {
233     boost::asio::deadline_timer t;
234     timer() : t(io_context) { t.expires_at(boost::posix_time::pos_infin); }
235   } timers[50];
236 
237   timers[2].t.async_wait(&timer_handler);
238   timers[41].t.async_wait(&timer_handler);
239   for (int i = 10; i < 20; ++i)
240     timers[i].t.async_wait(&timer_handler);
241 
242   BOOST_ASIO_CHECK(timers[2].t.cancel() == 1);
243   BOOST_ASIO_CHECK(timers[41].t.cancel() == 1);
244   for (int i = 10; i < 20; ++i)
245     BOOST_ASIO_CHECK(timers[i].t.cancel() == 1);
246 }
247 
248 struct custom_allocation_timer_handler
249 {
custom_allocation_timer_handlercustom_allocation_timer_handler250   custom_allocation_timer_handler(int* count) : count_(count) {}
operator ()custom_allocation_timer_handler251   void operator()(const boost::system::error_code&) {}
252   int* count_;
253 
254   template <typename T>
255   struct allocator
256   {
257     typedef size_t size_type;
258     typedef ptrdiff_t difference_type;
259     typedef T* pointer;
260     typedef const T* const_pointer;
261     typedef T& reference;
262     typedef const T& const_reference;
263     typedef T value_type;
264 
265     template <typename U>
266     struct rebind
267     {
268       typedef allocator<U> other;
269     };
270 
allocatorcustom_allocation_timer_handler::allocator271     explicit allocator(int* count) BOOST_ASIO_NOEXCEPT
272       : count_(count)
273     {
274     }
275 
allocatorcustom_allocation_timer_handler::allocator276     allocator(const allocator& other) BOOST_ASIO_NOEXCEPT
277       : count_(other.count_)
278     {
279     }
280 
281     template <typename U>
allocatorcustom_allocation_timer_handler::allocator282     allocator(const allocator<U>& other) BOOST_ASIO_NOEXCEPT
283       : count_(other.count_)
284     {
285     }
286 
allocatecustom_allocation_timer_handler::allocator287     pointer allocate(size_type n, const void* = 0)
288     {
289       ++(*count_);
290       return static_cast<T*>(::operator new(sizeof(T) * n));
291     }
292 
deallocatecustom_allocation_timer_handler::allocator293     void deallocate(pointer p, size_type)
294     {
295       --(*count_);
296       ::operator delete(p);
297     }
298 
max_sizecustom_allocation_timer_handler::allocator299     size_type max_size() const
300     {
301       return ~size_type(0);
302     }
303 
constructcustom_allocation_timer_handler::allocator304     void construct(pointer p, const T& v)
305     {
306       new (p) T(v);
307     }
308 
destroycustom_allocation_timer_handler::allocator309     void destroy(pointer p)
310     {
311       p->~T();
312     }
313 
314     int* count_;
315   };
316 
317   typedef allocator<int> allocator_type;
318 
get_allocatorcustom_allocation_timer_handler319   allocator_type get_allocator() const BOOST_ASIO_NOEXCEPT
320   {
321     return allocator_type(count_);
322   }
323 };
324 
deadline_timer_custom_allocation_test()325 void deadline_timer_custom_allocation_test()
326 {
327   static boost::asio::io_context io_context;
328   struct timer
329   {
330     boost::asio::deadline_timer t;
331     timer() : t(io_context) {}
332   } timers[100];
333 
334   int allocation_count = 0;
335 
336   for (int i = 0; i < 50; ++i)
337   {
338     timers[i].t.expires_at(boost::posix_time::pos_infin);
339     timers[i].t.async_wait(custom_allocation_timer_handler(&allocation_count));
340   }
341 
342   for (int i = 50; i < 100; ++i)
343   {
344     timers[i].t.expires_at(boost::posix_time::neg_infin);
345     timers[i].t.async_wait(custom_allocation_timer_handler(&allocation_count));
346   }
347 
348   for (int i = 0; i < 50; ++i)
349     timers[i].t.cancel();
350 
351   io_context.run();
352 
353   BOOST_ASIO_CHECK(allocation_count == 0);
354 }
355 
io_context_run(boost::asio::io_context * ioc)356 void io_context_run(boost::asio::io_context* ioc)
357 {
358   ioc->run();
359 }
360 
deadline_timer_thread_test()361 void deadline_timer_thread_test()
362 {
363   boost::asio::io_context ioc;
364   boost::asio::executor_work_guard<boost::asio::io_context::executor_type> work
365     = boost::asio::make_work_guard(ioc);
366   boost::asio::deadline_timer t1(ioc);
367   boost::asio::deadline_timer t2(ioc);
368   int count = 0;
369 
370   boost::asio::detail::thread th(boost::bind(io_context_run, &ioc));
371 
372   t2.expires_from_now(boost::posix_time::seconds(2));
373   t2.wait();
374 
375   t1.expires_from_now(boost::posix_time::seconds(2));
376   t1.async_wait(boost::bind(increment, &count));
377 
378   t2.expires_from_now(boost::posix_time::seconds(4));
379   t2.wait();
380 
381   ioc.stop();
382   th.join();
383 
384   BOOST_ASIO_CHECK(count == 1);
385 }
386 
deadline_timer_async_result_test()387 void deadline_timer_async_result_test()
388 {
389   boost::asio::io_context ioc;
390   boost::asio::deadline_timer t1(ioc);
391 
392   t1.expires_from_now(boost::posix_time::seconds(1));
393   int i = t1.async_wait(archetypes::lazy_handler());
394   BOOST_ASIO_CHECK(i == 42);
395 
396   ioc.run();
397 }
398 
399 #if defined(BOOST_ASIO_HAS_MOVE)
make_timer(boost::asio::io_context & ioc,int * count)400 boost::asio::deadline_timer make_timer(boost::asio::io_context& ioc, int* count)
401 {
402   boost::asio::deadline_timer t(ioc);
403   t.expires_from_now(boost::posix_time::seconds(1));
404   t.async_wait(boost::bind(increment, count));
405   return t;
406 }
407 #endif // defined(BOOST_ASIO_HAS_MOVE)
408 
deadline_timer_move_test()409 void deadline_timer_move_test()
410 {
411 #if defined(BOOST_ASIO_HAS_MOVE)
412   boost::asio::io_context io_context1;
413   boost::asio::io_context io_context2;
414   int count = 0;
415 
416   boost::asio::deadline_timer t1 = make_timer(io_context1, &count);
417   boost::asio::deadline_timer t2 = make_timer(io_context2, &count);
418   boost::asio::deadline_timer t3 = std::move(t1);
419 
420   t2 = std::move(t1);
421 
422   io_context2.run();
423 
424   BOOST_ASIO_CHECK(count == 1);
425 
426   io_context1.run();
427 
428   BOOST_ASIO_CHECK(count == 2);
429 #endif // defined(BOOST_ASIO_HAS_MOVE)
430 }
431 
432 BOOST_ASIO_TEST_SUITE
433 (
434   "deadline_timer",
435   BOOST_ASIO_TEST_CASE(deadline_timer_test)
436   BOOST_ASIO_TEST_CASE(deadline_timer_cancel_test)
437   BOOST_ASIO_TEST_CASE(deadline_timer_custom_allocation_test)
438   BOOST_ASIO_TEST_CASE(deadline_timer_thread_test)
439   BOOST_ASIO_TEST_CASE(deadline_timer_async_result_test)
440   BOOST_ASIO_TEST_CASE(deadline_timer_move_test)
441 )
442 #else // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
443 BOOST_ASIO_TEST_SUITE
444 (
445   "deadline_timer",
446   BOOST_ASIO_TEST_CASE(null_test)
447 )
448 #endif // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
449