strand.cpp 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. //
  2. // 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/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(strand<io_context::executor_type>* 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(strand<io_context::executor_type>* 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,
  90. strand<io_context::executor_type>* s, int* count)
  91. {
  92. // Give all threads a chance to start.
  93. timer t(*ioc, chronons::seconds(2));
  94. t.wait();
  95. // Start three increments.
  96. post(*s, bindns::bind(sleep_increment, ioc, count));
  97. post(*s, bindns::bind(sleep_increment, ioc, count));
  98. post(*s, bindns::bind(sleep_increment, ioc, count));
  99. }
  100. void throw_exception()
  101. {
  102. throw 1;
  103. }
  104. void io_context_run(io_context* ioc)
  105. {
  106. ioc->run();
  107. }
  108. void strand_test()
  109. {
  110. io_context ioc;
  111. strand<io_context::executor_type> s = make_strand(ioc);
  112. int count = 0;
  113. post(ioc, bindns::bind(increment_without_lock, &s, &count));
  114. // No handlers can be called until run() is called.
  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(count == 1);
  119. count = 0;
  120. ioc.restart();
  121. post(s, bindns::bind(increment_with_lock, &s, &count));
  122. // No handlers can be called until run() is called.
  123. BOOST_ASIO_CHECK(count == 0);
  124. ioc.run();
  125. // The run() call will not return until all work has finished.
  126. BOOST_ASIO_CHECK(count == 1);
  127. count = 0;
  128. ioc.restart();
  129. post(ioc, bindns::bind(start_sleep_increments, &ioc, &s, &count));
  130. boost::asio::detail::thread thread1(bindns::bind(io_context_run, &ioc));
  131. boost::asio::detail::thread thread2(bindns::bind(io_context_run, &ioc));
  132. // Check all events run one after another even though there are two threads.
  133. timer timer1(ioc, chronons::seconds(3));
  134. timer1.wait();
  135. BOOST_ASIO_CHECK(count == 0);
  136. #if defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
  137. timer1.expires_at(timer1.expires_at() + chronons::seconds(2));
  138. #else // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
  139. timer1.expires_at(timer1.expiry() + chronons::seconds(2));
  140. #endif // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
  141. timer1.wait();
  142. BOOST_ASIO_CHECK(count == 1);
  143. #if defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
  144. timer1.expires_at(timer1.expires_at() + chronons::seconds(2));
  145. #else // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
  146. timer1.expires_at(timer1.expiry() + chronons::seconds(2));
  147. #endif // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
  148. timer1.wait();
  149. BOOST_ASIO_CHECK(count == 2);
  150. thread1.join();
  151. thread2.join();
  152. // The run() calls will not return until all work has finished.
  153. BOOST_ASIO_CHECK(count == 3);
  154. count = 0;
  155. int exception_count = 0;
  156. ioc.restart();
  157. post(s, throw_exception);
  158. post(s, bindns::bind(increment, &count));
  159. post(s, bindns::bind(increment, &count));
  160. post(s, throw_exception);
  161. post(s, bindns::bind(increment, &count));
  162. // No handlers can be called until run() is called.
  163. BOOST_ASIO_CHECK(count == 0);
  164. BOOST_ASIO_CHECK(exception_count == 0);
  165. for (;;)
  166. {
  167. try
  168. {
  169. ioc.run();
  170. break;
  171. }
  172. catch (int)
  173. {
  174. ++exception_count;
  175. }
  176. }
  177. // The run() calls will not return until all work has finished.
  178. BOOST_ASIO_CHECK(count == 3);
  179. BOOST_ASIO_CHECK(exception_count == 2);
  180. count = 0;
  181. ioc.restart();
  182. // Check for clean shutdown when handlers posted through an orphaned strand
  183. // are abandoned.
  184. {
  185. strand<io_context::executor_type> s2 = make_strand(ioc.get_executor());
  186. post(s2, bindns::bind(increment, &count));
  187. post(s2, bindns::bind(increment, &count));
  188. post(s2, bindns::bind(increment, &count));
  189. }
  190. // No handlers can be called until run() is called.
  191. BOOST_ASIO_CHECK(count == 0);
  192. }
  193. BOOST_ASIO_TEST_SUITE
  194. (
  195. "strand",
  196. BOOST_ASIO_TEST_CASE(strand_test)
  197. )