spawn.hpp 30 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118
  1. //
  2. // impl/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_IMPL_SPAWN_HPP
  11. #define BOOST_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 <boost/asio/detail/config.hpp>
  16. #include <tuple>
  17. #include <boost/asio/associated_allocator.hpp>
  18. #include <boost/asio/associated_cancellation_slot.hpp>
  19. #include <boost/asio/associated_executor.hpp>
  20. #include <boost/asio/async_result.hpp>
  21. #include <boost/asio/bind_executor.hpp>
  22. #include <boost/asio/detail/atomic_count.hpp>
  23. #include <boost/asio/detail/bind_handler.hpp>
  24. #include <boost/asio/detail/handler_cont_helpers.hpp>
  25. #include <boost/asio/detail/memory.hpp>
  26. #include <boost/asio/detail/noncopyable.hpp>
  27. #include <boost/asio/detail/type_traits.hpp>
  28. #include <boost/asio/detail/utility.hpp>
  29. #include <boost/asio/disposition.hpp>
  30. #include <boost/asio/error.hpp>
  31. #include <boost/system/system_error.hpp>
  32. #if defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
  33. # include <boost/context/fiber.hpp>
  34. #endif // defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
  35. #include <boost/asio/detail/push_options.hpp>
  36. namespace boost {
  37. namespace asio {
  38. namespace detail {
  39. #if !defined(BOOST_ASIO_NO_EXCEPTIONS)
  40. inline void spawned_thread_rethrow(void* ex)
  41. {
  42. if (*static_cast<exception_ptr*>(ex))
  43. rethrow_exception(*static_cast<exception_ptr*>(ex));
  44. }
  45. #endif // !defined(BOOST_ASIO_NO_EXCEPTIONS)
  46. #if defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
  47. // Spawned thread implementation using Boost.Context's fiber.
  48. class spawned_fiber_thread : public spawned_thread_base
  49. {
  50. public:
  51. typedef boost::context::fiber fiber_type;
  52. spawned_fiber_thread(fiber_type&& caller)
  53. : caller_(static_cast<fiber_type&&>(caller)),
  54. on_suspend_fn_(0),
  55. on_suspend_arg_(0)
  56. {
  57. }
  58. template <typename StackAllocator, typename F>
  59. static spawned_thread_base* spawn(allocator_arg_t,
  60. StackAllocator&& stack_allocator,
  61. F&& f,
  62. cancellation_slot parent_cancel_slot = cancellation_slot(),
  63. cancellation_state cancel_state = cancellation_state())
  64. {
  65. spawned_fiber_thread* spawned_thread = 0;
  66. fiber_type callee(allocator_arg_t(),
  67. static_cast<StackAllocator&&>(stack_allocator),
  68. entry_point<decay_t<F>>(
  69. static_cast<F&&>(f), &spawned_thread));
  70. callee = fiber_type(static_cast<fiber_type&&>(callee)).resume();
  71. spawned_thread->callee_ = static_cast<fiber_type&&>(callee);
  72. spawned_thread->parent_cancellation_slot_ = parent_cancel_slot;
  73. spawned_thread->cancellation_state_ = cancel_state;
  74. return spawned_thread;
  75. }
  76. template <typename F>
  77. static spawned_thread_base* spawn(F&& f,
  78. cancellation_slot parent_cancel_slot = cancellation_slot(),
  79. cancellation_state cancel_state = cancellation_state())
  80. {
  81. return spawn(allocator_arg_t(), boost::context::fixedsize_stack(),
  82. static_cast<F&&>(f), parent_cancel_slot, cancel_state);
  83. }
  84. void resume()
  85. {
  86. callee_ = fiber_type(static_cast<fiber_type&&>(callee_)).resume();
  87. if (on_suspend_fn_)
  88. {
  89. void (*fn)(void*) = on_suspend_fn_;
  90. void* arg = on_suspend_arg_;
  91. on_suspend_fn_ = 0;
  92. fn(arg);
  93. }
  94. }
  95. void suspend_with(void (*fn)(void*), void* arg)
  96. {
  97. if (throw_if_cancelled_)
  98. if (!!cancellation_state_.cancelled())
  99. throw_error(boost::asio::error::operation_aborted, "yield");
  100. has_context_switched_ = true;
  101. on_suspend_fn_ = fn;
  102. on_suspend_arg_ = arg;
  103. caller_ = fiber_type(static_cast<fiber_type&&>(caller_)).resume();
  104. }
  105. void destroy()
  106. {
  107. fiber_type callee = static_cast<fiber_type&&>(callee_);
  108. if (terminal_)
  109. fiber_type(static_cast<fiber_type&&>(callee)).resume();
  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_fiber_thread** spawned_thread_out)
  119. : function_(static_cast<F&&>(f)),
  120. spawned_thread_out_(spawned_thread_out)
  121. {
  122. }
  123. fiber_type operator()(fiber_type&& caller)
  124. {
  125. Function function(static_cast<Function&&>(function_));
  126. spawned_fiber_thread spawned_thread(
  127. static_cast<fiber_type&&>(caller));
  128. *spawned_thread_out_ = &spawned_thread;
  129. spawned_thread_out_ = 0;
  130. spawned_thread.suspend();
  131. #if !defined(BOOST_ASIO_NO_EXCEPTIONS)
  132. try
  133. #endif // !defined(BOOST_ASIO_NO_EXCEPTIONS)
  134. {
  135. function(&spawned_thread);
  136. spawned_thread.terminal_ = true;
  137. spawned_thread.suspend();
  138. }
  139. #if !defined(BOOST_ASIO_NO_EXCEPTIONS)
  140. catch (const boost::context::detail::forced_unwind&)
  141. {
  142. throw;
  143. }
  144. catch (...)
  145. {
  146. exception_ptr ex = current_exception();
  147. spawned_thread.terminal_ = true;
  148. spawned_thread.suspend_with(spawned_thread_rethrow, &ex);
  149. }
  150. #endif // !defined(BOOST_ASIO_NO_EXCEPTIONS)
  151. return static_cast<fiber_type&&>(spawned_thread.caller_);
  152. }
  153. private:
  154. Function function_;
  155. spawned_fiber_thread** spawned_thread_out_;
  156. };
  157. fiber_type caller_;
  158. fiber_type callee_;
  159. void (*on_suspend_fn_)(void*);
  160. void* on_suspend_arg_;
  161. };
  162. #endif // defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
  163. #if defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
  164. typedef spawned_fiber_thread default_spawned_thread_type;
  165. #else
  166. # error No spawn() implementation available
  167. #endif
  168. // Helper class to perform the initial resume on the correct executor.
  169. class spawned_thread_resumer
  170. {
  171. public:
  172. explicit spawned_thread_resumer(spawned_thread_base* spawned_thread)
  173. : spawned_thread_(spawned_thread)
  174. {
  175. }
  176. spawned_thread_resumer(spawned_thread_resumer&& other) noexcept
  177. : spawned_thread_(other.spawned_thread_)
  178. {
  179. other.spawned_thread_ = 0;
  180. }
  181. ~spawned_thread_resumer()
  182. {
  183. if (spawned_thread_)
  184. spawned_thread_->destroy();
  185. }
  186. void operator()()
  187. {
  188. spawned_thread_->attach(&spawned_thread_);
  189. spawned_thread_->resume();
  190. }
  191. private:
  192. spawned_thread_base* spawned_thread_;
  193. };
  194. // Helper class to ensure spawned threads are destroyed on the correct executor.
  195. class spawned_thread_destroyer
  196. {
  197. public:
  198. explicit spawned_thread_destroyer(spawned_thread_base* spawned_thread)
  199. : spawned_thread_(spawned_thread)
  200. {
  201. spawned_thread->detach();
  202. }
  203. spawned_thread_destroyer(spawned_thread_destroyer&& other) noexcept
  204. : spawned_thread_(other.spawned_thread_)
  205. {
  206. other.spawned_thread_ = 0;
  207. }
  208. ~spawned_thread_destroyer()
  209. {
  210. if (spawned_thread_)
  211. spawned_thread_->destroy();
  212. }
  213. void operator()()
  214. {
  215. if (spawned_thread_)
  216. {
  217. spawned_thread_->destroy();
  218. spawned_thread_ = 0;
  219. }
  220. }
  221. private:
  222. spawned_thread_base* spawned_thread_;
  223. };
  224. // Base class for all completion handlers associated with a spawned thread.
  225. template <typename Executor>
  226. class spawn_handler_base
  227. {
  228. public:
  229. typedef Executor executor_type;
  230. typedef cancellation_slot cancellation_slot_type;
  231. spawn_handler_base(const basic_yield_context<Executor>& yield)
  232. : yield_(yield),
  233. spawned_thread_(yield.spawned_thread_)
  234. {
  235. spawned_thread_->detach();
  236. }
  237. spawn_handler_base(spawn_handler_base&& other) noexcept
  238. : yield_(other.yield_),
  239. spawned_thread_(other.spawned_thread_)
  240. {
  241. other.spawned_thread_ = 0;
  242. }
  243. ~spawn_handler_base()
  244. {
  245. if (spawned_thread_)
  246. (post)(yield_.executor_, spawned_thread_destroyer(spawned_thread_));
  247. }
  248. executor_type get_executor() const noexcept
  249. {
  250. return yield_.executor_;
  251. }
  252. cancellation_slot_type get_cancellation_slot() const noexcept
  253. {
  254. return spawned_thread_->get_cancellation_slot();
  255. }
  256. void resume()
  257. {
  258. spawned_thread_resumer resumer(spawned_thread_);
  259. spawned_thread_ = 0;
  260. resumer();
  261. }
  262. protected:
  263. const basic_yield_context<Executor>& yield_;
  264. spawned_thread_base* spawned_thread_;
  265. };
  266. // Completion handlers for when basic_yield_context is used as a token.
  267. template <typename Executor, typename Signature, typename = void>
  268. class spawn_handler;
  269. template <typename Executor, typename R>
  270. class spawn_handler<Executor, R()>
  271. : public spawn_handler_base<Executor>
  272. {
  273. public:
  274. typedef void return_type;
  275. struct result_type {};
  276. spawn_handler(const basic_yield_context<Executor>& yield, result_type&)
  277. : spawn_handler_base<Executor>(yield)
  278. {
  279. }
  280. void operator()()
  281. {
  282. this->resume();
  283. }
  284. static return_type on_resume(result_type&)
  285. {
  286. }
  287. };
  288. template <typename Executor, typename R>
  289. class spawn_handler<Executor, R(boost::system::error_code)>
  290. : public spawn_handler_base<Executor>
  291. {
  292. public:
  293. typedef void return_type;
  294. typedef boost::system::error_code* result_type;
  295. spawn_handler(const basic_yield_context<Executor>& yield, result_type& result)
  296. : spawn_handler_base<Executor>(yield),
  297. result_(result)
  298. {
  299. }
  300. void operator()(boost::system::error_code ec)
  301. {
  302. if (this->yield_.ec_)
  303. {
  304. *this->yield_.ec_ = ec;
  305. result_ = 0;
  306. }
  307. else
  308. result_ = &ec;
  309. this->resume();
  310. }
  311. static return_type on_resume(result_type& result)
  312. {
  313. if (result)
  314. throw_error(*result);
  315. }
  316. private:
  317. result_type& result_;
  318. };
  319. template <typename Executor, typename R, typename Disposition>
  320. class spawn_handler<Executor, R(Disposition),
  321. enable_if_t<is_disposition<Disposition>::value>
  322. > : public spawn_handler_base<Executor>
  323. {
  324. public:
  325. typedef void return_type;
  326. typedef Disposition* result_type;
  327. spawn_handler(const basic_yield_context<Executor>& yield, result_type& result)
  328. : spawn_handler_base<Executor>(yield),
  329. result_(result)
  330. {
  331. }
  332. void operator()(Disposition d)
  333. {
  334. result_ = detail::addressof(d);
  335. this->resume();
  336. }
  337. static return_type on_resume(result_type& result)
  338. {
  339. if (*result != no_error)
  340. boost::asio::throw_exception(static_cast<Disposition&&>(*result));
  341. }
  342. private:
  343. result_type& result_;
  344. };
  345. template <typename Executor, typename R, typename T>
  346. class spawn_handler<Executor, R(T),
  347. enable_if_t<!is_disposition<T>::value>
  348. > : public spawn_handler_base<Executor>
  349. {
  350. public:
  351. typedef T return_type;
  352. typedef return_type* result_type;
  353. spawn_handler(const basic_yield_context<Executor>& yield, result_type& result)
  354. : spawn_handler_base<Executor>(yield),
  355. result_(result)
  356. {
  357. }
  358. void operator()(T value)
  359. {
  360. result_ = detail::addressof(value);
  361. this->resume();
  362. }
  363. static return_type on_resume(result_type& result)
  364. {
  365. return static_cast<return_type&&>(*result);
  366. }
  367. private:
  368. result_type& result_;
  369. };
  370. template <typename Executor, typename R, typename T>
  371. class spawn_handler<Executor, R(boost::system::error_code, T)>
  372. : public spawn_handler_base<Executor>
  373. {
  374. public:
  375. typedef T return_type;
  376. struct result_type
  377. {
  378. boost::system::error_code* ec_;
  379. return_type* value_;
  380. };
  381. spawn_handler(const basic_yield_context<Executor>& yield, result_type& result)
  382. : spawn_handler_base<Executor>(yield),
  383. result_(result)
  384. {
  385. }
  386. void operator()(boost::system::error_code ec, T value)
  387. {
  388. if (this->yield_.ec_)
  389. {
  390. *this->yield_.ec_ = ec;
  391. result_.ec_ = 0;
  392. }
  393. else
  394. result_.ec_ = &ec;
  395. result_.value_ = detail::addressof(value);
  396. this->resume();
  397. }
  398. static return_type on_resume(result_type& result)
  399. {
  400. if (result.ec_)
  401. throw_error(*result.ec_);
  402. return static_cast<return_type&&>(*result.value_);
  403. }
  404. private:
  405. result_type& result_;
  406. };
  407. template <typename Executor, typename R, typename Disposition, typename T>
  408. class spawn_handler<Executor, R(Disposition, T),
  409. enable_if_t<is_disposition<Disposition>::value>
  410. > : public spawn_handler_base<Executor>
  411. {
  412. public:
  413. typedef T return_type;
  414. struct result_type
  415. {
  416. Disposition* disposition_;
  417. return_type* value_;
  418. };
  419. spawn_handler(const basic_yield_context<Executor>& yield, result_type& result)
  420. : spawn_handler_base<Executor>(yield),
  421. result_(result)
  422. {
  423. }
  424. void operator()(Disposition d, T value)
  425. {
  426. result_.disposition_ = detail::addressof(d);
  427. result_.value_ = detail::addressof(value);
  428. this->resume();
  429. }
  430. static return_type on_resume(result_type& result)
  431. {
  432. if (*result.disposition_ != no_error)
  433. {
  434. boost::asio::throw_exception(
  435. static_cast<Disposition&&>(*result.disposition_));
  436. }
  437. return static_cast<return_type&&>(*result.value_);
  438. }
  439. private:
  440. result_type& result_;
  441. };
  442. template <typename Executor, typename R, typename T, typename... Ts>
  443. class spawn_handler<Executor, R(T, Ts...),
  444. enable_if_t<!is_disposition<T>::value>
  445. > : public spawn_handler_base<Executor>
  446. {
  447. public:
  448. typedef std::tuple<T, Ts...> return_type;
  449. typedef return_type* result_type;
  450. spawn_handler(const basic_yield_context<Executor>& yield, result_type& result)
  451. : spawn_handler_base<Executor>(yield),
  452. result_(result)
  453. {
  454. }
  455. template <typename... Args>
  456. void operator()(Args&&... args)
  457. {
  458. return_type value(static_cast<Args&&>(args)...);
  459. result_ = detail::addressof(value);
  460. this->resume();
  461. }
  462. static return_type on_resume(result_type& result)
  463. {
  464. return static_cast<return_type&&>(*result);
  465. }
  466. private:
  467. result_type& result_;
  468. };
  469. template <typename Executor, typename R, typename... Ts>
  470. class spawn_handler<Executor, R(boost::system::error_code, Ts...)>
  471. : public spawn_handler_base<Executor>
  472. {
  473. public:
  474. typedef std::tuple<Ts...> return_type;
  475. struct result_type
  476. {
  477. boost::system::error_code* ec_;
  478. return_type* value_;
  479. };
  480. spawn_handler(const basic_yield_context<Executor>& yield, result_type& result)
  481. : spawn_handler_base<Executor>(yield),
  482. result_(result)
  483. {
  484. }
  485. template <typename... Args>
  486. void operator()(boost::system::error_code ec,
  487. Args&&... args)
  488. {
  489. return_type value(static_cast<Args&&>(args)...);
  490. if (this->yield_.ec_)
  491. {
  492. *this->yield_.ec_ = ec;
  493. result_.ec_ = 0;
  494. }
  495. else
  496. result_.ec_ = &ec;
  497. result_.value_ = detail::addressof(value);
  498. this->resume();
  499. }
  500. static return_type on_resume(result_type& result)
  501. {
  502. if (result.ec_)
  503. throw_error(*result.ec_);
  504. return static_cast<return_type&&>(*result.value_);
  505. }
  506. private:
  507. result_type& result_;
  508. };
  509. template <typename Executor, typename R, typename Disposition, typename... Ts>
  510. class spawn_handler<Executor, R(Disposition, Ts...),
  511. enable_if_t<is_disposition<Disposition>::value>
  512. > : public spawn_handler_base<Executor>
  513. {
  514. public:
  515. typedef std::tuple<Ts...> return_type;
  516. struct result_type
  517. {
  518. Disposition* disposition_;
  519. return_type* value_;
  520. };
  521. spawn_handler(const basic_yield_context<Executor>& yield, result_type& result)
  522. : spawn_handler_base<Executor>(yield),
  523. result_(result)
  524. {
  525. }
  526. template <typename... Args>
  527. void operator()(Disposition d, Args&&... args)
  528. {
  529. return_type value(static_cast<Args&&>(args)...);
  530. result_.disposition_ = detail::addressof(d);
  531. result_.value_ = detail::addressof(value);
  532. this->resume();
  533. }
  534. static return_type on_resume(result_type& result)
  535. {
  536. if (*result.disposition_ != no_error)
  537. {
  538. boost::asio::throw_exception(
  539. static_cast<Disposition&&>(*result.disposition_));
  540. }
  541. return static_cast<return_type&&>(*result.value_);
  542. }
  543. private:
  544. result_type& result_;
  545. };
  546. template <typename Executor, typename Signature>
  547. inline bool asio_handler_is_continuation(spawn_handler<Executor, Signature>*)
  548. {
  549. return true;
  550. }
  551. } // namespace detail
  552. #if !defined(GENERATING_DOCUMENTATION)
  553. template <typename Executor, typename Signature>
  554. class async_result<basic_yield_context<Executor>, Signature>
  555. {
  556. public:
  557. typedef typename detail::spawn_handler<Executor, Signature> handler_type;
  558. typedef typename handler_type::return_type return_type;
  559. #if defined(BOOST_ASIO_HAS_VARIADIC_LAMBDA_CAPTURES)
  560. template <typename Initiation, typename... InitArgs>
  561. static return_type initiate(Initiation&& init,
  562. const basic_yield_context<Executor>& yield,
  563. InitArgs&&... init_args)
  564. {
  565. typename handler_type::result_type result
  566. = typename handler_type::result_type();
  567. yield.spawned_thread_->suspend_with(
  568. [&]()
  569. {
  570. static_cast<Initiation&&>(init)(
  571. handler_type(yield, result),
  572. static_cast<InitArgs&&>(init_args)...);
  573. });
  574. return handler_type::on_resume(result);
  575. }
  576. #else // defined(BOOST_ASIO_HAS_VARIADIC_LAMBDA_CAPTURES)
  577. template <typename Initiation, typename... InitArgs>
  578. struct suspend_with_helper
  579. {
  580. typename handler_type::result_type& result_;
  581. Initiation&& init_;
  582. const basic_yield_context<Executor>& yield_;
  583. std::tuple<InitArgs&&...> init_args_;
  584. template <std::size_t... I>
  585. void do_invoke(detail::index_sequence<I...>)
  586. {
  587. static_cast<Initiation&&>(init_)(
  588. handler_type(yield_, result_),
  589. static_cast<InitArgs&&>(std::get<I>(init_args_))...);
  590. }
  591. void operator()()
  592. {
  593. this->do_invoke(detail::make_index_sequence<sizeof...(InitArgs)>());
  594. }
  595. };
  596. template <typename Initiation, typename... InitArgs>
  597. static return_type initiate(Initiation&& init,
  598. const basic_yield_context<Executor>& yield,
  599. InitArgs&&... init_args)
  600. {
  601. typename handler_type::result_type result
  602. = typename handler_type::result_type();
  603. yield.spawned_thread_->suspend_with(
  604. suspend_with_helper<Initiation, InitArgs...>{
  605. result, static_cast<Initiation&&>(init), yield,
  606. std::tuple<InitArgs&&...>(
  607. static_cast<InitArgs&&>(init_args)...)});
  608. return handler_type::on_resume(result);
  609. }
  610. #endif // defined(BOOST_ASIO_HAS_VARIADIC_LAMBDA_CAPTURES)
  611. };
  612. #endif // !defined(GENERATING_DOCUMENTATION)
  613. namespace detail {
  614. template <typename Executor, typename Function, typename Handler>
  615. class spawn_entry_point
  616. {
  617. public:
  618. template <typename F, typename H>
  619. spawn_entry_point(const Executor& ex,
  620. F&& f, H&& h)
  621. : executor_(ex),
  622. function_(static_cast<F&&>(f)),
  623. handler_(static_cast<H&&>(h)),
  624. work_(handler_, executor_)
  625. {
  626. }
  627. void operator()(spawned_thread_base* spawned_thread)
  628. {
  629. const basic_yield_context<Executor> yield(spawned_thread, executor_);
  630. this->call(yield,
  631. void_type<result_of_t<Function(basic_yield_context<Executor>)>>());
  632. }
  633. private:
  634. void call(const basic_yield_context<Executor>& yield, void_type<void>)
  635. {
  636. #if !defined(BOOST_ASIO_NO_EXCEPTIONS)
  637. try
  638. #endif // !defined(BOOST_ASIO_NO_EXCEPTIONS)
  639. {
  640. function_(yield);
  641. if (!yield.spawned_thread_->has_context_switched())
  642. (post)(yield);
  643. detail::binder1<Handler, exception_ptr>
  644. handler(handler_, exception_ptr());
  645. work_.complete(handler, handler.handler_);
  646. }
  647. #if !defined(BOOST_ASIO_NO_EXCEPTIONS)
  648. # if defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
  649. catch (const boost::context::detail::forced_unwind&)
  650. {
  651. throw;
  652. }
  653. # endif // defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
  654. catch (...)
  655. {
  656. exception_ptr ex = current_exception();
  657. if (!yield.spawned_thread_->has_context_switched())
  658. (post)(yield);
  659. detail::binder1<Handler, exception_ptr> handler(handler_, ex);
  660. work_.complete(handler, handler.handler_);
  661. }
  662. #endif // !defined(BOOST_ASIO_NO_EXCEPTIONS)
  663. }
  664. template <typename T>
  665. void call(const basic_yield_context<Executor>& yield, void_type<T>)
  666. {
  667. #if !defined(BOOST_ASIO_NO_EXCEPTIONS)
  668. try
  669. #endif // !defined(BOOST_ASIO_NO_EXCEPTIONS)
  670. {
  671. T result(function_(yield));
  672. if (!yield.spawned_thread_->has_context_switched())
  673. (post)(yield);
  674. detail::move_binder2<Handler, exception_ptr, T>
  675. handler(0, static_cast<Handler&&>(handler_),
  676. exception_ptr(), static_cast<T&&>(result));
  677. work_.complete(handler, handler.handler_);
  678. }
  679. #if !defined(BOOST_ASIO_NO_EXCEPTIONS)
  680. # if defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
  681. catch (const boost::context::detail::forced_unwind&)
  682. {
  683. throw;
  684. }
  685. # endif // defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
  686. catch (...)
  687. {
  688. exception_ptr ex = current_exception();
  689. if (!yield.spawned_thread_->has_context_switched())
  690. (post)(yield);
  691. detail::move_binder2<Handler, exception_ptr, T>
  692. handler(0, static_cast<Handler&&>(handler_), ex, T());
  693. work_.complete(handler, handler.handler_);
  694. }
  695. #endif // !defined(BOOST_ASIO_NO_EXCEPTIONS)
  696. }
  697. Executor executor_;
  698. Function function_;
  699. Handler handler_;
  700. handler_work<Handler, Executor> work_;
  701. };
  702. struct spawn_cancellation_signal_emitter
  703. {
  704. cancellation_signal* signal_;
  705. cancellation_type_t type_;
  706. void operator()()
  707. {
  708. signal_->emit(type_);
  709. }
  710. };
  711. template <typename Handler, typename Executor, typename = void>
  712. class spawn_cancellation_handler
  713. {
  714. public:
  715. spawn_cancellation_handler(const Handler&, const Executor& ex)
  716. : ex_(ex)
  717. {
  718. }
  719. cancellation_slot slot()
  720. {
  721. return signal_.slot();
  722. }
  723. void operator()(cancellation_type_t type)
  724. {
  725. spawn_cancellation_signal_emitter emitter = { &signal_, type };
  726. (dispatch)(ex_, emitter);
  727. }
  728. private:
  729. cancellation_signal signal_;
  730. Executor ex_;
  731. };
  732. template <typename Handler, typename Executor>
  733. class spawn_cancellation_handler<Handler, Executor,
  734. enable_if_t<
  735. is_same<
  736. typename associated_executor<Handler,
  737. Executor>::asio_associated_executor_is_unspecialised,
  738. void
  739. >::value
  740. >>
  741. {
  742. public:
  743. spawn_cancellation_handler(const Handler&, const Executor&)
  744. {
  745. }
  746. cancellation_slot slot()
  747. {
  748. return signal_.slot();
  749. }
  750. void operator()(cancellation_type_t type)
  751. {
  752. signal_.emit(type);
  753. }
  754. private:
  755. cancellation_signal signal_;
  756. };
  757. template <typename Executor>
  758. class initiate_spawn
  759. {
  760. public:
  761. typedef Executor executor_type;
  762. explicit initiate_spawn(const executor_type& ex)
  763. : executor_(ex)
  764. {
  765. }
  766. executor_type get_executor() const noexcept
  767. {
  768. return executor_;
  769. }
  770. template <typename Handler, typename F>
  771. void operator()(Handler&& handler,
  772. F&& f) const
  773. {
  774. typedef decay_t<Handler> handler_type;
  775. typedef decay_t<F> function_type;
  776. typedef spawn_cancellation_handler<
  777. handler_type, Executor> cancel_handler_type;
  778. associated_cancellation_slot_t<handler_type> slot
  779. = boost::asio::get_associated_cancellation_slot(handler);
  780. cancel_handler_type* cancel_handler = slot.is_connected()
  781. ? &slot.template emplace<cancel_handler_type>(handler, executor_)
  782. : 0;
  783. cancellation_slot proxy_slot(
  784. cancel_handler
  785. ? cancel_handler->slot()
  786. : cancellation_slot());
  787. cancellation_state cancel_state(proxy_slot);
  788. (dispatch)(executor_,
  789. spawned_thread_resumer(
  790. default_spawned_thread_type::spawn(
  791. spawn_entry_point<Executor, function_type, handler_type>(
  792. executor_, static_cast<F&&>(f),
  793. static_cast<Handler&&>(handler)),
  794. proxy_slot, cancel_state)));
  795. }
  796. #if defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
  797. template <typename Handler, typename StackAllocator, typename F>
  798. void operator()(Handler&& handler, allocator_arg_t,
  799. StackAllocator&& stack_allocator,
  800. F&& f) const
  801. {
  802. typedef decay_t<Handler> handler_type;
  803. typedef decay_t<F> function_type;
  804. typedef spawn_cancellation_handler<
  805. handler_type, Executor> cancel_handler_type;
  806. associated_cancellation_slot_t<handler_type> slot
  807. = boost::asio::get_associated_cancellation_slot(handler);
  808. cancel_handler_type* cancel_handler = slot.is_connected()
  809. ? &slot.template emplace<cancel_handler_type>(handler, executor_)
  810. : 0;
  811. cancellation_slot proxy_slot(
  812. cancel_handler
  813. ? cancel_handler->slot()
  814. : cancellation_slot());
  815. cancellation_state cancel_state(proxy_slot);
  816. (dispatch)(executor_,
  817. spawned_thread_resumer(
  818. spawned_fiber_thread::spawn(allocator_arg_t(),
  819. static_cast<StackAllocator&&>(stack_allocator),
  820. spawn_entry_point<Executor, function_type, handler_type>(
  821. executor_, static_cast<F&&>(f),
  822. static_cast<Handler&&>(handler)),
  823. proxy_slot, cancel_state)));
  824. }
  825. #endif // defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
  826. private:
  827. executor_type executor_;
  828. };
  829. } // namespace detail
  830. template <typename Executor, typename F,
  831. BOOST_ASIO_COMPLETION_TOKEN_FOR(typename detail::spawn_signature<
  832. result_of_t<F(basic_yield_context<Executor>)>>::type) CompletionToken>
  833. inline auto spawn(const Executor& ex, F&& function, CompletionToken&& token,
  834. constraint_t<
  835. is_executor<Executor>::value || execution::is_executor<Executor>::value
  836. >)
  837. -> decltype(
  838. async_initiate<CompletionToken,
  839. typename detail::spawn_signature<
  840. result_of_t<F(basic_yield_context<Executor>)>>::type>(
  841. declval<detail::initiate_spawn<Executor>>(),
  842. token, static_cast<F&&>(function)))
  843. {
  844. return async_initiate<CompletionToken,
  845. typename detail::spawn_signature<
  846. result_of_t<F(basic_yield_context<Executor>)>>::type>(
  847. detail::initiate_spawn<Executor>(ex),
  848. token, static_cast<F&&>(function));
  849. }
  850. template <typename ExecutionContext, typename F,
  851. BOOST_ASIO_COMPLETION_TOKEN_FOR(typename detail::spawn_signature<
  852. result_of_t<F(basic_yield_context<
  853. typename ExecutionContext::executor_type>)>>::type) CompletionToken>
  854. inline auto spawn(ExecutionContext& ctx, F&& function, CompletionToken&& token,
  855. constraint_t<
  856. is_convertible<ExecutionContext&, execution_context&>::value
  857. >)
  858. -> decltype(
  859. async_initiate<CompletionToken,
  860. typename detail::spawn_signature<
  861. result_of_t<F(basic_yield_context<
  862. typename ExecutionContext::executor_type>)>>::type>(
  863. declval<detail::initiate_spawn<
  864. typename ExecutionContext::executor_type>>(),
  865. token, static_cast<F&&>(function)))
  866. {
  867. return (spawn)(ctx.get_executor(), static_cast<F&&>(function),
  868. static_cast<CompletionToken&&>(token));
  869. }
  870. template <typename Executor, typename F,
  871. BOOST_ASIO_COMPLETION_TOKEN_FOR(typename detail::spawn_signature<
  872. result_of_t<F(basic_yield_context<Executor>)>>::type)
  873. CompletionToken>
  874. inline auto spawn(const basic_yield_context<Executor>& ctx,
  875. F&& function, CompletionToken&& token,
  876. constraint_t<
  877. is_executor<Executor>::value || execution::is_executor<Executor>::value
  878. >)
  879. -> decltype(
  880. async_initiate<CompletionToken,
  881. typename detail::spawn_signature<
  882. result_of_t<F(basic_yield_context<Executor>)>>::type>(
  883. declval<detail::initiate_spawn<Executor>>(),
  884. token, static_cast<F&&>(function)))
  885. {
  886. return (spawn)(ctx.get_executor(), static_cast<F&&>(function),
  887. static_cast<CompletionToken&&>(token));
  888. }
  889. #if defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
  890. template <typename Executor, typename StackAllocator, typename F,
  891. BOOST_ASIO_COMPLETION_TOKEN_FOR(typename detail::spawn_signature<
  892. result_of_t<F(basic_yield_context<Executor>)>>::type)
  893. CompletionToken>
  894. inline auto spawn(const Executor& ex, allocator_arg_t,
  895. StackAllocator&& stack_allocator, F&& function, CompletionToken&& token,
  896. constraint_t<
  897. is_executor<Executor>::value || execution::is_executor<Executor>::value
  898. >)
  899. -> decltype(
  900. async_initiate<CompletionToken,
  901. typename detail::spawn_signature<
  902. result_of_t<F(basic_yield_context<Executor>)>>::type>(
  903. declval<detail::initiate_spawn<Executor>>(),
  904. token, allocator_arg_t(),
  905. static_cast<StackAllocator&&>(stack_allocator),
  906. static_cast<F&&>(function)))
  907. {
  908. return async_initiate<CompletionToken,
  909. typename detail::spawn_signature<
  910. result_of_t<F(basic_yield_context<Executor>)>>::type>(
  911. detail::initiate_spawn<Executor>(ex), token, allocator_arg_t(),
  912. static_cast<StackAllocator&&>(stack_allocator),
  913. static_cast<F&&>(function));
  914. }
  915. template <typename ExecutionContext, typename StackAllocator, typename F,
  916. BOOST_ASIO_COMPLETION_TOKEN_FOR(typename detail::spawn_signature<
  917. result_of_t<F(basic_yield_context<
  918. typename ExecutionContext::executor_type>)>>::type) CompletionToken>
  919. inline auto spawn(ExecutionContext& ctx, allocator_arg_t,
  920. StackAllocator&& stack_allocator, F&& function, CompletionToken&& token,
  921. constraint_t<
  922. is_convertible<ExecutionContext&, execution_context&>::value
  923. >)
  924. -> decltype(
  925. async_initiate<CompletionToken,
  926. typename detail::spawn_signature<
  927. result_of_t<F(basic_yield_context<
  928. typename ExecutionContext::executor_type>)>>::type>(
  929. declval<detail::initiate_spawn<
  930. typename ExecutionContext::executor_type>>(),
  931. token, allocator_arg_t(),
  932. static_cast<StackAllocator&&>(stack_allocator),
  933. static_cast<F&&>(function)))
  934. {
  935. return (spawn)(ctx.get_executor(), allocator_arg_t(),
  936. static_cast<StackAllocator&&>(stack_allocator),
  937. static_cast<F&&>(function), static_cast<CompletionToken&&>(token));
  938. }
  939. template <typename Executor, typename StackAllocator, typename F,
  940. BOOST_ASIO_COMPLETION_TOKEN_FOR(typename detail::spawn_signature<
  941. result_of_t<F(basic_yield_context<Executor>)>>::type) CompletionToken>
  942. inline auto spawn(const basic_yield_context<Executor>& ctx, allocator_arg_t,
  943. StackAllocator&& stack_allocator, F&& function, CompletionToken&& token,
  944. constraint_t<
  945. is_executor<Executor>::value || execution::is_executor<Executor>::value
  946. >)
  947. -> decltype(
  948. async_initiate<CompletionToken,
  949. typename detail::spawn_signature<
  950. result_of_t<F(basic_yield_context<Executor>)>>::type>(
  951. declval<detail::initiate_spawn<Executor>>(), token,
  952. allocator_arg_t(), static_cast<StackAllocator&&>(stack_allocator),
  953. static_cast<F&&>(function)))
  954. {
  955. return (spawn)(ctx.get_executor(), allocator_arg_t(),
  956. static_cast<StackAllocator&&>(stack_allocator),
  957. static_cast<F&&>(function), static_cast<CompletionToken&&>(token));
  958. }
  959. #endif // defined(BOOST_ASIO_HAS_BOOST_CONTEXT_FIBER)
  960. } // namespace asio
  961. } // namespace boost
  962. #include <boost/asio/detail/pop_options.hpp>
  963. #endif // BOOST_ASIO_IMPL_SPAWN_HPP