io_context.cpp 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. //
  2. // io_context.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/io_context.hpp>
  16. #include <sstream>
  17. #include <boost/asio/bind_executor.hpp>
  18. #include <boost/asio/dispatch.hpp>
  19. #include <boost/asio/post.hpp>
  20. #include <boost/asio/detail/thread.hpp>
  21. #include "unit_test.hpp"
  22. #if defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
  23. # include <boost/asio/deadline_timer.hpp>
  24. #else // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
  25. # include <boost/asio/steady_timer.hpp>
  26. #endif // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
  27. #if defined(BOOST_ASIO_HAS_BOOST_BIND)
  28. # include <boost/bind.hpp>
  29. #else // defined(BOOST_ASIO_HAS_BOOST_BIND)
  30. # include <functional>
  31. #endif // defined(BOOST_ASIO_HAS_BOOST_BIND)
  32. using namespace boost::asio;
  33. #if defined(BOOST_ASIO_HAS_BOOST_BIND)
  34. namespace bindns = boost;
  35. #else // defined(BOOST_ASIO_HAS_BOOST_BIND)
  36. namespace bindns = std;
  37. #endif
  38. #if defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
  39. typedef deadline_timer timer;
  40. namespace chronons = boost::posix_time;
  41. #elif defined(BOOST_ASIO_HAS_CHRONO)
  42. typedef steady_timer timer;
  43. namespace chronons = boost::asio::chrono;
  44. #endif // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
  45. void increment(int* count)
  46. {
  47. ++(*count);
  48. }
  49. void decrement_to_zero(io_context* ioc, int* count)
  50. {
  51. if (*count > 0)
  52. {
  53. --(*count);
  54. int before_value = *count;
  55. boost::asio::post(*ioc, bindns::bind(decrement_to_zero, ioc, count));
  56. // Handler execution cannot nest, so count value should remain unchanged.
  57. BOOST_ASIO_CHECK(*count == before_value);
  58. }
  59. }
  60. void nested_decrement_to_zero(io_context* ioc, int* count)
  61. {
  62. if (*count > 0)
  63. {
  64. --(*count);
  65. boost::asio::dispatch(*ioc,
  66. bindns::bind(nested_decrement_to_zero, ioc, count));
  67. // Handler execution is nested, so count value should now be zero.
  68. BOOST_ASIO_CHECK(*count == 0);
  69. }
  70. }
  71. void sleep_increment(io_context* ioc, int* count)
  72. {
  73. timer t(*ioc, chronons::seconds(2));
  74. t.wait();
  75. if (++(*count) < 3)
  76. boost::asio::post(*ioc, bindns::bind(sleep_increment, ioc, count));
  77. }
  78. void start_sleep_increments(io_context* ioc, int* count)
  79. {
  80. // Give all threads a chance to start.
  81. timer t(*ioc, chronons::seconds(2));
  82. t.wait();
  83. // Start the first of three increments.
  84. boost::asio::post(*ioc, bindns::bind(sleep_increment, ioc, count));
  85. }
  86. void throw_exception()
  87. {
  88. throw 1;
  89. }
  90. void io_context_run(io_context* ioc)
  91. {
  92. ioc->run();
  93. }
  94. void io_context_test()
  95. {
  96. io_context ioc;
  97. int count = 0;
  98. boost::asio::post(ioc, bindns::bind(increment, &count));
  99. // No handlers can be called until run() is called.
  100. BOOST_ASIO_CHECK(!ioc.stopped());
  101. BOOST_ASIO_CHECK(count == 0);
  102. ioc.run();
  103. // The run() call will not return until all work has finished.
  104. BOOST_ASIO_CHECK(ioc.stopped());
  105. BOOST_ASIO_CHECK(count == 1);
  106. count = 0;
  107. ioc.restart();
  108. boost::asio::post(ioc, bindns::bind(increment, &count));
  109. boost::asio::post(ioc, bindns::bind(increment, &count));
  110. boost::asio::post(ioc, bindns::bind(increment, &count));
  111. boost::asio::post(ioc, bindns::bind(increment, &count));
  112. boost::asio::post(ioc, bindns::bind(increment, &count));
  113. // No handlers can be called until run() is called.
  114. BOOST_ASIO_CHECK(!ioc.stopped());
  115. BOOST_ASIO_CHECK(count == 0);
  116. ioc.run();
  117. // The run() call will not return until all work has finished.
  118. BOOST_ASIO_CHECK(ioc.stopped());
  119. BOOST_ASIO_CHECK(count == 5);
  120. count = 0;
  121. ioc.restart();
  122. executor_work_guard<io_context::executor_type> w = make_work_guard(ioc);
  123. boost::asio::post(ioc, bindns::bind(&io_context::stop, &ioc));
  124. BOOST_ASIO_CHECK(!ioc.stopped());
  125. ioc.run();
  126. // The only operation executed should have been to stop run().
  127. BOOST_ASIO_CHECK(ioc.stopped());
  128. BOOST_ASIO_CHECK(count == 0);
  129. ioc.restart();
  130. boost::asio::post(ioc, bindns::bind(increment, &count));
  131. w.reset();
  132. // No handlers can be called until run() is called.
  133. BOOST_ASIO_CHECK(!ioc.stopped());
  134. BOOST_ASIO_CHECK(count == 0);
  135. ioc.run();
  136. // The run() call will not return until all work has finished.
  137. BOOST_ASIO_CHECK(ioc.stopped());
  138. BOOST_ASIO_CHECK(count == 1);
  139. count = 10;
  140. ioc.restart();
  141. boost::asio::post(ioc, bindns::bind(decrement_to_zero, &ioc, &count));
  142. // No handlers can be called until run() is called.
  143. BOOST_ASIO_CHECK(!ioc.stopped());
  144. BOOST_ASIO_CHECK(count == 10);
  145. ioc.run();
  146. // The run() call will not return until all work has finished.
  147. BOOST_ASIO_CHECK(ioc.stopped());
  148. BOOST_ASIO_CHECK(count == 0);
  149. count = 10;
  150. ioc.restart();
  151. boost::asio::post(ioc, bindns::bind(nested_decrement_to_zero, &ioc, &count));
  152. // No handlers can be called until run() is called.
  153. BOOST_ASIO_CHECK(!ioc.stopped());
  154. BOOST_ASIO_CHECK(count == 10);
  155. ioc.run();
  156. // The run() call will not return until all work has finished.
  157. BOOST_ASIO_CHECK(ioc.stopped());
  158. BOOST_ASIO_CHECK(count == 0);
  159. count = 10;
  160. ioc.restart();
  161. boost::asio::dispatch(ioc,
  162. bindns::bind(nested_decrement_to_zero, &ioc, &count));
  163. // No handlers can be called until run() is called, even though nested
  164. // delivery was specifically allowed in the previous call.
  165. BOOST_ASIO_CHECK(!ioc.stopped());
  166. BOOST_ASIO_CHECK(count == 10);
  167. ioc.run();
  168. // The run() call will not return until all work has finished.
  169. BOOST_ASIO_CHECK(ioc.stopped());
  170. BOOST_ASIO_CHECK(count == 0);
  171. count = 0;
  172. int count2 = 0;
  173. ioc.restart();
  174. BOOST_ASIO_CHECK(!ioc.stopped());
  175. boost::asio::post(ioc, bindns::bind(start_sleep_increments, &ioc, &count));
  176. boost::asio::post(ioc, bindns::bind(start_sleep_increments, &ioc, &count2));
  177. boost::asio::detail::thread thread1(bindns::bind(io_context_run, &ioc));
  178. boost::asio::detail::thread thread2(bindns::bind(io_context_run, &ioc));
  179. thread1.join();
  180. thread2.join();
  181. // The run() calls will not return until all work has finished.
  182. BOOST_ASIO_CHECK(ioc.stopped());
  183. BOOST_ASIO_CHECK(count == 3);
  184. BOOST_ASIO_CHECK(count2 == 3);
  185. count = 10;
  186. io_context ioc2;
  187. boost::asio::dispatch(ioc, boost::asio::bind_executor(ioc2,
  188. bindns::bind(decrement_to_zero, &ioc2, &count)));
  189. ioc.restart();
  190. BOOST_ASIO_CHECK(!ioc.stopped());
  191. ioc.run();
  192. // No decrement_to_zero handlers can be called until run() is called on the
  193. // second io_context object.
  194. BOOST_ASIO_CHECK(ioc.stopped());
  195. BOOST_ASIO_CHECK(count == 10);
  196. ioc2.run();
  197. // The run() call will not return until all work has finished.
  198. BOOST_ASIO_CHECK(count == 0);
  199. count = 0;
  200. int exception_count = 0;
  201. ioc.restart();
  202. boost::asio::post(ioc, &throw_exception);
  203. boost::asio::post(ioc, bindns::bind(increment, &count));
  204. boost::asio::post(ioc, bindns::bind(increment, &count));
  205. boost::asio::post(ioc, &throw_exception);
  206. boost::asio::post(ioc, bindns::bind(increment, &count));
  207. // No handlers can be called until run() is called.
  208. BOOST_ASIO_CHECK(!ioc.stopped());
  209. BOOST_ASIO_CHECK(count == 0);
  210. BOOST_ASIO_CHECK(exception_count == 0);
  211. for (;;)
  212. {
  213. try
  214. {
  215. ioc.run();
  216. break;
  217. }
  218. catch (int)
  219. {
  220. ++exception_count;
  221. }
  222. }
  223. // The run() calls will not return until all work has finished.
  224. BOOST_ASIO_CHECK(ioc.stopped());
  225. BOOST_ASIO_CHECK(count == 3);
  226. BOOST_ASIO_CHECK(exception_count == 2);
  227. }
  228. class test_service : public boost::asio::io_context::service
  229. {
  230. public:
  231. static boost::asio::io_context::id id;
  232. test_service(boost::asio::io_context& s)
  233. : boost::asio::io_context::service(s) {}
  234. private:
  235. virtual void shutdown_service() {}
  236. };
  237. boost::asio::io_context::id test_service::id;
  238. void io_context_service_test()
  239. {
  240. boost::asio::io_context ioc1;
  241. boost::asio::io_context ioc2;
  242. boost::asio::io_context ioc3;
  243. // Implicit service registration.
  244. boost::asio::use_service<test_service>(ioc1);
  245. BOOST_ASIO_CHECK(boost::asio::has_service<test_service>(ioc1));
  246. test_service* svc1 = new test_service(ioc1);
  247. try
  248. {
  249. boost::asio::add_service(ioc1, svc1);
  250. BOOST_ASIO_ERROR("add_service did not throw");
  251. }
  252. catch (boost::asio::service_already_exists&)
  253. {
  254. }
  255. delete svc1;
  256. // Explicit service registration.
  257. test_service* svc2 = new test_service(ioc2);
  258. boost::asio::add_service(ioc2, svc2);
  259. BOOST_ASIO_CHECK(boost::asio::has_service<test_service>(ioc2));
  260. BOOST_ASIO_CHECK(&boost::asio::use_service<test_service>(ioc2) == svc2);
  261. test_service* svc3 = new test_service(ioc2);
  262. try
  263. {
  264. boost::asio::add_service(ioc2, svc3);
  265. BOOST_ASIO_ERROR("add_service did not throw");
  266. }
  267. catch (boost::asio::service_already_exists&)
  268. {
  269. }
  270. delete svc3;
  271. // Explicit registration with invalid owner.
  272. test_service* svc4 = new test_service(ioc2);
  273. try
  274. {
  275. boost::asio::add_service(ioc3, svc4);
  276. BOOST_ASIO_ERROR("add_service did not throw");
  277. }
  278. catch (boost::asio::invalid_service_owner&)
  279. {
  280. }
  281. delete svc4;
  282. BOOST_ASIO_CHECK(!boost::asio::has_service<test_service>(ioc3));
  283. }
  284. BOOST_ASIO_TEST_SUITE
  285. (
  286. "io_context",
  287. BOOST_ASIO_TEST_CASE(io_context_test)
  288. BOOST_ASIO_TEST_CASE(io_context_service_test)
  289. )