system_timer.cpp 11 KB

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