io_context_strand.hpp 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. //
  2. // io_context_strand.hpp
  3. // ~~~~~~~~~~~~~~~~~~~~~
  4. //
  5. // Copyright (c) 2003-2025 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. #ifndef BOOST_ASIO_IO_CONTEXT_STRAND_HPP
  11. #define BOOST_ASIO_IO_CONTEXT_STRAND_HPP
  12. #if defined(_MSC_VER) && (_MSC_VER >= 1200)
  13. # pragma once
  14. #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
  15. #include <boost/asio/detail/config.hpp>
  16. #if !defined(BOOST_ASIO_NO_EXTENSIONS) \
  17. && !defined(BOOST_ASIO_NO_TS_EXECUTORS)
  18. #include <boost/asio/async_result.hpp>
  19. #include <boost/asio/detail/handler_type_requirements.hpp>
  20. #include <boost/asio/detail/strand_service.hpp>
  21. #include <boost/asio/detail/wrapped_handler.hpp>
  22. #include <boost/asio/io_context.hpp>
  23. #include <boost/asio/detail/push_options.hpp>
  24. namespace boost {
  25. namespace asio {
  26. /// Provides serialised handler execution.
  27. /**
  28. * The io_context::strand class provides the ability to post and dispatch
  29. * handlers with the guarantee that none of those handlers will execute
  30. * concurrently.
  31. *
  32. * @par Order of handler invocation
  33. * Given:
  34. *
  35. * @li a strand object @c s
  36. *
  37. * @li an object @c a meeting completion handler requirements
  38. *
  39. * @li an object @c a1 which is an arbitrary copy of @c a made by the
  40. * implementation
  41. *
  42. * @li an object @c b meeting completion handler requirements
  43. *
  44. * @li an object @c b1 which is an arbitrary copy of @c b made by the
  45. * implementation
  46. *
  47. * if any of the following conditions are true:
  48. *
  49. * @li @c s.post(a) happens-before @c s.post(b)
  50. *
  51. * @li @c s.post(a) happens-before @c s.dispatch(b), where the latter is
  52. * performed outside the strand
  53. *
  54. * @li @c s.dispatch(a) happens-before @c s.post(b), where the former is
  55. * performed outside the strand
  56. *
  57. * @li @c s.dispatch(a) happens-before @c s.dispatch(b), where both are
  58. * performed outside the strand
  59. *
  60. * then @c a() happens-before @c b()
  61. *
  62. * Note that in the following case:
  63. * @code async_op_1(..., s.wrap(a));
  64. * async_op_2(..., s.wrap(b)); @endcode
  65. * the completion of the first async operation will perform @c s.dispatch(a),
  66. * and the second will perform @c s.dispatch(b), but the order in which those
  67. * are performed is unspecified. That is, you cannot state whether one
  68. * happens-before the other. Therefore none of the above conditions are met and
  69. * no ordering guarantee is made.
  70. *
  71. * @note The implementation makes no guarantee that handlers posted or
  72. * dispatched through different @c strand objects will be invoked concurrently.
  73. *
  74. * @par Thread Safety
  75. * @e Distinct @e objects: Safe.@n
  76. * @e Shared @e objects: Safe.
  77. *
  78. * @par Concepts:
  79. * Dispatcher.
  80. */
  81. class io_context::strand
  82. {
  83. public:
  84. /// Constructor.
  85. /**
  86. * Constructs the strand.
  87. *
  88. * @param io_context The io_context object that the strand will use to
  89. * dispatch handlers that are ready to be run.
  90. */
  91. explicit strand(boost::asio::io_context& io_context)
  92. : service_(boost::asio::use_service<
  93. boost::asio::detail::strand_service>(io_context))
  94. {
  95. service_.construct(impl_);
  96. }
  97. /// Copy constructor.
  98. /**
  99. * Creates a copy such that both strand objects share the same underlying
  100. * state.
  101. */
  102. strand(const strand& other) noexcept
  103. : service_(other.service_),
  104. impl_(other.impl_)
  105. {
  106. }
  107. /// Destructor.
  108. /**
  109. * Destroys a strand.
  110. *
  111. * Handlers posted through the strand that have not yet been invoked will
  112. * still be dispatched in a way that meets the guarantee of non-concurrency.
  113. */
  114. ~strand()
  115. {
  116. }
  117. /// Obtain the underlying execution context.
  118. boost::asio::io_context& context() const noexcept
  119. {
  120. return service_.get_io_context();
  121. }
  122. /// Inform the strand that it has some outstanding work to do.
  123. /**
  124. * The strand delegates this call to its underlying io_context.
  125. */
  126. void on_work_started() const noexcept
  127. {
  128. context().get_executor().on_work_started();
  129. }
  130. /// Inform the strand that some work is no longer outstanding.
  131. /**
  132. * The strand delegates this call to its underlying io_context.
  133. */
  134. void on_work_finished() const noexcept
  135. {
  136. context().get_executor().on_work_finished();
  137. }
  138. /// Request the strand to invoke the given function object.
  139. /**
  140. * This function is used to ask the strand to execute the given function
  141. * object on its underlying io_context. The function object will be executed
  142. * inside this function if the strand is not otherwise busy and if the
  143. * underlying io_context's executor's @c dispatch() function is also able to
  144. * execute the function before returning.
  145. *
  146. * @param f The function object to be called. The executor will make
  147. * a copy of the handler object as required. The function signature of the
  148. * function object must be: @code void function(); @endcode
  149. *
  150. * @param a An allocator that may be used by the executor to allocate the
  151. * internal storage needed for function invocation.
  152. */
  153. template <typename Function, typename Allocator>
  154. void dispatch(Function&& f, const Allocator& a) const
  155. {
  156. decay_t<Function> tmp(static_cast<Function&&>(f));
  157. service_.dispatch(impl_, tmp);
  158. (void)a;
  159. }
  160. /// Request the strand to invoke the given function object.
  161. /**
  162. * This function is used to ask the executor to execute the given function
  163. * object. The function object will never be executed inside this function.
  164. * Instead, it will be scheduled to run by the underlying io_context.
  165. *
  166. * @param f The function object to be called. The executor will make
  167. * a copy of the handler object as required. The function signature of the
  168. * function object must be: @code void function(); @endcode
  169. *
  170. * @param a An allocator that may be used by the executor to allocate the
  171. * internal storage needed for function invocation.
  172. */
  173. template <typename Function, typename Allocator>
  174. void post(Function&& f, const Allocator& a) const
  175. {
  176. decay_t<Function> tmp(static_cast<Function&&>(f));
  177. service_.post(impl_, tmp);
  178. (void)a;
  179. }
  180. /// Request the strand to invoke the given function object.
  181. /**
  182. * This function is used to ask the executor to execute the given function
  183. * object. The function object will never be executed inside this function.
  184. * Instead, it will be scheduled to run by the underlying io_context.
  185. *
  186. * @param f The function object to be called. The executor will make
  187. * a copy of the handler object as required. The function signature of the
  188. * function object must be: @code void function(); @endcode
  189. *
  190. * @param a An allocator that may be used by the executor to allocate the
  191. * internal storage needed for function invocation.
  192. */
  193. template <typename Function, typename Allocator>
  194. void defer(Function&& f, const Allocator& a) const
  195. {
  196. decay_t<Function> tmp(static_cast<Function&&>(f));
  197. service_.post(impl_, tmp);
  198. (void)a;
  199. }
  200. #if !defined(BOOST_ASIO_NO_DEPRECATED)
  201. /// (Deprecated: Use boost::asio::bind_executor().) Create a new handler that
  202. /// automatically dispatches the wrapped handler on the strand.
  203. /**
  204. * This function is used to create a new handler function object that, when
  205. * invoked, will automatically pass the wrapped handler to the strand's
  206. * dispatch function.
  207. *
  208. * @param handler The handler to be wrapped. The strand will make a copy of
  209. * the handler object as required. The function signature of the handler must
  210. * be: @code void handler(A1 a1, ... An an); @endcode
  211. *
  212. * @return A function object that, when invoked, passes the wrapped handler to
  213. * the strand's dispatch function. Given a function object with the signature:
  214. * @code R f(A1 a1, ... An an); @endcode
  215. * If this function object is passed to the wrap function like so:
  216. * @code strand.wrap(f); @endcode
  217. * then the return value is a function object with the signature
  218. * @code void g(A1 a1, ... An an); @endcode
  219. * that, when invoked, executes code equivalent to:
  220. * @code boost::asio::dispatch(strand, boost::bind(f, a1, ... an)); @endcode
  221. */
  222. template <typename Handler>
  223. BOOST_ASIO_DEPRECATED_MSG("Use boost::asio::bind_executor()")
  224. #if defined(GENERATING_DOCUMENTATION)
  225. unspecified
  226. #else
  227. detail::wrapped_handler<strand, Handler, detail::is_continuation_if_running>
  228. #endif
  229. wrap(Handler handler)
  230. {
  231. return detail::wrapped_handler<io_context::strand, Handler,
  232. detail::is_continuation_if_running>(*this, handler);
  233. }
  234. #endif // !defined(BOOST_ASIO_NO_DEPRECATED)
  235. /// Determine whether the strand is running in the current thread.
  236. /**
  237. * @return @c true if the current thread is executing a handler that was
  238. * submitted to the strand using post(), dispatch() or wrap(). Otherwise
  239. * returns @c false.
  240. */
  241. bool running_in_this_thread() const noexcept
  242. {
  243. return service_.running_in_this_thread(impl_);
  244. }
  245. /// Compare two strands for equality.
  246. /**
  247. * Two strands are equal if they refer to the same ordered, non-concurrent
  248. * state.
  249. */
  250. friend bool operator==(const strand& a, const strand& b) noexcept
  251. {
  252. return a.impl_ == b.impl_;
  253. }
  254. /// Compare two strands for inequality.
  255. /**
  256. * Two strands are equal if they refer to the same ordered, non-concurrent
  257. * state.
  258. */
  259. friend bool operator!=(const strand& a, const strand& b) noexcept
  260. {
  261. return a.impl_ != b.impl_;
  262. }
  263. private:
  264. boost::asio::detail::strand_service& service_;
  265. mutable boost::asio::detail::strand_service::implementation_type impl_;
  266. };
  267. } // namespace asio
  268. } // namespace boost
  269. #include <boost/asio/detail/pop_options.hpp>
  270. #endif // !defined(BOOST_ASIO_NO_EXTENSIONS)
  271. // && !defined(BOOST_ASIO_NO_TS_EXECUTORS)
  272. #endif // BOOST_ASIO_IO_CONTEXT_STRAND_HPP