io_context_strand.cpp 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. //
  2. // io_context_strand.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_strand.hpp>
  16. #include <sstream>
  17. #include <boost/asio/io_context.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 increment_without_lock(io_context::strand* s, int* count)
  50. {
  51. BOOST_ASIO_CHECK(!s->running_in_this_thread());
  52. int original_count = *count;
  53. dispatch(*s, bindns::bind(increment, count));
  54. // No other functions are currently executing through the locking dispatcher,
  55. // so the previous call to dispatch should have successfully nested.
  56. BOOST_ASIO_CHECK(*count == original_count + 1);
  57. }
  58. void increment_with_lock(io_context::strand* s, int* count)
  59. {
  60. BOOST_ASIO_CHECK(s->running_in_this_thread());
  61. int original_count = *count;
  62. dispatch(*s, bindns::bind(increment, count));
  63. // The current function already holds the strand's lock, so the
  64. // previous call to dispatch should have successfully nested.
  65. BOOST_ASIO_CHECK(*count == original_count + 1);
  66. }
  67. void sleep_increment(io_context* ioc, int* count)
  68. {
  69. timer t(*ioc, chronons::seconds(2));
  70. t.wait();
  71. ++(*count);
  72. }
  73. void increment_by_a(int* count, int a)
  74. {
  75. (*count) += a;
  76. }
  77. void increment_by_a_b(int* count, int a, int b)
  78. {
  79. (*count) += a + b;
  80. }
  81. void increment_by_a_b_c(int* count, int a, int b, int c)
  82. {
  83. (*count) += a + b + c;
  84. }
  85. void increment_by_a_b_c_d(int* count, int a, int b, int c, int d)
  86. {
  87. (*count) += a + b + c + d;
  88. }
  89. void start_sleep_increments(io_context* ioc, io_context::strand* s, int* count)
  90. {
  91. // Give all threads a chance to start.
  92. timer t(*ioc, chronons::seconds(2));
  93. t.wait();
  94. // Start three increments.
  95. post(*s, bindns::bind(sleep_increment, ioc, count));
  96. post(*s, bindns::bind(sleep_increment, ioc, count));
  97. post(*s, bindns::bind(sleep_increment, ioc, count));
  98. }
  99. void throw_exception()
  100. {
  101. throw 1;
  102. }
  103. void io_context_run(io_context* ioc)
  104. {
  105. ioc->run();
  106. }
  107. void strand_test()
  108. {
  109. io_context ioc;
  110. io_context::strand s(ioc);
  111. int count = 0;
  112. post(ioc, bindns::bind(increment_without_lock, &s, &count));
  113. // No handlers can be called until run() is called.
  114. BOOST_ASIO_CHECK(count == 0);
  115. ioc.run();
  116. // The run() call will not return until all work has finished.
  117. BOOST_ASIO_CHECK(count == 1);
  118. count = 0;
  119. ioc.restart();
  120. post(s, bindns::bind(increment_with_lock, &s, &count));
  121. // No handlers can be called until run() is called.
  122. BOOST_ASIO_CHECK(count == 0);
  123. ioc.run();
  124. // The run() call will not return until all work has finished.
  125. BOOST_ASIO_CHECK(count == 1);
  126. count = 0;
  127. ioc.restart();
  128. post(ioc, bindns::bind(start_sleep_increments, &ioc, &s, &count));
  129. boost::asio::detail::thread thread1(bindns::bind(io_context_run, &ioc));
  130. boost::asio::detail::thread thread2(bindns::bind(io_context_run, &ioc));
  131. // Check all events run one after another even though there are two threads.
  132. timer timer1(ioc, chronons::seconds(3));
  133. timer1.wait();
  134. BOOST_ASIO_CHECK(count == 0);
  135. #if defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
  136. timer1.expires_at(timer1.expires_at() + chronons::seconds(2));
  137. #else // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
  138. timer1.expires_at(timer1.expiry() + chronons::seconds(2));
  139. #endif // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
  140. timer1.wait();
  141. BOOST_ASIO_CHECK(count == 1);
  142. #if defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
  143. timer1.expires_at(timer1.expires_at() + chronons::seconds(2));
  144. #else // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
  145. timer1.expires_at(timer1.expiry() + chronons::seconds(2));
  146. #endif // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
  147. timer1.wait();
  148. BOOST_ASIO_CHECK(count == 2);
  149. thread1.join();
  150. thread2.join();
  151. // The run() calls will not return until all work has finished.
  152. BOOST_ASIO_CHECK(count == 3);
  153. count = 0;
  154. int exception_count = 0;
  155. ioc.restart();
  156. post(s, throw_exception);
  157. post(s, bindns::bind(increment, &count));
  158. post(s, bindns::bind(increment, &count));
  159. post(s, throw_exception);
  160. post(s, bindns::bind(increment, &count));
  161. // No handlers can be called until run() is called.
  162. BOOST_ASIO_CHECK(count == 0);
  163. BOOST_ASIO_CHECK(exception_count == 0);
  164. for (;;)
  165. {
  166. try
  167. {
  168. ioc.run();
  169. break;
  170. }
  171. catch (int)
  172. {
  173. ++exception_count;
  174. }
  175. }
  176. // The run() calls will not return until all work has finished.
  177. BOOST_ASIO_CHECK(count == 3);
  178. BOOST_ASIO_CHECK(exception_count == 2);
  179. count = 0;
  180. ioc.restart();
  181. // Check for clean shutdown when handlers posted through an orphaned strand
  182. // are abandoned.
  183. {
  184. io_context::strand s2(ioc);
  185. post(s2, bindns::bind(increment, &count));
  186. post(s2, bindns::bind(increment, &count));
  187. post(s2, bindns::bind(increment, &count));
  188. }
  189. // No handlers can be called until run() is called.
  190. BOOST_ASIO_CHECK(count == 0);
  191. }
  192. void strand_wrap_test()
  193. {
  194. #if !defined(BOOST_ASIO_NO_DEPRECATED)
  195. io_context ioc;
  196. io_context::strand s(ioc);
  197. int count = 0;
  198. s.wrap(bindns::bind(increment, &count))();
  199. // No handlers can be called until run() is called.
  200. BOOST_ASIO_CHECK(count == 0);
  201. ioc.restart();
  202. ioc.run();
  203. // The run() calls will not return until all work has finished.
  204. BOOST_ASIO_CHECK(count == 1);
  205. count = 0;
  206. s.wrap(increment)(&count);
  207. // No handlers can be called until run() is called.
  208. BOOST_ASIO_CHECK(count == 0);
  209. ioc.restart();
  210. ioc.run();
  211. // The run() calls will not return until all work has finished.
  212. BOOST_ASIO_CHECK(count == 1);
  213. count = 0;
  214. s.wrap(increment_by_a)(&count, 1);
  215. // No handlers can be called until run() is called.
  216. BOOST_ASIO_CHECK(count == 0);
  217. ioc.restart();
  218. ioc.run();
  219. // The run() calls will not return until all work has finished.
  220. BOOST_ASIO_CHECK(count == 1);
  221. count = 0;
  222. s.wrap(increment_by_a_b)(&count, 1, 2);
  223. // No handlers can be called until run() is called.
  224. BOOST_ASIO_CHECK(count == 0);
  225. ioc.restart();
  226. ioc.run();
  227. // The run() calls will not return until all work has finished.
  228. BOOST_ASIO_CHECK(count == 3);
  229. count = 0;
  230. s.wrap(increment_by_a_b_c)(&count, 1, 2, 3);
  231. // No handlers can be called until run() is called.
  232. BOOST_ASIO_CHECK(count == 0);
  233. ioc.restart();
  234. ioc.run();
  235. // The run() calls will not return until all work has finished.
  236. BOOST_ASIO_CHECK(count == 6);
  237. count = 0;
  238. s.wrap(increment_by_a_b_c_d)(&count, 1, 2, 3, 4);
  239. // No handlers can be called until run() is called.
  240. BOOST_ASIO_CHECK(count == 0);
  241. ioc.restart();
  242. ioc.run();
  243. // The run() calls will not return until all work has finished.
  244. BOOST_ASIO_CHECK(count == 10);
  245. #endif // !defined(BOOST_ASIO_NO_DEPRECATED)
  246. }
  247. BOOST_ASIO_TEST_SUITE
  248. (
  249. "strand",
  250. BOOST_ASIO_TEST_CASE(strand_test)
  251. BOOST_ASIO_TEST_CASE(strand_wrap_test)
  252. )