spawn.hpp 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400
  1. //
  2. // impl/spawn.hpp
  3. // ~~~~~~~~~~~~~~
  4. //
  5. // Copyright (c) 2003-2023 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 ASIO_IMPL_SPAWN_HPP
  11. #define ASIO_IMPL_SPAWN_HPP
  12. #if defined(_MSC_VER) && (_MSC_VER >= 1200)
  13. # pragma once
  14. #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
  15. #include "asio/detail/config.hpp"
  16. #include <tuple>
  17. #include "asio/associated_allocator.hpp"
  18. #include "asio/associated_cancellation_slot.hpp"
  19. #include "asio/associated_executor.hpp"
  20. #include "asio/async_result.hpp"
  21. #include "asio/bind_executor.hpp"
  22. #include "asio/detail/atomic_count.hpp"
  23. #include "asio/detail/bind_handler.hpp"
  24. #include "asio/detail/handler_cont_helpers.hpp"
  25. #include "asio/detail/memory.hpp"
  26. #include "asio/detail/noncopyable.hpp"
  27. #include "asio/detail/type_traits.hpp"
  28. #include "asio/detail/utility.hpp"
  29. #include "asio/system_error.hpp"
  30. #if defined(ASIO_HAS_BOOST_CONTEXT_FIBER)
  31. # include <boost/context/fiber.hpp>
  32. #endif // defined(ASIO_HAS_BOOST_CONTEXT_FIBER)
  33. #include "asio/detail/push_options.hpp"
  34. namespace asio {
  35. namespace detail {
  36. #if !defined(ASIO_NO_EXCEPTIONS)
  37. inline void spawned_thread_rethrow(void* ex)
  38. {
  39. if (*static_cast<exception_ptr*>(ex))
  40. rethrow_exception(*static_cast<exception_ptr*>(ex));
  41. }
  42. #endif // !defined(ASIO_NO_EXCEPTIONS)
  43. #if defined(ASIO_HAS_BOOST_COROUTINE)
  44. // Spawned thread implementation using Boost.Coroutine.
  45. class spawned_coroutine_thread : public spawned_thread_base
  46. {
  47. public:
  48. #if defined(BOOST_COROUTINES_UNIDIRECT) || defined(BOOST_COROUTINES_V2)
  49. typedef boost::coroutines::pull_coroutine<void> callee_type;
  50. typedef boost::coroutines::push_coroutine<void> caller_type;
  51. #else
  52. typedef boost::coroutines::coroutine<void()> callee_type;
  53. typedef boost::coroutines::coroutine<void()> caller_type;
  54. #endif
  55. spawned_coroutine_thread(caller_type& caller)
  56. : caller_(caller),
  57. on_suspend_fn_(0),
  58. on_suspend_arg_(0)
  59. {
  60. }
  61. template <typename F>
  62. static spawned_thread_base* spawn(F&& f,
  63. const boost::coroutines::attributes& attributes,
  64. cancellation_slot parent_cancel_slot = cancellation_slot(),
  65. cancellation_state cancel_state = cancellation_state())
  66. {
  67. spawned_coroutine_thread* spawned_thread = 0;
  68. callee_type callee(entry_point<decay_t<F>>(
  69. static_cast<F&&>(f), &spawned_thread), attributes);
  70. spawned_thread->callee_.swap(callee);
  71. spawned_thread->parent_cancellation_slot_ = parent_cancel_slot;
  72. spawned_thread->cancellation_state_ = cancel_state;
  73. return spawned_thread;
  74. }
  75. template <typename F>
  76. static spawned_thread_base* spawn(F&& f,
  77. cancellation_slot parent_cancel_slot = cancellation_slot(),
  78. cancellation_state cancel_state = cancellation_state())
  79. {
  80. return spawn(static_cast<F&&>(f), boost::coroutines::attributes(),
  81. parent_cancel_slot, cancel_state);
  82. }
  83. void resume()
  84. {
  85. callee_();
  86. if (on_suspend_fn_)
  87. {
  88. void (*fn)(void*) = on_suspend_fn_;
  89. void* arg = on_suspend_arg_;
  90. on_suspend_fn_ = 0;
  91. fn(arg);
  92. }
  93. }
  94. void suspend_with(void (*fn)(void*), void* arg)
  95. {
  96. if (throw_if_cancelled_)
  97. if (!!cancellation_state_.cancelled())
  98. throw_error(asio::error::operation_aborted, "yield");
  99. has_context_switched_ = true;
  100. on_suspend_fn_ = fn;
  101. on_suspend_arg_ = arg;
  102. caller_();
  103. }
  104. void destroy()
  105. {
  106. callee_type callee;
  107. callee.swap(callee_);
  108. if (terminal_)
  109. callee();
  110. }
  111. private:
  112. template <typename Function>
  113. class entry_point
  114. {
  115. public:
  116. template <typename F>
  117. entry_point(F&& f,
  118. spawned_coroutine_thread** spawned_thread_out)
  119. : function_(static_cast<F&&>(f)),
  120. spawned_thread_out_(spawned_thread_out)
  121. {
  122. }
  123. void operator()(caller_type& caller)
  124. {
  125. Function function(static_cast<Function&&>(function_));
  126. spawned_coroutine_thread spawned_thread(caller);
  127. *spawned_thread_out_ = &spawned_thread;
  128. spawned_thread_out_ = 0;
  129. spawned_thread.suspend();
  130. #if !defined(ASIO_NO_EXCEPTIONS)
  131. try
  132. #endif // !defined(ASIO_NO_EXCEPTIONS)
  133. {
  134. function(&spawned_thread);
  135. spawned_thread.terminal_ = true;
  136. spawned_thread.suspend();
  137. }
  138. #if !defined(ASIO_NO_EXCEPTIONS)
  139. catch (const boost::coroutines::detail::forced_unwind&)
  140. {
  141. throw;
  142. }
  143. catch (...)
  144. {
  145. exception_ptr ex = current_exception();
  146. spawned_thread.terminal_ = true;
  147. spawned_thread.suspend_with(spawned_thread_rethrow, &ex);
  148. }
  149. #endif // !defined(ASIO_NO_EXCEPTIONS)
  150. }
  151. private:
  152. Function function_;
  153. spawned_coroutine_thread** spawned_thread_out_;
  154. };
  155. caller_type& caller_;
  156. callee_type callee_;
  157. void (*on_suspend_fn_)(void*);
  158. void* on_suspend_arg_;
  159. };
  160. #endif // defined(ASIO_HAS_BOOST_COROUTINE)
  161. #if defined(ASIO_HAS_BOOST_CONTEXT_FIBER)
  162. // Spawned thread implementation using Boost.Context's fiber.
  163. class spawned_fiber_thread : public spawned_thread_base
  164. {
  165. public:
  166. typedef boost::context::fiber fiber_type;
  167. spawned_fiber_thread(fiber_type&& caller)
  168. : caller_(static_cast<fiber_type&&>(caller)),
  169. on_suspend_fn_(0),
  170. on_suspend_arg_(0)
  171. {
  172. }
  173. template <typename StackAllocator, typename F>
  174. static spawned_thread_base* spawn(allocator_arg_t,
  175. StackAllocator&& stack_allocator,
  176. F&& f,
  177. cancellation_slot parent_cancel_slot = cancellation_slot(),
  178. cancellation_state cancel_state = cancellation_state())
  179. {
  180. spawned_fiber_thread* spawned_thread = 0;
  181. fiber_type callee(allocator_arg_t(),
  182. static_cast<StackAllocator&&>(stack_allocator),
  183. entry_point<decay_t<F>>(
  184. static_cast<F&&>(f), &spawned_thread));
  185. callee = fiber_type(static_cast<fiber_type&&>(callee)).resume();
  186. spawned_thread->callee_ = static_cast<fiber_type&&>(callee);
  187. spawned_thread->parent_cancellation_slot_ = parent_cancel_slot;
  188. spawned_thread->cancellation_state_ = cancel_state;
  189. return spawned_thread;
  190. }
  191. template <typename F>
  192. static spawned_thread_base* spawn(F&& f,
  193. cancellation_slot parent_cancel_slot = cancellation_slot(),
  194. cancellation_state cancel_state = cancellation_state())
  195. {
  196. return spawn(allocator_arg_t(), boost::context::fixedsize_stack(),
  197. static_cast<F&&>(f), parent_cancel_slot, cancel_state);
  198. }
  199. void resume()
  200. {
  201. callee_ = fiber_type(static_cast<fiber_type&&>(callee_)).resume();
  202. if (on_suspend_fn_)
  203. {
  204. void (*fn)(void*) = on_suspend_fn_;
  205. void* arg = on_suspend_arg_;
  206. on_suspend_fn_ = 0;
  207. fn(arg);
  208. }
  209. }
  210. void suspend_with(void (*fn)(void*), void* arg)
  211. {
  212. if (throw_if_cancelled_)
  213. if (!!cancellation_state_.cancelled())
  214. throw_error(asio::error::operation_aborted, "yield");
  215. has_context_switched_ = true;
  216. on_suspend_fn_ = fn;
  217. on_suspend_arg_ = arg;
  218. caller_ = fiber_type(static_cast<fiber_type&&>(caller_)).resume();
  219. }
  220. void destroy()
  221. {
  222. fiber_type callee = static_cast<fiber_type&&>(callee_);
  223. if (terminal_)
  224. fiber_type(static_cast<fiber_type&&>(callee)).resume();
  225. }
  226. private:
  227. template <typename Function>
  228. class entry_point
  229. {
  230. public:
  231. template <typename F>
  232. entry_point(F&& f,
  233. spawned_fiber_thread** spawned_thread_out)
  234. : function_(static_cast<F&&>(f)),
  235. spawned_thread_out_(spawned_thread_out)
  236. {
  237. }
  238. fiber_type operator()(fiber_type&& caller)
  239. {
  240. Function function(static_cast<Function&&>(function_));
  241. spawned_fiber_thread spawned_thread(
  242. static_cast<fiber_type&&>(caller));
  243. *spawned_thread_out_ = &spawned_thread;
  244. spawned_thread_out_ = 0;
  245. spawned_thread.suspend();
  246. #if !defined(ASIO_NO_EXCEPTIONS)
  247. try
  248. #endif // !defined(ASIO_NO_EXCEPTIONS)
  249. {
  250. function(&spawned_thread);
  251. spawned_thread.terminal_ = true;
  252. spawned_thread.suspend();
  253. }
  254. #if !defined(ASIO_NO_EXCEPTIONS)
  255. catch (const boost::context::detail::forced_unwind&)
  256. {
  257. throw;
  258. }
  259. catch (...)
  260. {
  261. exception_ptr ex = current_exception();
  262. spawned_thread.terminal_ = true;
  263. spawned_thread.suspend_with(spawned_thread_rethrow, &ex);
  264. }
  265. #endif // !defined(ASIO_NO_EXCEPTIONS)
  266. return static_cast<fiber_type&&>(spawned_thread.caller_);
  267. }
  268. private:
  269. Function function_;
  270. spawned_fiber_thread** spawned_thread_out_;
  271. };
  272. fiber_type caller_;
  273. fiber_type callee_;
  274. void (*on_suspend_fn_)(void*);
  275. void* on_suspend_arg_;
  276. };
  277. #endif // defined(ASIO_HAS_BOOST_CONTEXT_FIBER)
  278. #if defined(ASIO_HAS_BOOST_CONTEXT_FIBER)
  279. typedef spawned_fiber_thread default_spawned_thread_type;
  280. #elif defined(ASIO_HAS_BOOST_COROUTINE)
  281. typedef spawned_coroutine_thread default_spawned_thread_type;
  282. #else
  283. # error No spawn() implementation available
  284. #endif
  285. // Helper class to perform the initial resume on the correct executor.
  286. class spawned_thread_resumer
  287. {
  288. public:
  289. explicit spawned_thread_resumer(spawned_thread_base* spawned_thread)
  290. : spawned_thread_(spawned_thread)
  291. {
  292. }
  293. spawned_thread_resumer(spawned_thread_resumer&& other) noexcept
  294. : spawned_thread_(other.spawned_thread_)
  295. {
  296. other.spawned_thread_ = 0;
  297. }
  298. ~spawned_thread_resumer()
  299. {
  300. if (spawned_thread_)
  301. spawned_thread_->destroy();
  302. }
  303. void operator()()
  304. {
  305. spawned_thread_->attach(&spawned_thread_);
  306. spawned_thread_->resume();
  307. }
  308. private:
  309. spawned_thread_base* spawned_thread_;
  310. };
  311. // Helper class to ensure spawned threads are destroyed on the correct executor.
  312. class spawned_thread_destroyer
  313. {
  314. public:
  315. explicit spawned_thread_destroyer(spawned_thread_base* spawned_thread)
  316. : spawned_thread_(spawned_thread)
  317. {
  318. spawned_thread->detach();
  319. }
  320. spawned_thread_destroyer(spawned_thread_destroyer&& other) noexcept
  321. : spawned_thread_(other.spawned_thread_)
  322. {
  323. other.spawned_thread_ = 0;
  324. }
  325. ~spawned_thread_destroyer()
  326. {
  327. if (spawned_thread_)
  328. spawned_thread_->destroy();
  329. }
  330. void operator()()
  331. {
  332. if (spawned_thread_)
  333. {
  334. spawned_thread_->destroy();
  335. spawned_thread_ = 0;
  336. }
  337. }
  338. private:
  339. spawned_thread_base* spawned_thread_;
  340. };
  341. // Base class for all completion handlers associated with a spawned thread.
  342. template <typename Executor>
  343. class spawn_handler_base
  344. {
  345. public:
  346. typedef Executor executor_type;
  347. typedef cancellation_slot cancellation_slot_type;
  348. spawn_handler_base(const basic_yield_context<Executor>& yield)
  349. : yield_(yield),
  350. spawned_thread_(yield.spawned_thread_)
  351. {
  352. spawned_thread_->detach();
  353. }
  354. spawn_handler_base(spawn_handler_base&& other) noexcept
  355. : yield_(other.yield_),
  356. spawned_thread_(other.spawned_thread_)
  357. {
  358. other.spawned_thread_ = 0;
  359. }
  360. ~spawn_handler_base()
  361. {
  362. if (spawned_thread_)
  363. (post)(yield_.executor_, spawned_thread_destroyer(spawned_thread_));
  364. }
  365. executor_type get_executor() const noexcept
  366. {
  367. return yield_.executor_;
  368. }
  369. cancellation_slot_type get_cancellation_slot() const noexcept
  370. {
  371. return spawned_thread_->get_cancellation_slot();
  372. }
  373. void resume()
  374. {
  375. spawned_thread_resumer resumer(spawned_thread_);
  376. spawned_thread_ = 0;
  377. resumer();
  378. }
  379. protected:
  380. const basic_yield_context<Executor>& yield_;
  381. spawned_thread_base* spawned_thread_;
  382. };
  383. // Completion handlers for when basic_yield_context is used as a token.
  384. template <typename Executor, typename Signature>
  385. class spawn_handler;
  386. template <typename Executor, typename R>
  387. class spawn_handler<Executor, R()>
  388. : public spawn_handler_base<Executor>
  389. {
  390. public:
  391. typedef void return_type;
  392. struct result_type {};
  393. spawn_handler(const basic_yield_context<Executor>& yield, result_type&)
  394. : spawn_handler_base<Executor>(yield)
  395. {
  396. }
  397. void operator()()
  398. {
  399. this->resume();
  400. }
  401. static return_type on_resume(result_type&)
  402. {
  403. }
  404. };
  405. template <typename Executor, typename R>
  406. class spawn_handler<Executor, R(asio::error_code)>
  407. : public spawn_handler_base<Executor>
  408. {
  409. public:
  410. typedef void return_type;
  411. typedef asio::error_code* result_type;
  412. spawn_handler(const basic_yield_context<Executor>& yield, result_type& result)
  413. : spawn_handler_base<Executor>(yield),
  414. result_(result)
  415. {
  416. }
  417. void operator()(asio::error_code ec)
  418. {
  419. if (this->yield_.ec_)
  420. {
  421. *this->yield_.ec_ = ec;
  422. result_ = 0;
  423. }
  424. else
  425. result_ = &ec;
  426. this->resume();
  427. }
  428. static return_type on_resume(result_type& result)
  429. {
  430. if (result)
  431. throw_error(*result);
  432. }
  433. private:
  434. result_type& result_;
  435. };
  436. template <typename Executor, typename R>
  437. class spawn_handler<Executor, R(exception_ptr)>
  438. : public spawn_handler_base<Executor>
  439. {
  440. public:
  441. typedef void return_type;
  442. typedef exception_ptr* result_type;
  443. spawn_handler(const basic_yield_context<Executor>& yield, result_type& result)
  444. : spawn_handler_base<Executor>(yield),
  445. result_(result)
  446. {
  447. }
  448. void operator()(exception_ptr ex)
  449. {
  450. result_ = &ex;
  451. this->resume();
  452. }
  453. static return_type on_resume(result_type& result)
  454. {
  455. if (*result)
  456. rethrow_exception(*result);
  457. }
  458. private:
  459. result_type& result_;
  460. };
  461. template <typename Executor, typename R, typename T>
  462. class spawn_handler<Executor, R(T)>
  463. : public spawn_handler_base<Executor>
  464. {
  465. public:
  466. typedef T return_type;
  467. typedef return_type* result_type;
  468. spawn_handler(const basic_yield_context<Executor>& yield, result_type& result)
  469. : spawn_handler_base<Executor>(yield),
  470. result_(result)
  471. {
  472. }
  473. void operator()(T value)
  474. {
  475. result_ = &value;
  476. this->resume();
  477. }
  478. static return_type on_resume(result_type& result)
  479. {
  480. return static_cast<return_type&&>(*result);
  481. }
  482. private:
  483. result_type& result_;
  484. };
  485. template <typename Executor, typename R, typename T>
  486. class spawn_handler<Executor, R(asio::error_code, T)>
  487. : public spawn_handler_base<Executor>
  488. {
  489. public:
  490. typedef T return_type;
  491. struct result_type
  492. {
  493. asio::error_code* ec_;
  494. return_type* value_;
  495. };
  496. spawn_handler(const basic_yield_context<Executor>& yield, result_type& result)
  497. : spawn_handler_base<Executor>(yield),
  498. result_(result)
  499. {
  500. }
  501. void operator()(asio::error_code ec, T value)
  502. {
  503. if (this->yield_.ec_)
  504. {
  505. *this->yield_.ec_ = ec;
  506. result_.ec_ = 0;
  507. }
  508. else
  509. result_.ec_ = &ec;
  510. result_.value_ = &value;
  511. this->resume();
  512. }
  513. static return_type on_resume(result_type& result)
  514. {
  515. if (result.ec_)
  516. throw_error(*result.ec_);
  517. return static_cast<return_type&&>(*result.value_);
  518. }
  519. private:
  520. result_type& result_;
  521. };
  522. template <typename Executor, typename R, typename T>
  523. class spawn_handler<Executor, R(exception_ptr, T)>
  524. : public spawn_handler_base<Executor>
  525. {
  526. public:
  527. typedef T return_type;
  528. struct result_type
  529. {
  530. exception_ptr* ex_;
  531. return_type* value_;
  532. };
  533. spawn_handler(const basic_yield_context<Executor>& yield, result_type& result)
  534. : spawn_handler_base<Executor>(yield),
  535. result_(result)
  536. {
  537. }
  538. void operator()(exception_ptr ex, T value)
  539. {
  540. result_.ex_ = &ex;
  541. result_.value_ = &value;
  542. this->resume();
  543. }
  544. static return_type on_resume(result_type& result)
  545. {
  546. if (*result.ex_)
  547. rethrow_exception(*result.ex_);
  548. return static_cast<return_type&&>(*result.value_);
  549. }
  550. private:
  551. result_type& result_;
  552. };
  553. template <typename Executor, typename R, typename... Ts>
  554. class spawn_handler<Executor, R(Ts...)>
  555. : public spawn_handler_base<Executor>
  556. {
  557. public:
  558. typedef std::tuple<Ts...> return_type;
  559. typedef return_type* result_type;
  560. spawn_handler(const basic_yield_context<Executor>& yield, result_type& result)
  561. : spawn_handler_base<Executor>(yield),
  562. result_(result)
  563. {
  564. }
  565. template <typename... Args>
  566. void operator()(Args&&... args)
  567. {
  568. return_type value(static_cast<Args&&>(args)...);
  569. result_ = &value;
  570. this->resume();
  571. }
  572. static return_type on_resume(result_type& result)
  573. {
  574. return static_cast<return_type&&>(*result);
  575. }
  576. private:
  577. result_type& result_;
  578. };
  579. template <typename Executor, typename R, typename... Ts>
  580. class spawn_handler<Executor, R(asio::error_code, Ts...)>
  581. : public spawn_handler_base<Executor>
  582. {
  583. public:
  584. typedef std::tuple<Ts...> return_type;
  585. struct result_type
  586. {
  587. asio::error_code* ec_;
  588. return_type* value_;
  589. };
  590. spawn_handler(const basic_yield_context<Executor>& yield, result_type& result)
  591. : spawn_handler_base<Executor>(yield),
  592. result_(result)
  593. {
  594. }
  595. template <typename... Args>
  596. void operator()(asio::error_code ec,
  597. Args&&... args)
  598. {
  599. return_type value(static_cast<Args&&>(args)...);
  600. if (this->yield_.ec_)
  601. {
  602. *this->yield_.ec_ = ec;
  603. result_.ec_ = 0;
  604. }
  605. else
  606. result_.ec_ = &ec;
  607. result_.value_ = &value;
  608. this->resume();
  609. }
  610. static return_type on_resume(result_type& result)
  611. {
  612. if (result.ec_)
  613. throw_error(*result.ec_);
  614. return static_cast<return_type&&>(*result.value_);
  615. }
  616. private:
  617. result_type& result_;
  618. };
  619. template <typename Executor, typename R, typename... Ts>
  620. class spawn_handler<Executor, R(exception_ptr, Ts...)>
  621. : public spawn_handler_base<Executor>
  622. {
  623. public:
  624. typedef std::tuple<Ts...> return_type;
  625. struct result_type
  626. {
  627. exception_ptr* ex_;
  628. return_type* value_;
  629. };
  630. spawn_handler(const basic_yield_context<Executor>& yield, result_type& result)
  631. : spawn_handler_base<Executor>(yield),
  632. result_(result)
  633. {
  634. }
  635. template <typename... Args>
  636. void operator()(exception_ptr ex, Args&&... args)
  637. {
  638. return_type value(static_cast<Args&&>(args)...);
  639. result_.ex_ = &ex;
  640. result_.value_ = &value;
  641. this->resume();
  642. }
  643. static return_type on_resume(result_type& result)
  644. {
  645. if (*result.ex_)
  646. rethrow_exception(*result.ex_);
  647. return static_cast<return_type&&>(*result.value_);
  648. }
  649. private:
  650. result_type& result_;
  651. };
  652. template <typename Executor, typename Signature>
  653. inline bool asio_handler_is_continuation(spawn_handler<Executor, Signature>*)
  654. {
  655. return true;
  656. }
  657. } // namespace detail
  658. template <typename Executor, typename Signature>
  659. class async_result<basic_yield_context<Executor>, Signature>
  660. {
  661. public:
  662. typedef typename detail::spawn_handler<Executor, Signature> handler_type;
  663. typedef typename handler_type::return_type return_type;
  664. #if defined(ASIO_HAS_VARIADIC_LAMBDA_CAPTURES)
  665. template <typename Initiation, typename... InitArgs>
  666. static return_type initiate(Initiation&& init,
  667. const basic_yield_context<Executor>& yield,
  668. InitArgs&&... init_args)
  669. {
  670. typename handler_type::result_type result
  671. = typename handler_type::result_type();
  672. yield.spawned_thread_->suspend_with(
  673. [&]()
  674. {
  675. static_cast<Initiation&&>(init)(
  676. handler_type(yield, result),
  677. static_cast<InitArgs&&>(init_args)...);
  678. });
  679. return handler_type::on_resume(result);
  680. }
  681. #else // defined(ASIO_HAS_VARIADIC_LAMBDA_CAPTURES)
  682. template <typename Initiation, typename... InitArgs>
  683. struct suspend_with_helper
  684. {
  685. typename handler_type::result_type& result_;
  686. Initiation&& init_;
  687. const basic_yield_context<Executor>& yield_;
  688. std::tuple<InitArgs&&...> init_args_;
  689. template <std::size_t... I>
  690. void do_invoke(detail::index_sequence<I...>)
  691. {
  692. static_cast<Initiation&&>(init_)(
  693. handler_type(yield_, result_),
  694. static_cast<InitArgs&&>(std::get<I>(init_args_))...);
  695. }
  696. void operator()()
  697. {
  698. this->do_invoke(detail::make_index_sequence<sizeof...(InitArgs)>());
  699. }
  700. };
  701. template <typename Initiation, typename... InitArgs>
  702. static return_type initiate(Initiation&& init,
  703. const basic_yield_context<Executor>& yield,
  704. InitArgs&&... init_args)
  705. {
  706. typename handler_type::result_type result
  707. = typename handler_type::result_type();
  708. yield.spawned_thread_->suspend_with(
  709. suspend_with_helper<Initiation, InitArgs...>{
  710. result, static_cast<Initiation&&>(init), yield,
  711. std::tuple<InitArgs&&...>(
  712. static_cast<InitArgs&&>(init_args)...)});
  713. return handler_type::on_resume(result);
  714. }
  715. #endif // defined(ASIO_HAS_VARIADIC_LAMBDA_CAPTURES)
  716. };
  717. namespace detail {
  718. template <typename Executor, typename Function, typename Handler>
  719. class spawn_entry_point
  720. {
  721. public:
  722. template <typename F, typename H>
  723. spawn_entry_point(const Executor& ex,
  724. F&& f, H&& h)
  725. : executor_(ex),
  726. function_(static_cast<F&&>(f)),
  727. handler_(static_cast<H&&>(h)),
  728. work_(handler_, executor_)
  729. {
  730. }
  731. void operator()(spawned_thread_base* spawned_thread)
  732. {
  733. const basic_yield_context<Executor> yield(spawned_thread, executor_);
  734. this->call(yield,
  735. void_type<result_of_t<Function(basic_yield_context<Executor>)>>());
  736. }
  737. private:
  738. void call(const basic_yield_context<Executor>& yield, void_type<void>)
  739. {
  740. #if !defined(ASIO_NO_EXCEPTIONS)
  741. try
  742. #endif // !defined(ASIO_NO_EXCEPTIONS)
  743. {
  744. function_(yield);
  745. if (!yield.spawned_thread_->has_context_switched())
  746. (post)(yield);
  747. detail::binder1<Handler, exception_ptr>
  748. handler(handler_, exception_ptr());
  749. work_.complete(handler, handler.handler_);
  750. }
  751. #if !defined(ASIO_NO_EXCEPTIONS)
  752. # if defined(ASIO_HAS_BOOST_CONTEXT_FIBER)
  753. catch (const boost::context::detail::forced_unwind&)
  754. {
  755. throw;
  756. }
  757. # endif // defined(ASIO_HAS_BOOST_CONTEXT_FIBER)
  758. # if defined(ASIO_HAS_BOOST_COROUTINE)
  759. catch (const boost::coroutines::detail::forced_unwind&)
  760. {
  761. throw;
  762. }
  763. # endif // defined(ASIO_HAS_BOOST_COROUTINE)
  764. catch (...)
  765. {
  766. exception_ptr ex = current_exception();
  767. if (!yield.spawned_thread_->has_context_switched())
  768. (post)(yield);
  769. detail::binder1<Handler, exception_ptr> handler(handler_, ex);
  770. work_.complete(handler, handler.handler_);
  771. }
  772. #endif // !defined(ASIO_NO_EXCEPTIONS)
  773. }
  774. template <typename T>
  775. void call(const basic_yield_context<Executor>& yield, void_type<T>)
  776. {
  777. #if !defined(ASIO_NO_EXCEPTIONS)
  778. try
  779. #endif // !defined(ASIO_NO_EXCEPTIONS)
  780. {
  781. T result(function_(yield));
  782. if (!yield.spawned_thread_->has_context_switched())
  783. (post)(yield);
  784. detail::binder2<Handler, exception_ptr, T>
  785. handler(handler_, exception_ptr(), static_cast<T&&>(result));
  786. work_.complete(handler, handler.handler_);
  787. }
  788. #if !defined(ASIO_NO_EXCEPTIONS)
  789. # if defined(ASIO_HAS_BOOST_CONTEXT_FIBER)
  790. catch (const boost::context::detail::forced_unwind&)
  791. {
  792. throw;
  793. }
  794. # endif // defined(ASIO_HAS_BOOST_CONTEXT_FIBER)
  795. # if defined(ASIO_HAS_BOOST_COROUTINE)
  796. catch (const boost::coroutines::detail::forced_unwind&)
  797. {
  798. throw;
  799. }
  800. # endif // defined(ASIO_HAS_BOOST_COROUTINE)
  801. catch (...)
  802. {
  803. exception_ptr ex = current_exception();
  804. if (!yield.spawned_thread_->has_context_switched())
  805. (post)(yield);
  806. detail::binder2<Handler, exception_ptr, T> handler(handler_, ex, T());
  807. work_.complete(handler, handler.handler_);
  808. }
  809. #endif // !defined(ASIO_NO_EXCEPTIONS)
  810. }
  811. Executor executor_;
  812. Function function_;
  813. Handler handler_;
  814. handler_work<Handler, Executor> work_;
  815. };
  816. struct spawn_cancellation_signal_emitter
  817. {
  818. cancellation_signal* signal_;
  819. cancellation_type_t type_;
  820. void operator()()
  821. {
  822. signal_->emit(type_);
  823. }
  824. };
  825. template <typename Handler, typename Executor, typename = void>
  826. class spawn_cancellation_handler
  827. {
  828. public:
  829. spawn_cancellation_handler(const Handler&, const Executor& ex)
  830. : ex_(ex)
  831. {
  832. }
  833. cancellation_slot slot()
  834. {
  835. return signal_.slot();
  836. }
  837. void operator()(cancellation_type_t type)
  838. {
  839. spawn_cancellation_signal_emitter emitter = { &signal_, type };
  840. (dispatch)(ex_, emitter);
  841. }
  842. private:
  843. cancellation_signal signal_;
  844. Executor ex_;
  845. };
  846. template <typename Handler, typename Executor>
  847. class spawn_cancellation_handler<Handler, Executor,
  848. enable_if_t<
  849. is_same<
  850. typename associated_executor<Handler,
  851. Executor>::asio_associated_executor_is_unspecialised,
  852. void
  853. >::value
  854. >>
  855. {
  856. public:
  857. spawn_cancellation_handler(const Handler&, const Executor&)
  858. {
  859. }
  860. cancellation_slot slot()
  861. {
  862. return signal_.slot();
  863. }
  864. void operator()(cancellation_type_t type)
  865. {
  866. signal_.emit(type);
  867. }
  868. private:
  869. cancellation_signal signal_;
  870. };
  871. template <typename Executor>
  872. class initiate_spawn
  873. {
  874. public:
  875. typedef Executor executor_type;
  876. explicit initiate_spawn(const executor_type& ex)
  877. : executor_(ex)
  878. {
  879. }
  880. executor_type get_executor() const noexcept
  881. {
  882. return executor_;
  883. }
  884. template <typename Handler, typename F>
  885. void operator()(Handler&& handler,
  886. F&& f) const
  887. {
  888. typedef decay_t<Handler> handler_type;
  889. typedef decay_t<F> function_type;
  890. typedef spawn_cancellation_handler<
  891. handler_type, Executor> cancel_handler_type;
  892. associated_cancellation_slot_t<handler_type> slot
  893. = asio::get_associated_cancellation_slot(handler);
  894. cancel_handler_type* cancel_handler = slot.is_connected()
  895. ? &slot.template emplace<cancel_handler_type>(handler, executor_)
  896. : 0;
  897. cancellation_slot proxy_slot(
  898. cancel_handler
  899. ? cancel_handler->slot()
  900. : cancellation_slot());
  901. cancellation_state cancel_state(proxy_slot);
  902. (dispatch)(executor_,
  903. spawned_thread_resumer(
  904. default_spawned_thread_type::spawn(
  905. spawn_entry_point<Executor, function_type, handler_type>(
  906. executor_, static_cast<F&&>(f),
  907. static_cast<Handler&&>(handler)),
  908. proxy_slot, cancel_state)));
  909. }
  910. #if defined(ASIO_HAS_BOOST_CONTEXT_FIBER)
  911. template <typename Handler, typename StackAllocator, typename F>
  912. void operator()(Handler&& handler, allocator_arg_t,
  913. StackAllocator&& stack_allocator,
  914. F&& f) const
  915. {
  916. typedef decay_t<Handler> handler_type;
  917. typedef decay_t<F> function_type;
  918. typedef spawn_cancellation_handler<
  919. handler_type, Executor> cancel_handler_type;
  920. associated_cancellation_slot_t<handler_type> slot
  921. = asio::get_associated_cancellation_slot(handler);
  922. cancel_handler_type* cancel_handler = slot.is_connected()
  923. ? &slot.template emplace<cancel_handler_type>(handler, executor_)
  924. : 0;
  925. cancellation_slot proxy_slot(
  926. cancel_handler
  927. ? cancel_handler->slot()
  928. : cancellation_slot());
  929. cancellation_state cancel_state(proxy_slot);
  930. (dispatch)(executor_,
  931. spawned_thread_resumer(
  932. spawned_fiber_thread::spawn(allocator_arg_t(),
  933. static_cast<StackAllocator&&>(stack_allocator),
  934. spawn_entry_point<Executor, function_type, handler_type>(
  935. executor_, static_cast<F&&>(f),
  936. static_cast<Handler&&>(handler)),
  937. proxy_slot, cancel_state)));
  938. }
  939. #endif // defined(ASIO_HAS_BOOST_CONTEXT_FIBER)
  940. private:
  941. executor_type executor_;
  942. };
  943. } // namespace detail
  944. template <typename Executor, typename F,
  945. ASIO_COMPLETION_TOKEN_FOR(typename detail::spawn_signature<
  946. result_of_t<F(basic_yield_context<Executor>)>>::type) CompletionToken>
  947. inline auto spawn(const Executor& ex, F&& function, CompletionToken&& token,
  948. #if defined(ASIO_HAS_BOOST_COROUTINE)
  949. constraint_t<
  950. !is_same<
  951. decay_t<CompletionToken>,
  952. boost::coroutines::attributes
  953. >::value
  954. >,
  955. #endif // defined(ASIO_HAS_BOOST_COROUTINE)
  956. constraint_t<
  957. is_executor<Executor>::value || execution::is_executor<Executor>::value
  958. >)
  959. -> decltype(
  960. async_initiate<CompletionToken,
  961. typename detail::spawn_signature<
  962. result_of_t<F(basic_yield_context<Executor>)>>::type>(
  963. declval<detail::initiate_spawn<Executor>>(),
  964. token, static_cast<F&&>(function)))
  965. {
  966. return async_initiate<CompletionToken,
  967. typename detail::spawn_signature<
  968. result_of_t<F(basic_yield_context<Executor>)>>::type>(
  969. detail::initiate_spawn<Executor>(ex),
  970. token, static_cast<F&&>(function));
  971. }
  972. template <typename ExecutionContext, typename F,
  973. ASIO_COMPLETION_TOKEN_FOR(typename detail::spawn_signature<
  974. result_of_t<F(basic_yield_context<
  975. typename ExecutionContext::executor_type>)>>::type) CompletionToken>
  976. inline auto spawn(ExecutionContext& ctx, F&& function, CompletionToken&& token,
  977. #if defined(ASIO_HAS_BOOST_COROUTINE)
  978. constraint_t<
  979. !is_same<
  980. decay_t<CompletionToken>,
  981. boost::coroutines::attributes
  982. >::value
  983. >,
  984. #endif // defined(ASIO_HAS_BOOST_COROUTINE)
  985. constraint_t<
  986. is_convertible<ExecutionContext&, execution_context&>::value
  987. >)
  988. -> decltype(
  989. async_initiate<CompletionToken,
  990. typename detail::spawn_signature<
  991. result_of_t<F(basic_yield_context<
  992. typename ExecutionContext::executor_type>)>>::type>(
  993. declval<detail::initiate_spawn<
  994. typename ExecutionContext::executor_type>>(),
  995. token, static_cast<F&&>(function)))
  996. {
  997. return (spawn)(ctx.get_executor(), static_cast<F&&>(function),
  998. static_cast<CompletionToken&&>(token));
  999. }
  1000. template <typename Executor, typename F,
  1001. ASIO_COMPLETION_TOKEN_FOR(typename detail::spawn_signature<
  1002. result_of_t<F(basic_yield_context<Executor>)>>::type)
  1003. CompletionToken>
  1004. inline auto spawn(const basic_yield_context<Executor>& ctx,
  1005. F&& function, CompletionToken&& token,
  1006. #if defined(ASIO_HAS_BOOST_COROUTINE)
  1007. constraint_t<
  1008. !is_same<
  1009. decay_t<CompletionToken>,
  1010. boost::coroutines::attributes
  1011. >::value
  1012. >,
  1013. #endif // defined(ASIO_HAS_BOOST_COROUTINE)
  1014. constraint_t<
  1015. is_executor<Executor>::value || execution::is_executor<Executor>::value
  1016. >)
  1017. -> decltype(
  1018. async_initiate<CompletionToken,
  1019. typename detail::spawn_signature<
  1020. result_of_t<F(basic_yield_context<Executor>)>>::type>(
  1021. declval<detail::initiate_spawn<Executor>>(),
  1022. token, static_cast<F&&>(function)))
  1023. {
  1024. return (spawn)(ctx.get_executor(), static_cast<F&&>(function),
  1025. static_cast<CompletionToken&&>(token));
  1026. }
  1027. #if defined(ASIO_HAS_BOOST_CONTEXT_FIBER)
  1028. template <typename Executor, typename StackAllocator, typename F,
  1029. ASIO_COMPLETION_TOKEN_FOR(typename detail::spawn_signature<
  1030. result_of_t<F(basic_yield_context<Executor>)>>::type)
  1031. CompletionToken>
  1032. inline auto spawn(const Executor& ex, allocator_arg_t,
  1033. StackAllocator&& stack_allocator, F&& function, CompletionToken&& token,
  1034. constraint_t<
  1035. is_executor<Executor>::value || execution::is_executor<Executor>::value
  1036. >)
  1037. -> decltype(
  1038. async_initiate<CompletionToken,
  1039. typename detail::spawn_signature<
  1040. result_of_t<F(basic_yield_context<Executor>)>>::type>(
  1041. declval<detail::initiate_spawn<Executor>>(),
  1042. token, allocator_arg_t(),
  1043. static_cast<StackAllocator&&>(stack_allocator),
  1044. static_cast<F&&>(function)))
  1045. {
  1046. return async_initiate<CompletionToken,
  1047. typename detail::spawn_signature<
  1048. result_of_t<F(basic_yield_context<Executor>)>>::type>(
  1049. detail::initiate_spawn<Executor>(ex), token, allocator_arg_t(),
  1050. static_cast<StackAllocator&&>(stack_allocator),
  1051. static_cast<F&&>(function));
  1052. }
  1053. template <typename ExecutionContext, typename StackAllocator, typename F,
  1054. ASIO_COMPLETION_TOKEN_FOR(typename detail::spawn_signature<
  1055. result_of_t<F(basic_yield_context<
  1056. typename ExecutionContext::executor_type>)>>::type) CompletionToken>
  1057. inline auto spawn(ExecutionContext& ctx, allocator_arg_t,
  1058. StackAllocator&& stack_allocator, F&& function, CompletionToken&& token,
  1059. constraint_t<
  1060. is_convertible<ExecutionContext&, execution_context&>::value
  1061. >)
  1062. -> decltype(
  1063. async_initiate<CompletionToken,
  1064. typename detail::spawn_signature<
  1065. result_of_t<F(basic_yield_context<
  1066. typename ExecutionContext::executor_type>)>>::type>(
  1067. declval<detail::initiate_spawn<
  1068. typename ExecutionContext::executor_type>>(),
  1069. token, allocator_arg_t(),
  1070. static_cast<StackAllocator&&>(stack_allocator),
  1071. static_cast<F&&>(function)))
  1072. {
  1073. return (spawn)(ctx.get_executor(), allocator_arg_t(),
  1074. static_cast<StackAllocator&&>(stack_allocator),
  1075. static_cast<F&&>(function), static_cast<CompletionToken&&>(token));
  1076. }
  1077. template <typename Executor, typename StackAllocator, typename F,
  1078. ASIO_COMPLETION_TOKEN_FOR(typename detail::spawn_signature<
  1079. result_of_t<F(basic_yield_context<Executor>)>>::type) CompletionToken>
  1080. inline auto spawn(const basic_yield_context<Executor>& ctx, allocator_arg_t,
  1081. StackAllocator&& stack_allocator, F&& function, CompletionToken&& token,
  1082. constraint_t<
  1083. is_executor<Executor>::value || execution::is_executor<Executor>::value
  1084. >)
  1085. -> decltype(
  1086. async_initiate<CompletionToken,
  1087. typename detail::spawn_signature<
  1088. result_of_t<F(basic_yield_context<Executor>)>>::type>(
  1089. declval<detail::initiate_spawn<Executor>>(), token,
  1090. allocator_arg_t(), static_cast<StackAllocator&&>(stack_allocator),
  1091. static_cast<F&&>(function)))
  1092. {
  1093. return (spawn)(ctx.get_executor(), allocator_arg_t(),
  1094. static_cast<StackAllocator&&>(stack_allocator),
  1095. static_cast<F&&>(function), static_cast<CompletionToken&&>(token));
  1096. }
  1097. #endif // defined(ASIO_HAS_BOOST_CONTEXT_FIBER)
  1098. #if defined(ASIO_HAS_BOOST_COROUTINE)
  1099. namespace detail {
  1100. template <typename Executor, typename Function, typename Handler>
  1101. class old_spawn_entry_point
  1102. {
  1103. public:
  1104. template <typename F, typename H>
  1105. old_spawn_entry_point(const Executor& ex, F&& f, H&& h)
  1106. : executor_(ex),
  1107. function_(static_cast<F&&>(f)),
  1108. handler_(static_cast<H&&>(h))
  1109. {
  1110. }
  1111. void operator()(spawned_thread_base* spawned_thread)
  1112. {
  1113. const basic_yield_context<Executor> yield(spawned_thread, executor_);
  1114. this->call(yield,
  1115. void_type<result_of_t<Function(basic_yield_context<Executor>)>>());
  1116. }
  1117. private:
  1118. void call(const basic_yield_context<Executor>& yield, void_type<void>)
  1119. {
  1120. function_(yield);
  1121. static_cast<Handler&&>(handler_)();
  1122. }
  1123. template <typename T>
  1124. void call(const basic_yield_context<Executor>& yield, void_type<T>)
  1125. {
  1126. static_cast<Handler&&>(handler_)(function_(yield));
  1127. }
  1128. Executor executor_;
  1129. Function function_;
  1130. Handler handler_;
  1131. };
  1132. inline void default_spawn_handler() {}
  1133. } // namespace detail
  1134. template <typename Function>
  1135. inline void spawn(Function&& function,
  1136. const boost::coroutines::attributes& attributes)
  1137. {
  1138. associated_executor_t<decay_t<Function>> ex(
  1139. (get_associated_executor)(function));
  1140. asio::spawn(ex, static_cast<Function&&>(function), attributes);
  1141. }
  1142. template <typename Handler, typename Function>
  1143. void spawn(Handler&& handler, Function&& function,
  1144. const boost::coroutines::attributes& attributes,
  1145. constraint_t<
  1146. !is_executor<decay_t<Handler>>::value &&
  1147. !execution::is_executor<decay_t<Handler>>::value &&
  1148. !is_convertible<Handler&, execution_context&>::value>)
  1149. {
  1150. typedef associated_executor_t<decay_t<Handler>> executor_type;
  1151. executor_type ex((get_associated_executor)(handler));
  1152. (dispatch)(ex,
  1153. detail::spawned_thread_resumer(
  1154. detail::spawned_coroutine_thread::spawn(
  1155. detail::old_spawn_entry_point<executor_type,
  1156. decay_t<Function>, void (*)()>(
  1157. ex, static_cast<Function&&>(function),
  1158. &detail::default_spawn_handler), attributes)));
  1159. }
  1160. template <typename Executor, typename Function>
  1161. void spawn(basic_yield_context<Executor> ctx, Function&& function,
  1162. const boost::coroutines::attributes& attributes)
  1163. {
  1164. (dispatch)(ctx.get_executor(),
  1165. detail::spawned_thread_resumer(
  1166. detail::spawned_coroutine_thread::spawn(
  1167. detail::old_spawn_entry_point<Executor,
  1168. decay_t<Function>, void (*)()>(
  1169. ctx.get_executor(), static_cast<Function&&>(function),
  1170. &detail::default_spawn_handler), attributes)));
  1171. }
  1172. template <typename Function, typename Executor>
  1173. inline void spawn(const Executor& ex, Function&& function,
  1174. const boost::coroutines::attributes& attributes,
  1175. constraint_t<
  1176. is_executor<Executor>::value || execution::is_executor<Executor>::value
  1177. >)
  1178. {
  1179. asio::spawn(asio::strand<Executor>(ex),
  1180. static_cast<Function&&>(function), attributes);
  1181. }
  1182. template <typename Function, typename Executor>
  1183. inline void spawn(const strand<Executor>& ex, Function&& function,
  1184. const boost::coroutines::attributes& attributes)
  1185. {
  1186. asio::spawn(asio::bind_executor(
  1187. ex, &detail::default_spawn_handler),
  1188. static_cast<Function&&>(function), attributes);
  1189. }
  1190. #if !defined(ASIO_NO_TS_EXECUTORS)
  1191. template <typename Function>
  1192. inline void spawn(const asio::io_context::strand& s, Function&& function,
  1193. const boost::coroutines::attributes& attributes)
  1194. {
  1195. asio::spawn(asio::bind_executor(
  1196. s, &detail::default_spawn_handler),
  1197. static_cast<Function&&>(function), attributes);
  1198. }
  1199. #endif // !defined(ASIO_NO_TS_EXECUTORS)
  1200. template <typename Function, typename ExecutionContext>
  1201. inline void spawn(ExecutionContext& ctx, Function&& function,
  1202. const boost::coroutines::attributes& attributes,
  1203. constraint_t<
  1204. is_convertible<ExecutionContext&, execution_context&>::value
  1205. >)
  1206. {
  1207. asio::spawn(ctx.get_executor(),
  1208. static_cast<Function&&>(function), attributes);
  1209. }
  1210. #endif // defined(ASIO_HAS_BOOST_COROUTINE)
  1211. } // namespace asio
  1212. #include "asio/detail/pop_options.hpp"
  1213. #endif // ASIO_IMPL_SPAWN_HPP