spawn.hpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. //
  2. // spawn.hpp
  3. // ~~~~~~~~~
  4. //
  5. // Copyright (c) 2003-2022 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_SPAWN_HPP
  11. #define BOOST_ASIO_SPAWN_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. #include <boost/coroutine/all.hpp>
  17. #include <boost/asio/any_io_executor.hpp>
  18. #include <boost/asio/bind_executor.hpp>
  19. #include <boost/asio/detail/memory.hpp>
  20. #include <boost/asio/detail/type_traits.hpp>
  21. #include <boost/asio/detail/wrapped_handler.hpp>
  22. #include <boost/asio/io_context.hpp>
  23. #include <boost/asio/is_executor.hpp>
  24. #include <boost/asio/strand.hpp>
  25. #include <boost/asio/detail/push_options.hpp>
  26. namespace boost {
  27. namespace asio {
  28. /// A completion token that represents the currently executing coroutine.
  29. /**
  30. * The basic_yield_context class is a completion token type that is used to
  31. * represent the currently executing stackful coroutine. A basic_yield_context
  32. * object may be passed as a completion token to an asynchronous operation. For
  33. * example:
  34. *
  35. * @code template <typename Handler>
  36. * void my_coroutine(basic_yield_context<Handler> yield)
  37. * {
  38. * ...
  39. * std::size_t n = my_socket.async_read_some(buffer, yield);
  40. * ...
  41. * } @endcode
  42. *
  43. * The initiating function (async_read_some in the above example) suspends the
  44. * current coroutine. The coroutine is resumed when the asynchronous operation
  45. * completes, and the result of the operation is returned.
  46. */
  47. template <typename Handler>
  48. class basic_yield_context
  49. {
  50. public:
  51. /// The coroutine callee type, used by the implementation.
  52. /**
  53. * When using Boost.Coroutine v1, this type is:
  54. * @code typename coroutine<void()> @endcode
  55. * When using Boost.Coroutine v2 (unidirectional coroutines), this type is:
  56. * @code push_coroutine<void> @endcode
  57. */
  58. #if defined(GENERATING_DOCUMENTATION)
  59. typedef implementation_defined callee_type;
  60. #elif defined(BOOST_COROUTINES_UNIDIRECT) || defined(BOOST_COROUTINES_V2)
  61. typedef boost::coroutines::push_coroutine<void> callee_type;
  62. #else
  63. typedef boost::coroutines::coroutine<void()> callee_type;
  64. #endif
  65. /// The coroutine caller type, used by the implementation.
  66. /**
  67. * When using Boost.Coroutine v1, this type is:
  68. * @code typename coroutine<void()>::caller_type @endcode
  69. * When using Boost.Coroutine v2 (unidirectional coroutines), this type is:
  70. * @code pull_coroutine<void> @endcode
  71. */
  72. #if defined(GENERATING_DOCUMENTATION)
  73. typedef implementation_defined caller_type;
  74. #elif defined(BOOST_COROUTINES_UNIDIRECT) || defined(BOOST_COROUTINES_V2)
  75. typedef boost::coroutines::pull_coroutine<void> caller_type;
  76. #else
  77. typedef boost::coroutines::coroutine<void()>::caller_type caller_type;
  78. #endif
  79. /// Construct a yield context to represent the specified coroutine.
  80. /**
  81. * Most applications do not need to use this constructor. Instead, the
  82. * spawn() function passes a yield context as an argument to the coroutine
  83. * function.
  84. */
  85. basic_yield_context(
  86. const detail::weak_ptr<callee_type>& coro,
  87. caller_type& ca, Handler& handler)
  88. : coro_(coro),
  89. ca_(ca),
  90. handler_(handler),
  91. ec_(0)
  92. {
  93. }
  94. /// Construct a yield context from another yield context type.
  95. /**
  96. * Requires that OtherHandler be convertible to Handler.
  97. */
  98. template <typename OtherHandler>
  99. basic_yield_context(const basic_yield_context<OtherHandler>& other)
  100. : coro_(other.coro_),
  101. ca_(other.ca_),
  102. handler_(other.handler_),
  103. ec_(other.ec_)
  104. {
  105. }
  106. /// Return a yield context that sets the specified error_code.
  107. /**
  108. * By default, when a yield context is used with an asynchronous operation, a
  109. * non-success error_code is converted to system_error and thrown. This
  110. * operator may be used to specify an error_code object that should instead be
  111. * set with the asynchronous operation's result. For example:
  112. *
  113. * @code template <typename Handler>
  114. * void my_coroutine(basic_yield_context<Handler> yield)
  115. * {
  116. * ...
  117. * std::size_t n = my_socket.async_read_some(buffer, yield[ec]);
  118. * if (ec)
  119. * {
  120. * // An error occurred.
  121. * }
  122. * ...
  123. * } @endcode
  124. */
  125. basic_yield_context operator[](boost::system::error_code& ec) const
  126. {
  127. basic_yield_context tmp(*this);
  128. tmp.ec_ = &ec;
  129. return tmp;
  130. }
  131. #if defined(GENERATING_DOCUMENTATION)
  132. private:
  133. #endif // defined(GENERATING_DOCUMENTATION)
  134. detail::weak_ptr<callee_type> coro_;
  135. caller_type& ca_;
  136. Handler handler_;
  137. boost::system::error_code* ec_;
  138. };
  139. #if defined(GENERATING_DOCUMENTATION)
  140. /// Context object that represents the currently executing coroutine.
  141. typedef basic_yield_context<unspecified> yield_context;
  142. #else // defined(GENERATING_DOCUMENTATION)
  143. typedef basic_yield_context<
  144. executor_binder<void(*)(), any_io_executor> > yield_context;
  145. #endif // defined(GENERATING_DOCUMENTATION)
  146. /**
  147. * @defgroup spawn boost::asio::spawn
  148. *
  149. * @brief Start a new stackful coroutine.
  150. *
  151. * The spawn() function is a high-level wrapper over the Boost.Coroutine
  152. * library. This function enables programs to implement asynchronous logic in a
  153. * synchronous manner, as illustrated by the following example:
  154. *
  155. * @code boost::asio::spawn(my_strand, do_echo);
  156. *
  157. * // ...
  158. *
  159. * void do_echo(boost::asio::yield_context yield)
  160. * {
  161. * try
  162. * {
  163. * char data[128];
  164. * for (;;)
  165. * {
  166. * std::size_t length =
  167. * my_socket.async_read_some(
  168. * boost::asio::buffer(data), yield);
  169. *
  170. * boost::asio::async_write(my_socket,
  171. * boost::asio::buffer(data, length), yield);
  172. * }
  173. * }
  174. * catch (std::exception& e)
  175. * {
  176. * // ...
  177. * }
  178. * } @endcode
  179. */
  180. /*@{*/
  181. /// Start a new stackful coroutine, calling the specified handler when it
  182. /// completes.
  183. /**
  184. * This function is used to launch a new coroutine.
  185. *
  186. * @param function The coroutine function. The function must have the signature:
  187. * @code void function(basic_yield_context<Handler> yield); @endcode
  188. *
  189. * @param attributes Boost.Coroutine attributes used to customise the coroutine.
  190. */
  191. template <typename Function>
  192. void spawn(BOOST_ASIO_MOVE_ARG(Function) function,
  193. const boost::coroutines::attributes& attributes
  194. = boost::coroutines::attributes());
  195. /// Start a new stackful coroutine, calling the specified handler when it
  196. /// completes.
  197. /**
  198. * This function is used to launch a new coroutine.
  199. *
  200. * @param handler A handler to be called when the coroutine exits. More
  201. * importantly, the handler provides an execution context (via the the handler
  202. * invocation hook) for the coroutine. The handler must have the signature:
  203. * @code void handler(); @endcode
  204. *
  205. * @param function The coroutine function. The function must have the signature:
  206. * @code void function(basic_yield_context<Handler> yield); @endcode
  207. *
  208. * @param attributes Boost.Coroutine attributes used to customise the coroutine.
  209. */
  210. template <typename Handler, typename Function>
  211. void spawn(BOOST_ASIO_MOVE_ARG(Handler) handler,
  212. BOOST_ASIO_MOVE_ARG(Function) function,
  213. const boost::coroutines::attributes& attributes
  214. = boost::coroutines::attributes(),
  215. typename constraint<
  216. !is_executor<typename decay<Handler>::type>::value &&
  217. !execution::is_executor<typename decay<Handler>::type>::value &&
  218. !is_convertible<Handler&, execution_context&>::value>::type = 0);
  219. /// Start a new stackful coroutine, inheriting the execution context of another.
  220. /**
  221. * This function is used to launch a new coroutine.
  222. *
  223. * @param ctx Identifies the current coroutine as a parent of the new
  224. * coroutine. This specifies that the new coroutine should inherit the
  225. * execution context of the parent. For example, if the parent coroutine is
  226. * executing in a particular strand, then the new coroutine will execute in the
  227. * same strand.
  228. *
  229. * @param function The coroutine function. The function must have the signature:
  230. * @code void function(basic_yield_context<Handler> yield); @endcode
  231. *
  232. * @param attributes Boost.Coroutine attributes used to customise the coroutine.
  233. */
  234. template <typename Handler, typename Function>
  235. void spawn(basic_yield_context<Handler> ctx,
  236. BOOST_ASIO_MOVE_ARG(Function) function,
  237. const boost::coroutines::attributes& attributes
  238. = boost::coroutines::attributes());
  239. /// Start a new stackful coroutine that executes on a given executor.
  240. /**
  241. * This function is used to launch a new coroutine.
  242. *
  243. * @param ex Identifies the executor that will run the coroutine. The new
  244. * coroutine is implicitly given its own strand within this executor.
  245. *
  246. * @param function The coroutine function. The function must have the signature:
  247. * @code void function(yield_context yield); @endcode
  248. *
  249. * @param attributes Boost.Coroutine attributes used to customise the coroutine.
  250. */
  251. template <typename Function, typename Executor>
  252. void spawn(const Executor& ex,
  253. BOOST_ASIO_MOVE_ARG(Function) function,
  254. const boost::coroutines::attributes& attributes
  255. = boost::coroutines::attributes(),
  256. typename constraint<
  257. is_executor<Executor>::value || execution::is_executor<Executor>::value
  258. >::type = 0);
  259. /// Start a new stackful coroutine that executes on a given strand.
  260. /**
  261. * This function is used to launch a new coroutine.
  262. *
  263. * @param ex Identifies the strand that will run the coroutine.
  264. *
  265. * @param function The coroutine function. The function must have the signature:
  266. * @code void function(yield_context yield); @endcode
  267. *
  268. * @param attributes Boost.Coroutine attributes used to customise the coroutine.
  269. */
  270. template <typename Function, typename Executor>
  271. void spawn(const strand<Executor>& ex,
  272. BOOST_ASIO_MOVE_ARG(Function) function,
  273. const boost::coroutines::attributes& attributes
  274. = boost::coroutines::attributes());
  275. #if !defined(BOOST_ASIO_NO_TS_EXECUTORS)
  276. /// Start a new stackful coroutine that executes in the context of a strand.
  277. /**
  278. * This function is used to launch a new coroutine.
  279. *
  280. * @param s Identifies a strand. By starting multiple coroutines on the same
  281. * strand, the implementation ensures that none of those coroutines can execute
  282. * simultaneously.
  283. *
  284. * @param function The coroutine function. The function must have the signature:
  285. * @code void function(yield_context yield); @endcode
  286. *
  287. * @param attributes Boost.Coroutine attributes used to customise the coroutine.
  288. */
  289. template <typename Function>
  290. void spawn(const boost::asio::io_context::strand& s,
  291. BOOST_ASIO_MOVE_ARG(Function) function,
  292. const boost::coroutines::attributes& attributes
  293. = boost::coroutines::attributes());
  294. #endif // !defined(BOOST_ASIO_NO_TS_EXECUTORS)
  295. /// Start a new stackful coroutine that executes on a given execution context.
  296. /**
  297. * This function is used to launch a new coroutine.
  298. *
  299. * @param ctx Identifies the execution context that will run the coroutine. The
  300. * new coroutine is implicitly given its own strand within this execution
  301. * context.
  302. *
  303. * @param function The coroutine function. The function must have the signature:
  304. * @code void function(yield_context yield); @endcode
  305. *
  306. * @param attributes Boost.Coroutine attributes used to customise the coroutine.
  307. */
  308. template <typename Function, typename ExecutionContext>
  309. void spawn(ExecutionContext& ctx,
  310. BOOST_ASIO_MOVE_ARG(Function) function,
  311. const boost::coroutines::attributes& attributes
  312. = boost::coroutines::attributes(),
  313. typename constraint<is_convertible<
  314. ExecutionContext&, execution_context&>::value>::type = 0);
  315. /*@}*/
  316. } // namespace asio
  317. } // namespace boost
  318. #include <boost/asio/detail/pop_options.hpp>
  319. #include <boost/asio/impl/spawn.hpp>
  320. #endif // BOOST_ASIO_SPAWN_HPP