deadline_timer.cpp 10 KB

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