spawn.hpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693
  1. //
  2. // spawn.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_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/asio/any_io_executor.hpp>
  17. #include <boost/asio/cancellation_signal.hpp>
  18. #include <boost/asio/cancellation_state.hpp>
  19. #include <boost/asio/detail/exception.hpp>
  20. #include <boost/asio/detail/memory.hpp>
  21. #include <boost/asio/detail/type_traits.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. namespace detail {
  29. // Base class for all spawn()-ed thread implementations.
  30. class spawned_thread_base
  31. {
  32. public:
  33. spawned_thread_base()
  34. : owner_(0),
  35. has_context_switched_(false),
  36. throw_if_cancelled_(false),
  37. terminal_(false)
  38. {
  39. }
  40. virtual ~spawned_thread_base() {}
  41. virtual void resume() = 0;
  42. virtual void suspend_with(void (*fn)(void*), void* arg) = 0;
  43. virtual void destroy() = 0;
  44. void attach(spawned_thread_base** owner)
  45. {
  46. owner_ = owner;
  47. *owner_ = this;
  48. }
  49. void detach()
  50. {
  51. if (owner_)
  52. *owner_ = 0;
  53. owner_ = 0;
  54. }
  55. void suspend()
  56. {
  57. suspend_with(0, 0);
  58. }
  59. template <typename F>
  60. void suspend_with(F f)
  61. {
  62. suspend_with(&spawned_thread_base::call<F>, &f);
  63. }
  64. cancellation_slot get_cancellation_slot() const noexcept
  65. {
  66. return cancellation_state_.slot();
  67. }
  68. cancellation_state get_cancellation_state() const noexcept
  69. {
  70. return cancellation_state_;
  71. }
  72. void reset_cancellation_state()
  73. {
  74. cancellation_state_ = cancellation_state(parent_cancellation_slot_);
  75. }
  76. template <typename Filter>
  77. void reset_cancellation_state(Filter filter)
  78. {
  79. cancellation_state_ = cancellation_state(
  80. parent_cancellation_slot_, filter, filter);
  81. }
  82. template <typename InFilter, typename OutFilter>
  83. void reset_cancellation_state(InFilter in_filter, OutFilter out_filter)
  84. {
  85. cancellation_state_ = cancellation_state(
  86. parent_cancellation_slot_, in_filter, out_filter);
  87. }
  88. cancellation_type_t cancelled() const noexcept
  89. {
  90. return cancellation_state_.cancelled();
  91. }
  92. bool has_context_switched() const noexcept
  93. {
  94. return has_context_switched_;
  95. }
  96. bool throw_if_cancelled() const noexcept
  97. {
  98. return throw_if_cancelled_;
  99. }
  100. void throw_if_cancelled(bool value) noexcept
  101. {
  102. throw_if_cancelled_ = value;
  103. }
  104. protected:
  105. spawned_thread_base** owner_; // Points to data member in active handler.
  106. boost::asio::cancellation_slot parent_cancellation_slot_;
  107. boost::asio::cancellation_state cancellation_state_;
  108. bool has_context_switched_;
  109. bool throw_if_cancelled_;
  110. bool terminal_;
  111. private:
  112. // Disallow copying and assignment.
  113. spawned_thread_base(const spawned_thread_base&) = delete;
  114. spawned_thread_base& operator=(const spawned_thread_base&) = delete;
  115. template <typename F>
  116. static void call(void* f)
  117. {
  118. (*static_cast<F*>(f))();
  119. }
  120. };
  121. template <typename T>
  122. struct spawn_signature
  123. {
  124. typedef void type(exception_ptr, T);
  125. };
  126. template <>
  127. struct spawn_signature<void>
  128. {
  129. typedef void type(exception_ptr);
  130. };
  131. template <typename Executor>
  132. class initiate_spawn;
  133. } // namespace detail
  134. /// A @ref completion_token that represents the currently executing coroutine.
  135. /**
  136. * The basic_yield_context class is a completion token type that is used to
  137. * represent the currently executing stackful coroutine. A basic_yield_context
  138. * object may be passed as a completion token to an asynchronous operation. For
  139. * example:
  140. *
  141. * @code template <typename Executor>
  142. * void my_coroutine(basic_yield_context<Executor> yield)
  143. * {
  144. * ...
  145. * std::size_t n = my_socket.async_read_some(buffer, yield);
  146. * ...
  147. * } @endcode
  148. *
  149. * The initiating function (async_read_some in the above example) suspends the
  150. * current coroutine. The coroutine is resumed when the asynchronous operation
  151. * completes, and the result of the operation is returned.
  152. */
  153. template <typename Executor>
  154. class basic_yield_context
  155. {
  156. public:
  157. /// The executor type associated with the yield context.
  158. typedef Executor executor_type;
  159. /// The cancellation slot type associated with the yield context.
  160. typedef cancellation_slot cancellation_slot_type;
  161. /// Construct a yield context from another yield context type.
  162. /**
  163. * Requires that OtherExecutor be convertible to Executor.
  164. */
  165. template <typename OtherExecutor>
  166. basic_yield_context(const basic_yield_context<OtherExecutor>& other,
  167. constraint_t<
  168. is_convertible<OtherExecutor, Executor>::value
  169. > = 0)
  170. : spawned_thread_(other.spawned_thread_),
  171. executor_(other.executor_),
  172. ec_(other.ec_)
  173. {
  174. }
  175. /// Get the executor associated with the yield context.
  176. executor_type get_executor() const noexcept
  177. {
  178. return executor_;
  179. }
  180. /// Get the cancellation slot associated with the coroutine.
  181. cancellation_slot_type get_cancellation_slot() const noexcept
  182. {
  183. return spawned_thread_->get_cancellation_slot();
  184. }
  185. /// Get the cancellation state associated with the coroutine.
  186. cancellation_state get_cancellation_state() const noexcept
  187. {
  188. return spawned_thread_->get_cancellation_state();
  189. }
  190. /// Reset the cancellation state associated with the coroutine.
  191. /**
  192. * Let <tt>P</tt> be the cancellation slot associated with the current
  193. * coroutine's @ref spawn completion handler. Assigns a new
  194. * boost::asio::cancellation_state object <tt>S</tt>, constructed as
  195. * <tt>S(P)</tt>, into the current coroutine's cancellation state object.
  196. */
  197. void reset_cancellation_state() const
  198. {
  199. spawned_thread_->reset_cancellation_state();
  200. }
  201. /// Reset the cancellation state associated with the coroutine.
  202. /**
  203. * Let <tt>P</tt> be the cancellation slot associated with the current
  204. * coroutine's @ref spawn completion handler. Assigns a new
  205. * boost::asio::cancellation_state object <tt>S</tt>, constructed as <tt>S(P,
  206. * std::forward<Filter>(filter))</tt>, into the current coroutine's
  207. * cancellation state object.
  208. */
  209. template <typename Filter>
  210. void reset_cancellation_state(Filter&& filter) const
  211. {
  212. spawned_thread_->reset_cancellation_state(
  213. static_cast<Filter&&>(filter));
  214. }
  215. /// Reset the cancellation state associated with the coroutine.
  216. /**
  217. * Let <tt>P</tt> be the cancellation slot associated with the current
  218. * coroutine's @ref spawn completion handler. Assigns a new
  219. * boost::asio::cancellation_state object <tt>S</tt>, constructed as <tt>S(P,
  220. * std::forward<InFilter>(in_filter),
  221. * std::forward<OutFilter>(out_filter))</tt>, into the current coroutine's
  222. * cancellation state object.
  223. */
  224. template <typename InFilter, typename OutFilter>
  225. void reset_cancellation_state(InFilter&& in_filter,
  226. OutFilter&& out_filter) const
  227. {
  228. spawned_thread_->reset_cancellation_state(
  229. static_cast<InFilter&&>(in_filter),
  230. static_cast<OutFilter&&>(out_filter));
  231. }
  232. /// Determine whether the current coroutine has been cancelled.
  233. cancellation_type_t cancelled() const noexcept
  234. {
  235. return spawned_thread_->cancelled();
  236. }
  237. /// Determine whether the coroutine throws if trying to suspend when it has
  238. /// been cancelled.
  239. bool throw_if_cancelled() const noexcept
  240. {
  241. return spawned_thread_->throw_if_cancelled();
  242. }
  243. /// Set whether the coroutine throws if trying to suspend when it has been
  244. /// cancelled.
  245. void throw_if_cancelled(bool value) const noexcept
  246. {
  247. spawned_thread_->throw_if_cancelled(value);
  248. }
  249. /// Return a yield context that sets the specified error_code.
  250. /**
  251. * By default, when a yield context is used with an asynchronous operation, a
  252. * non-success error_code is converted to system_error and thrown. This
  253. * operator may be used to specify an error_code object that should instead be
  254. * set with the asynchronous operation's result. For example:
  255. *
  256. * @code template <typename Executor>
  257. * void my_coroutine(basic_yield_context<Executor> yield)
  258. * {
  259. * ...
  260. * std::size_t n = my_socket.async_read_some(buffer, yield[ec]);
  261. * if (ec)
  262. * {
  263. * // An error occurred.
  264. * }
  265. * ...
  266. * } @endcode
  267. */
  268. basic_yield_context operator[](boost::system::error_code& ec) const
  269. {
  270. basic_yield_context tmp(*this);
  271. tmp.ec_ = &ec;
  272. return tmp;
  273. }
  274. #if !defined(GENERATING_DOCUMENTATION)
  275. //private:
  276. basic_yield_context(detail::spawned_thread_base* spawned_thread,
  277. const Executor& ex)
  278. : spawned_thread_(spawned_thread),
  279. executor_(ex),
  280. ec_(0)
  281. {
  282. }
  283. detail::spawned_thread_base* spawned_thread_;
  284. Executor executor_;
  285. boost::system::error_code* ec_;
  286. #endif // !defined(GENERATING_DOCUMENTATION)
  287. };
  288. /// A @ref completion_token object that represents the currently executing
  289. /// coroutine.
  290. typedef basic_yield_context<any_io_executor> yield_context;
  291. /**
  292. * @defgroup spawn boost::asio::spawn
  293. *
  294. * @brief Start a new stackful coroutine.
  295. *
  296. * The spawn() function is a high-level wrapper over the Boost.Coroutine
  297. * library. This function enables programs to implement asynchronous logic in a
  298. * synchronous manner, as illustrated by the following example:
  299. *
  300. * @code boost::asio::spawn(my_strand, do_echo, boost::asio::detached);
  301. *
  302. * // ...
  303. *
  304. * void do_echo(boost::asio::yield_context yield)
  305. * {
  306. * try
  307. * {
  308. * char data[128];
  309. * for (;;)
  310. * {
  311. * std::size_t length =
  312. * my_socket.async_read_some(
  313. * boost::asio::buffer(data), yield);
  314. *
  315. * boost::asio::async_write(my_socket,
  316. * boost::asio::buffer(data, length), yield);
  317. * }
  318. * }
  319. * catch (std::exception& e)
  320. * {
  321. * // ...
  322. * }
  323. * } @endcode
  324. */
  325. /*@{*/
  326. /// Start a new stackful coroutine that executes on a given executor.
  327. /**
  328. * This function is used to launch a new stackful coroutine.
  329. *
  330. * @param ex Identifies the executor that will run the stackful coroutine.
  331. *
  332. * @param function The coroutine function. The function must be callable the
  333. * signature:
  334. * @code void function(basic_yield_context<Executor> yield); @endcode
  335. *
  336. * @param token The @ref completion_token that will handle the notification
  337. * that the coroutine has completed. If the return type @c R of @c function is
  338. * @c void, the function signature of the completion handler must be:
  339. *
  340. * @code void handler(std::exception_ptr); @endcode
  341. * Otherwise, the function signature of the completion handler must be:
  342. * @code void handler(std::exception_ptr, R); @endcode
  343. *
  344. * @par Completion Signature
  345. * @code void(std::exception_ptr, R) @endcode
  346. * where @c R is the return type of the function object.
  347. *
  348. * @par Per-Operation Cancellation
  349. * The new thread of execution is created with a cancellation state that
  350. * supports @c cancellation_type::terminal values only. To change the
  351. * cancellation state, call the basic_yield_context member function
  352. * @c reset_cancellation_state.
  353. */
  354. template <typename Executor, typename F,
  355. BOOST_ASIO_COMPLETION_TOKEN_FOR(typename detail::spawn_signature<
  356. result_of_t<F(basic_yield_context<Executor>)>>::type)
  357. CompletionToken = default_completion_token_t<Executor>>
  358. auto spawn(const Executor& ex, F&& function,
  359. CompletionToken&& token = default_completion_token_t<Executor>(),
  360. constraint_t<
  361. is_executor<Executor>::value || execution::is_executor<Executor>::value
  362. > = 0)
  363. -> decltype(
  364. async_initiate<CompletionToken,
  365. typename detail::spawn_signature<
  366. result_of_t<F(basic_yield_context<Executor>)>>::type>(
  367. declval<detail::initiate_spawn<Executor>>(),
  368. token, static_cast<F&&>(function)));
  369. /// Start a new stackful coroutine that executes on a given execution context.
  370. /**
  371. * This function is used to launch a new stackful coroutine.
  372. *
  373. * @param ctx Identifies the execution context that will run the stackful
  374. * coroutine.
  375. *
  376. * @param function The coroutine function. The function must be callable the
  377. * signature:
  378. * @code void function(basic_yield_context<Executor> yield); @endcode
  379. *
  380. * @param token The @ref completion_token that will handle the notification
  381. * that the coroutine has completed. If the return type @c R of @c function is
  382. * @c void, the function signature of the completion handler must be:
  383. *
  384. * @code void handler(std::exception_ptr); @endcode
  385. * Otherwise, the function signature of the completion handler must be:
  386. * @code void handler(std::exception_ptr, R); @endcode
  387. *
  388. * @par Completion Signature
  389. * @code void(std::exception_ptr, R) @endcode
  390. * where @c R is the return type of the function object.
  391. *
  392. * @par Per-Operation Cancellation
  393. * The new thread of execution is created with a cancellation state that
  394. * supports @c cancellation_type::terminal values only. To change the
  395. * cancellation state, call the basic_yield_context member function
  396. * @c reset_cancellation_state.
  397. */
  398. template <typename ExecutionContext, typename F,
  399. BOOST_ASIO_COMPLETION_TOKEN_FOR(typename detail::spawn_signature<
  400. result_of_t<F(basic_yield_context<
  401. typename ExecutionContext::executor_type>)>>::type)
  402. CompletionToken = default_completion_token_t<
  403. typename ExecutionContext::executor_type>>
  404. auto spawn(ExecutionContext& ctx, F&& function,
  405. CompletionToken&& token
  406. = default_completion_token_t<typename ExecutionContext::executor_type>(),
  407. constraint_t<
  408. is_convertible<ExecutionContext&, execution_context&>::value
  409. > = 0)
  410. -> decltype(
  411. async_initiate<CompletionToken,
  412. typename detail::spawn_signature<
  413. result_of_t<F(basic_yield_context<
  414. typename ExecutionContext::executor_type>)>>::type>(
  415. declval<detail::initiate_spawn<
  416. typename ExecutionContext::executor_type>>(),
  417. token, static_cast<F&&>(function)));
  418. /// Start a new stackful coroutine, inheriting the executor of another.
  419. /**
  420. * This function is used to launch a new stackful coroutine.
  421. *
  422. * @param ctx Identifies the current coroutine as a parent of the new
  423. * coroutine. This specifies that the new coroutine should inherit the executor
  424. * of the parent. For example, if the parent coroutine is executing in a
  425. * particular strand, then the new coroutine will execute in the same strand.
  426. *
  427. * @param function The coroutine function. The function must be callable the
  428. * signature:
  429. * @code void function(basic_yield_context<Executor> yield); @endcode
  430. *
  431. * @param token The @ref completion_token that will handle the notification
  432. * that the coroutine has completed. If the return type @c R of @c function is
  433. * @c void, the function signature of the completion handler must be:
  434. *
  435. * @code void handler(std::exception_ptr); @endcode
  436. * Otherwise, the function signature of the completion handler must be:
  437. * @code void handler(std::exception_ptr, R); @endcode
  438. *
  439. * @par Completion Signature
  440. * @code void(std::exception_ptr, R) @endcode
  441. * where @c R is the return type of the function object.
  442. *
  443. * @par Per-Operation Cancellation
  444. * The new thread of execution is created with a cancellation state that
  445. * supports @c cancellation_type::terminal values only. To change the
  446. * cancellation state, call the basic_yield_context member function
  447. * @c reset_cancellation_state.
  448. */
  449. template <typename Executor, typename F,
  450. BOOST_ASIO_COMPLETION_TOKEN_FOR(typename detail::spawn_signature<
  451. result_of_t<F(basic_yield_context<Executor>)>>::type)
  452. CompletionToken = default_completion_token_t<Executor>>
  453. auto spawn(const basic_yield_context<Executor>& ctx, F&& function,
  454. CompletionToken&& token = default_completion_token_t<Executor>(),
  455. constraint_t<
  456. is_executor<Executor>::value || execution::is_executor<Executor>::value
  457. > = 0)
  458. -> decltype(
  459. async_initiate<CompletionToken,
  460. typename detail::spawn_signature<
  461. result_of_t<F(basic_yield_context<Executor>)>>::type>(
  462. declval<detail::initiate_spawn<Executor>>(),
  463. token, static_cast<F&&>(function)));
  464. #if defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER) \
  465. || defined(GENERATING_DOCUMENTATION)
  466. /// Start a new stackful coroutine that executes on a given executor.
  467. /**
  468. * This function is used to launch a new stackful coroutine using the
  469. * specified stack allocator.
  470. *
  471. * @param ex Identifies the executor that will run the stackful coroutine.
  472. *
  473. * @param stack_allocator Denotes the allocator to be used to allocate the
  474. * underlying coroutine's stack. The type must satisfy the stack-allocator
  475. * concept defined by the Boost.Context library.
  476. *
  477. * @param function The coroutine function. The function must be callable the
  478. * signature:
  479. * @code void function(basic_yield_context<Executor> yield); @endcode
  480. *
  481. * @param token The @ref completion_token that will handle the notification
  482. * that the coroutine has completed. If the return type @c R of @c function is
  483. * @c void, the function signature of the completion handler must be:
  484. *
  485. * @code void handler(std::exception_ptr); @endcode
  486. * Otherwise, the function signature of the completion handler must be:
  487. * @code void handler(std::exception_ptr, R); @endcode
  488. *
  489. * @par Completion Signature
  490. * @code void(std::exception_ptr, R) @endcode
  491. * where @c R is the return type of the function object.
  492. *
  493. * @par Per-Operation Cancellation
  494. * The new thread of execution is created with a cancellation state that
  495. * supports @c cancellation_type::terminal values only. To change the
  496. * cancellation state, call the basic_yield_context member function
  497. * @c reset_cancellation_state.
  498. */
  499. template <typename Executor, typename StackAllocator, typename F,
  500. BOOST_ASIO_COMPLETION_TOKEN_FOR(typename detail::spawn_signature<
  501. result_of_t<F(basic_yield_context<Executor>)>>::type)
  502. CompletionToken = default_completion_token_t<Executor>>
  503. auto spawn(const Executor& ex, allocator_arg_t,
  504. StackAllocator&& stack_allocator, F&& function,
  505. CompletionToken&& token = default_completion_token_t<Executor>(),
  506. constraint_t<
  507. is_executor<Executor>::value || execution::is_executor<Executor>::value
  508. > = 0)
  509. -> decltype(
  510. async_initiate<CompletionToken,
  511. typename detail::spawn_signature<
  512. result_of_t<F(basic_yield_context<Executor>)>>::type>(
  513. declval<detail::initiate_spawn<Executor>>(),
  514. token, allocator_arg_t(),
  515. static_cast<StackAllocator&&>(stack_allocator),
  516. static_cast<F&&>(function)));
  517. /// Start a new stackful coroutine that executes on a given execution context.
  518. /**
  519. * This function is used to launch a new stackful coroutine.
  520. *
  521. * @param ctx Identifies the execution context that will run the stackful
  522. * coroutine.
  523. *
  524. * @param stack_allocator Denotes the allocator to be used to allocate the
  525. * underlying coroutine's stack. The type must satisfy the stack-allocator
  526. * concept defined by the Boost.Context library.
  527. *
  528. * @param function The coroutine function. The function must be callable the
  529. * signature:
  530. * @code void function(basic_yield_context<Executor> yield); @endcode
  531. *
  532. * @param token The @ref completion_token that will handle the notification
  533. * that the coroutine has completed. If the return type @c R of @c function is
  534. * @c void, the function signature of the completion handler must be:
  535. *
  536. * @code void handler(std::exception_ptr); @endcode
  537. * Otherwise, the function signature of the completion handler must be:
  538. * @code void handler(std::exception_ptr, R); @endcode
  539. *
  540. * @par Completion Signature
  541. * @code void(std::exception_ptr, R) @endcode
  542. * where @c R is the return type of the function object.
  543. *
  544. * @par Per-Operation Cancellation
  545. * The new thread of execution is created with a cancellation state that
  546. * supports @c cancellation_type::terminal values only. To change the
  547. * cancellation state, call the basic_yield_context member function
  548. * @c reset_cancellation_state.
  549. */
  550. template <typename ExecutionContext, typename StackAllocator, typename F,
  551. BOOST_ASIO_COMPLETION_TOKEN_FOR(typename detail::spawn_signature<
  552. result_of_t<F(basic_yield_context<
  553. typename ExecutionContext::executor_type>)>>::type)
  554. CompletionToken = default_completion_token_t<
  555. typename ExecutionContext::executor_type>>
  556. auto spawn(ExecutionContext& ctx, allocator_arg_t,
  557. StackAllocator&& stack_allocator, F&& function,
  558. CompletionToken&& token
  559. = default_completion_token_t<typename ExecutionContext::executor_type>(),
  560. constraint_t<
  561. is_convertible<ExecutionContext&, execution_context&>::value
  562. > = 0)
  563. -> decltype(
  564. async_initiate<CompletionToken,
  565. typename detail::spawn_signature<
  566. result_of_t<F(basic_yield_context<
  567. typename ExecutionContext::executor_type>)>>::type>(
  568. declval<detail::initiate_spawn<
  569. typename ExecutionContext::executor_type>>(),
  570. token, allocator_arg_t(),
  571. static_cast<StackAllocator&&>(stack_allocator),
  572. static_cast<F&&>(function)));
  573. /// Start a new stackful coroutine, inheriting the executor of another.
  574. /**
  575. * This function is used to launch a new stackful coroutine using the
  576. * specified stack allocator.
  577. *
  578. * @param ctx Identifies the current coroutine as a parent of the new
  579. * coroutine. This specifies that the new coroutine should inherit the
  580. * executor of the parent. For example, if the parent coroutine is executing
  581. * in a particular strand, then the new coroutine will execute in the same
  582. * strand.
  583. *
  584. * @param stack_allocator Denotes the allocator to be used to allocate the
  585. * underlying coroutine's stack. The type must satisfy the stack-allocator
  586. * concept defined by the Boost.Context library.
  587. *
  588. * @param function The coroutine function. The function must be callable the
  589. * signature:
  590. * @code void function(basic_yield_context<Executor> yield); @endcode
  591. *
  592. * @param token The @ref completion_token that will handle the notification
  593. * that the coroutine has completed. If the return type @c R of @c function is
  594. * @c void, the function signature of the completion handler must be:
  595. *
  596. * @code void handler(std::exception_ptr); @endcode
  597. * Otherwise, the function signature of the completion handler must be:
  598. * @code void handler(std::exception_ptr, R); @endcode
  599. *
  600. * @par Completion Signature
  601. * @code void(std::exception_ptr, R) @endcode
  602. * where @c R is the return type of the function object.
  603. *
  604. * @par Per-Operation Cancellation
  605. * The new thread of execution is created with a cancellation state that
  606. * supports @c cancellation_type::terminal values only. To change the
  607. * cancellation state, call the basic_yield_context member function
  608. * @c reset_cancellation_state.
  609. */
  610. template <typename Executor, typename StackAllocator, typename F,
  611. BOOST_ASIO_COMPLETION_TOKEN_FOR(typename detail::spawn_signature<
  612. result_of_t<F(basic_yield_context<Executor>)>>::type)
  613. CompletionToken = default_completion_token_t<Executor>>
  614. auto spawn(const basic_yield_context<Executor>& ctx, allocator_arg_t,
  615. StackAllocator&& stack_allocator, F&& function,
  616. CompletionToken&& token = default_completion_token_t<Executor>(),
  617. constraint_t<
  618. is_executor<Executor>::value || execution::is_executor<Executor>::value
  619. > = 0)
  620. -> decltype(
  621. async_initiate<CompletionToken,
  622. typename detail::spawn_signature<
  623. result_of_t<F(basic_yield_context<Executor>)>>::type>(
  624. declval<detail::initiate_spawn<Executor>>(),
  625. token, allocator_arg_t(),
  626. static_cast<StackAllocator&&>(stack_allocator),
  627. static_cast<F&&>(function)));
  628. #endif // defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
  629. // || defined(GENERATING_DOCUMENTATION)
  630. /*@}*/
  631. } // namespace asio
  632. } // namespace boost
  633. #include <boost/asio/detail/pop_options.hpp>
  634. #include <boost/asio/impl/spawn.hpp>
  635. #endif // BOOST_ASIO_SPAWN_HPP