co_composed.hpp 40 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324
  1. //
  2. // co_composed.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_CO_COMPOSED_HPP
  11. #define BOOST_ASIO_CO_COMPOSED_HPP
  12. #if defined(_MSC_VER) && (_MSC_VER >= 1200)
  13. # pragma once
  14. #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
  15. #include <boost/asio/detail/config.hpp>
  16. #if defined(BOOST_ASIO_HAS_CO_AWAIT) || defined(GENERATING_DOCUMENTATION)
  17. #include <new>
  18. #include <tuple>
  19. #include <variant>
  20. #include <boost/asio/associated_cancellation_slot.hpp>
  21. #include <boost/asio/associator.hpp>
  22. #include <boost/asio/async_result.hpp>
  23. #include <boost/asio/cancellation_state.hpp>
  24. #include <boost/asio/detail/composed_work.hpp>
  25. #include <boost/asio/detail/recycling_allocator.hpp>
  26. #include <boost/asio/detail/throw_error.hpp>
  27. #include <boost/asio/detail/type_traits.hpp>
  28. #include <boost/asio/error.hpp>
  29. #if defined(BOOST_ASIO_HAS_STD_COROUTINE)
  30. # include <coroutine>
  31. #else // defined(BOOST_ASIO_HAS_STD_COROUTINE)
  32. # include <experimental/coroutine>
  33. #endif // defined(BOOST_ASIO_HAS_STD_COROUTINE)
  34. #if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
  35. # if defined(BOOST_ASIO_HAS_SOURCE_LOCATION)
  36. # include <boost/asio/detail/source_location.hpp>
  37. # endif // defined(BOOST_ASIO_HAS_SOURCE_LOCATION)
  38. #endif // defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
  39. #include <boost/asio/detail/push_options.hpp>
  40. namespace boost {
  41. namespace asio {
  42. namespace detail {
  43. #if defined(BOOST_ASIO_HAS_STD_COROUTINE)
  44. using std::coroutine_handle;
  45. using std::suspend_always;
  46. using std::suspend_never;
  47. #else // defined(BOOST_ASIO_HAS_STD_COROUTINE)
  48. using std::experimental::coroutine_handle;
  49. using std::experimental::suspend_always;
  50. using std::experimental::suspend_never;
  51. #endif // defined(BOOST_ASIO_HAS_STD_COROUTINE)
  52. using boost::asio::detail::composed_io_executors;
  53. using boost::asio::detail::composed_work;
  54. using boost::asio::detail::composed_work_guard;
  55. using boost::asio::detail::get_composed_io_executor;
  56. using boost::asio::detail::make_composed_io_executors;
  57. using boost::asio::detail::recycling_allocator;
  58. using boost::asio::detail::throw_error;
  59. template <typename Executors, typename Handler, typename Return>
  60. class co_composed_state;
  61. template <typename Executors, typename Handler, typename Return>
  62. class co_composed_handler_base;
  63. template <typename Executors, typename Handler, typename Return>
  64. class co_composed_promise;
  65. template <BOOST_ASIO_COMPLETION_SIGNATURE... Signatures>
  66. class co_composed_returns
  67. {
  68. };
  69. struct co_composed_on_suspend
  70. {
  71. void (*fn_)(void*) = nullptr;
  72. void* arg_ = nullptr;
  73. };
  74. template <typename... T>
  75. struct co_composed_completion : std::tuple<T&&...>
  76. {
  77. template <typename... U>
  78. co_composed_completion(U&&... u) noexcept
  79. : std::tuple<T&&...>(static_cast<U&&>(u)...)
  80. {
  81. }
  82. };
  83. template <typename Executors, typename Handler,
  84. typename Return, typename Signature>
  85. class co_composed_state_return_overload;
  86. template <typename Executors, typename Handler,
  87. typename Return, typename R, typename... Args>
  88. class co_composed_state_return_overload<
  89. Executors, Handler, Return, R(Args...)>
  90. {
  91. public:
  92. using derived_type = co_composed_state<Executors, Handler, Return>;
  93. using promise_type = co_composed_promise<Executors, Handler, Return>;
  94. using return_type = std::tuple<Args...>;
  95. void on_cancellation_complete_with(Args... args)
  96. {
  97. derived_type& state = *static_cast<derived_type*>(this);
  98. state.return_value_ = std::make_tuple(std::move(args)...);
  99. state.cancellation_on_suspend_fn(
  100. [](void* p)
  101. {
  102. auto& promise = *static_cast<promise_type*>(p);
  103. co_composed_handler_base<Executors, Handler,
  104. Return> composed_handler(promise);
  105. Handler handler(std::move(promise.state().handler_));
  106. return_type result(
  107. std::move(std::get<return_type>(promise.state().return_value_)));
  108. co_composed_handler_base<Executors, Handler,
  109. Return>(std::move(composed_handler));
  110. std::apply(std::move(handler), std::move(result));
  111. });
  112. }
  113. };
  114. template <typename Executors, typename Handler, typename Return>
  115. class co_composed_state_return;
  116. template <typename Executors, typename Handler, typename... Signatures>
  117. class co_composed_state_return<
  118. Executors, Handler, co_composed_returns<Signatures...>>
  119. : public co_composed_state_return_overload<Executors,
  120. Handler, co_composed_returns<Signatures...>, Signatures>...
  121. {
  122. public:
  123. using co_composed_state_return_overload<Executors,
  124. Handler, co_composed_returns<Signatures...>,
  125. Signatures>::on_cancellation_complete_with...;
  126. private:
  127. template <typename, typename, typename, typename>
  128. friend class co_composed_promise_return_overload;
  129. template <typename, typename, typename, typename>
  130. friend class co_composed_state_return_overload;
  131. std::variant<std::monostate,
  132. typename co_composed_state_return_overload<
  133. Executors, Handler, co_composed_returns<Signatures...>,
  134. Signatures>::return_type...> return_value_;
  135. };
  136. template <typename Executors, typename Handler,
  137. typename Return, typename... Signatures>
  138. struct co_composed_state_default_cancellation_on_suspend_impl;
  139. template <typename Executors, typename Handler, typename Return>
  140. struct co_composed_state_default_cancellation_on_suspend_impl<
  141. Executors, Handler, Return>
  142. {
  143. static constexpr void (*fn())(void*)
  144. {
  145. return nullptr;
  146. }
  147. };
  148. template <typename Executors, typename Handler, typename Return,
  149. typename R, typename... Args, typename... Signatures>
  150. struct co_composed_state_default_cancellation_on_suspend_impl<
  151. Executors, Handler, Return, R(Args...), Signatures...>
  152. {
  153. static constexpr void (*fn())(void*)
  154. {
  155. return co_composed_state_default_cancellation_on_suspend_impl<
  156. Executors, Handler, Return, Signatures...>::fn();
  157. }
  158. };
  159. template <typename Executors, typename Handler, typename Return,
  160. typename R, typename... Args, typename... Signatures>
  161. struct co_composed_state_default_cancellation_on_suspend_impl<Executors,
  162. Handler, Return, R(boost::system::error_code, Args...), Signatures...>
  163. {
  164. using promise_type = co_composed_promise<Executors, Handler, Return>;
  165. using return_type = std::tuple<boost::system::error_code, Args...>;
  166. static constexpr void (*fn())(void*)
  167. {
  168. if constexpr ((is_constructible<Args>::value && ...))
  169. {
  170. return [](void* p)
  171. {
  172. auto& promise = *static_cast<promise_type*>(p);
  173. co_composed_handler_base<Executors, Handler,
  174. Return> composed_handler(promise);
  175. Handler handler(std::move(promise.state().handler_));
  176. co_composed_handler_base<Executors, Handler,
  177. Return>(std::move(composed_handler));
  178. std::move(handler)(
  179. boost::system::error_code(boost::asio::error::operation_aborted),
  180. Args{}...);
  181. };
  182. }
  183. else
  184. {
  185. return co_composed_state_default_cancellation_on_suspend_impl<
  186. Executors, Handler, Return, Signatures...>::fn();
  187. }
  188. }
  189. };
  190. template <typename Executors, typename Handler, typename Return,
  191. typename R, typename... Args, typename... Signatures>
  192. struct co_composed_state_default_cancellation_on_suspend_impl<Executors,
  193. Handler, Return, R(std::exception_ptr, Args...), Signatures...>
  194. {
  195. using promise_type = co_composed_promise<Executors, Handler, Return>;
  196. using return_type = std::tuple<std::exception_ptr, Args...>;
  197. static constexpr void (*fn())(void*)
  198. {
  199. if constexpr ((is_constructible<Args>::value && ...))
  200. {
  201. return [](void* p)
  202. {
  203. auto& promise = *static_cast<promise_type*>(p);
  204. co_composed_handler_base<Executors, Handler,
  205. Return> composed_handler(promise);
  206. Handler handler(std::move(promise.state().handler_));
  207. co_composed_handler_base<Executors, Handler,
  208. Return>(std::move(composed_handler));
  209. std::move(handler)(
  210. std::make_exception_ptr(
  211. boost::system::system_error(
  212. boost::asio::error::operation_aborted, "co_await")),
  213. Args{}...);
  214. };
  215. }
  216. else
  217. {
  218. return co_composed_state_default_cancellation_on_suspend_impl<
  219. Executors, Handler, Return, Signatures...>::fn();
  220. }
  221. }
  222. };
  223. template <typename Executors, typename Handler, typename Return>
  224. struct co_composed_state_default_cancellation_on_suspend;
  225. template <typename Executors, typename Handler, typename... Signatures>
  226. struct co_composed_state_default_cancellation_on_suspend<
  227. Executors, Handler, co_composed_returns<Signatures...>>
  228. : co_composed_state_default_cancellation_on_suspend_impl<Executors,
  229. Handler, co_composed_returns<Signatures...>, Signatures...>
  230. {
  231. };
  232. template <typename Executors, typename Handler, typename Return>
  233. class co_composed_state_cancellation
  234. {
  235. public:
  236. using cancellation_slot_type = cancellation_slot;
  237. cancellation_slot_type get_cancellation_slot() const noexcept
  238. {
  239. return cancellation_state_.slot();
  240. }
  241. cancellation_state get_cancellation_state() const noexcept
  242. {
  243. return cancellation_state_;
  244. }
  245. void reset_cancellation_state()
  246. {
  247. cancellation_state_ = cancellation_state(
  248. (get_associated_cancellation_slot)(
  249. static_cast<co_composed_state<Executors, Handler, Return>*>(
  250. this)->handler()));
  251. }
  252. template <typename Filter>
  253. void reset_cancellation_state(Filter filter)
  254. {
  255. cancellation_state_ = cancellation_state(
  256. (get_associated_cancellation_slot)(
  257. static_cast<co_composed_state<Executors, Handler, Return>*>(
  258. this)->handler()), filter, filter);
  259. }
  260. template <typename InFilter, typename OutFilter>
  261. void reset_cancellation_state(InFilter&& in_filter, OutFilter&& out_filter)
  262. {
  263. cancellation_state_ = cancellation_state(
  264. (get_associated_cancellation_slot)(
  265. static_cast<co_composed_state<Executors, Handler, Return>*>(
  266. this)->handler()),
  267. static_cast<InFilter&&>(in_filter),
  268. static_cast<OutFilter&&>(out_filter));
  269. }
  270. cancellation_type_t cancelled() const noexcept
  271. {
  272. return cancellation_state_.cancelled();
  273. }
  274. void clear_cancellation_slot() noexcept
  275. {
  276. cancellation_state_.slot().clear();
  277. }
  278. [[nodiscard]] bool throw_if_cancelled() const noexcept
  279. {
  280. return throw_if_cancelled_;
  281. }
  282. void throw_if_cancelled(bool b) noexcept
  283. {
  284. throw_if_cancelled_ = b;
  285. }
  286. [[nodiscard]] bool complete_if_cancelled() const noexcept
  287. {
  288. return complete_if_cancelled_;
  289. }
  290. void complete_if_cancelled(bool b) noexcept
  291. {
  292. complete_if_cancelled_ = b;
  293. }
  294. private:
  295. template <typename, typename, typename>
  296. friend class co_composed_promise;
  297. template <typename, typename, typename, typename>
  298. friend class co_composed_state_return_overload;
  299. void cancellation_on_suspend_fn(void (*fn)(void*))
  300. {
  301. cancellation_on_suspend_fn_ = fn;
  302. }
  303. void check_for_cancellation_on_transform()
  304. {
  305. if (throw_if_cancelled_ && !!cancelled())
  306. throw_error(boost::asio::error::operation_aborted, "co_await");
  307. }
  308. bool check_for_cancellation_on_suspend(
  309. co_composed_promise<Executors, Handler, Return>& promise) noexcept
  310. {
  311. if (complete_if_cancelled_ && !!cancelled() && cancellation_on_suspend_fn_)
  312. {
  313. promise.state().work_.reset();
  314. promise.state().on_suspend_->fn_ = cancellation_on_suspend_fn_;
  315. promise.state().on_suspend_->arg_ = &promise;
  316. return false;
  317. }
  318. return true;
  319. }
  320. cancellation_state cancellation_state_;
  321. void (*cancellation_on_suspend_fn_)(void*) =
  322. co_composed_state_default_cancellation_on_suspend<
  323. Executors, Handler, Return>::fn();
  324. bool throw_if_cancelled_ = false;
  325. bool complete_if_cancelled_ = true;
  326. };
  327. template <typename Executors, typename Handler, typename Return>
  328. requires is_same<
  329. typename associated_cancellation_slot<
  330. Handler, cancellation_slot
  331. >::asio_associated_cancellation_slot_is_unspecialised,
  332. void>::value
  333. class co_composed_state_cancellation<Executors, Handler, Return>
  334. {
  335. public:
  336. void reset_cancellation_state()
  337. {
  338. }
  339. template <typename Filter>
  340. void reset_cancellation_state(Filter)
  341. {
  342. }
  343. template <typename InFilter, typename OutFilter>
  344. void reset_cancellation_state(InFilter&&, OutFilter&&)
  345. {
  346. }
  347. cancellation_type_t cancelled() const noexcept
  348. {
  349. return cancellation_type::none;
  350. }
  351. void clear_cancellation_slot() noexcept
  352. {
  353. }
  354. [[nodiscard]] bool throw_if_cancelled() const noexcept
  355. {
  356. return false;
  357. }
  358. void throw_if_cancelled(bool) noexcept
  359. {
  360. }
  361. [[nodiscard]] bool complete_if_cancelled() const noexcept
  362. {
  363. return false;
  364. }
  365. void complete_if_cancelled(bool) noexcept
  366. {
  367. }
  368. private:
  369. template <typename, typename, typename>
  370. friend class co_composed_promise;
  371. template <typename, typename, typename, typename>
  372. friend class co_composed_state_return_overload;
  373. void cancellation_on_suspend_fn(void (*)(void*))
  374. {
  375. }
  376. void check_for_cancellation_on_transform() noexcept
  377. {
  378. }
  379. bool check_for_cancellation_on_suspend(
  380. co_composed_promise<Executors, Handler, Return>&) noexcept
  381. {
  382. return true;
  383. }
  384. };
  385. template <typename Executors, typename Handler, typename Return>
  386. class co_composed_state
  387. : public co_composed_state_return<Executors, Handler, Return>,
  388. public co_composed_state_cancellation<Executors, Handler, Return>
  389. {
  390. public:
  391. using io_executor_type = typename composed_work_guard<
  392. typename composed_work<Executors>::head_type>::executor_type;
  393. template <typename H>
  394. co_composed_state(composed_io_executors<Executors>&& executors,
  395. H&& h, co_composed_on_suspend& on_suspend)
  396. : work_(std::move(executors)),
  397. handler_(static_cast<H&&>(h)),
  398. on_suspend_(&on_suspend)
  399. {
  400. this->reset_cancellation_state(enable_terminal_cancellation());
  401. }
  402. io_executor_type get_io_executor() const noexcept
  403. {
  404. return work_.head_.get_executor();
  405. }
  406. template <typename... Args>
  407. [[nodiscard]] co_composed_completion<Args...> complete(Args&&... args)
  408. requires requires { declval<Handler>()(static_cast<Args&&>(args)...); }
  409. {
  410. return co_composed_completion<Args...>(static_cast<Args&&>(args)...);
  411. }
  412. const Handler& handler() const noexcept
  413. {
  414. return handler_;
  415. }
  416. private:
  417. template <typename, typename, typename>
  418. friend class co_composed_handler_base;
  419. template <typename, typename, typename>
  420. friend class co_composed_promise;
  421. template <typename, typename, typename, typename>
  422. friend class co_composed_promise_return_overload;
  423. template <typename, typename, typename>
  424. friend class co_composed_state_cancellation;
  425. template <typename, typename, typename, typename>
  426. friend class co_composed_state_return_overload;
  427. template <typename, typename, typename, typename...>
  428. friend struct co_composed_state_default_cancellation_on_suspend_impl;
  429. composed_work<Executors> work_;
  430. Handler handler_;
  431. co_composed_on_suspend* on_suspend_;
  432. };
  433. template <typename Executors, typename Handler, typename Return>
  434. class co_composed_handler_cancellation
  435. {
  436. public:
  437. using cancellation_slot_type = cancellation_slot;
  438. cancellation_slot_type get_cancellation_slot() const noexcept
  439. {
  440. return static_cast<
  441. const co_composed_handler_base<Executors, Handler, Return>*>(
  442. this)->promise().state().get_cancellation_slot();
  443. }
  444. };
  445. template <typename Executors, typename Handler, typename Return>
  446. requires is_same<
  447. typename associated_cancellation_slot<
  448. Handler, cancellation_slot
  449. >::asio_associated_cancellation_slot_is_unspecialised,
  450. void>::value
  451. class co_composed_handler_cancellation<Executors, Handler, Return>
  452. {
  453. };
  454. template <typename Executors, typename Handler, typename Return>
  455. class co_composed_handler_base :
  456. public co_composed_handler_cancellation<Executors, Handler, Return>
  457. {
  458. public:
  459. co_composed_handler_base(
  460. co_composed_promise<Executors, Handler, Return>& p) noexcept
  461. : p_(&p)
  462. {
  463. }
  464. co_composed_handler_base(co_composed_handler_base&& other) noexcept
  465. : p_(std::exchange(other.p_, nullptr))
  466. {
  467. }
  468. ~co_composed_handler_base()
  469. {
  470. if (p_) [[unlikely]]
  471. p_->destroy();
  472. }
  473. co_composed_promise<Executors, Handler, Return>& promise() const noexcept
  474. {
  475. return *p_;
  476. }
  477. protected:
  478. void resume(void* result)
  479. {
  480. co_composed_on_suspend on_suspend{};
  481. std::exchange(p_, nullptr)->resume(p_, result, on_suspend);
  482. if (on_suspend.fn_)
  483. on_suspend.fn_(on_suspend.arg_);
  484. }
  485. private:
  486. co_composed_promise<Executors, Handler, Return>* p_;
  487. };
  488. template <typename Executors, typename Handler,
  489. typename Return, typename Signature>
  490. class co_composed_handler;
  491. template <typename Executors, typename Handler,
  492. typename Return, typename R, typename... Args>
  493. class co_composed_handler<Executors, Handler, Return, R(Args...)>
  494. : public co_composed_handler_base<Executors, Handler, Return>
  495. {
  496. public:
  497. using co_composed_handler_base<Executors,
  498. Handler, Return>::co_composed_handler_base;
  499. using result_type = std::tuple<decay_t<Args>...>;
  500. template <typename... T>
  501. void operator()(T&&... args)
  502. {
  503. result_type result(static_cast<T&&>(args)...);
  504. this->resume(&result);
  505. }
  506. static auto on_resume(void* result)
  507. {
  508. auto& args = *static_cast<result_type*>(result);
  509. if constexpr (sizeof...(Args) == 0)
  510. return;
  511. else if constexpr (sizeof...(Args) == 1)
  512. return std::move(std::get<0>(args));
  513. else
  514. return std::move(args);
  515. }
  516. };
  517. template <typename Executors, typename Handler,
  518. typename Return, typename R, typename... Args>
  519. class co_composed_handler<Executors, Handler,
  520. Return, R(boost::system::error_code, Args...)>
  521. : public co_composed_handler_base<Executors, Handler, Return>
  522. {
  523. public:
  524. using co_composed_handler_base<Executors,
  525. Handler, Return>::co_composed_handler_base;
  526. using args_type = std::tuple<decay_t<Args>...>;
  527. using result_type = std::tuple<boost::system::error_code, args_type>;
  528. template <typename... T>
  529. void operator()(const boost::system::error_code& ec, T&&... args)
  530. {
  531. result_type result(ec, args_type(static_cast<T&&>(args)...));
  532. this->resume(&result);
  533. }
  534. static auto on_resume(void* result)
  535. {
  536. auto& [ec, args] = *static_cast<result_type*>(result);
  537. throw_error(ec);
  538. if constexpr (sizeof...(Args) == 0)
  539. return;
  540. else if constexpr (sizeof...(Args) == 1)
  541. return std::move(std::get<0>(args));
  542. else
  543. return std::move(args);
  544. }
  545. };
  546. template <typename Executors, typename Handler,
  547. typename Return, typename R, typename... Args>
  548. class co_composed_handler<Executors, Handler,
  549. Return, R(std::exception_ptr, Args...)>
  550. : public co_composed_handler_base<Executors, Handler, Return>
  551. {
  552. public:
  553. using co_composed_handler_base<Executors,
  554. Handler, Return>::co_composed_handler_base;
  555. using args_type = std::tuple<decay_t<Args>...>;
  556. using result_type = std::tuple<std::exception_ptr, args_type>;
  557. template <typename... T>
  558. void operator()(std::exception_ptr ex, T&&... args)
  559. {
  560. result_type result(std::move(ex), args_type(static_cast<T&&>(args)...));
  561. this->resume(&result);
  562. }
  563. static auto on_resume(void* result)
  564. {
  565. auto& [ex, args] = *static_cast<result_type*>(result);
  566. if (ex)
  567. std::rethrow_exception(ex);
  568. if constexpr (sizeof...(Args) == 0)
  569. return;
  570. else if constexpr (sizeof...(Args) == 1)
  571. return std::move(std::get<0>(args));
  572. else
  573. return std::move(args);
  574. }
  575. };
  576. template <typename Executors, typename Handler, typename Return>
  577. class co_composed_promise_return;
  578. template <typename Executors, typename Handler>
  579. class co_composed_promise_return<Executors, Handler, co_composed_returns<>>
  580. {
  581. public:
  582. auto final_suspend() noexcept
  583. {
  584. return suspend_never();
  585. }
  586. void return_void() noexcept
  587. {
  588. }
  589. };
  590. template <typename Executors, typename Handler,
  591. typename Return, typename Signature>
  592. class co_composed_promise_return_overload;
  593. template <typename Executors, typename Handler,
  594. typename Return, typename R, typename... Args>
  595. class co_composed_promise_return_overload<
  596. Executors, Handler, Return, R(Args...)>
  597. {
  598. public:
  599. using derived_type = co_composed_promise<Executors, Handler, Return>;
  600. using return_type = std::tuple<Args...>;
  601. void return_value(std::tuple<Args...>&& value)
  602. {
  603. derived_type& promise = *static_cast<derived_type*>(this);
  604. promise.state().return_value_ = std::move(value);
  605. promise.state().work_.reset();
  606. promise.state().on_suspend_->arg_ = this;
  607. promise.state().on_suspend_->fn_ =
  608. [](void* p)
  609. {
  610. auto& promise = *static_cast<derived_type*>(p);
  611. co_composed_handler_base<Executors, Handler,
  612. Return> composed_handler(promise);
  613. Handler handler(std::move(promise.state().handler_));
  614. return_type result(
  615. std::move(std::get<return_type>(promise.state().return_value_)));
  616. co_composed_handler_base<Executors, Handler,
  617. Return>(std::move(composed_handler));
  618. std::apply(std::move(handler), std::move(result));
  619. };
  620. }
  621. };
  622. template <typename Executors, typename Handler, typename... Signatures>
  623. class co_composed_promise_return<Executors,
  624. Handler, co_composed_returns<Signatures...>>
  625. : public co_composed_promise_return_overload<Executors,
  626. Handler, co_composed_returns<Signatures...>, Signatures>...
  627. {
  628. public:
  629. auto final_suspend() noexcept
  630. {
  631. return suspend_always();
  632. }
  633. using co_composed_promise_return_overload<Executors, Handler,
  634. co_composed_returns<Signatures...>, Signatures>::return_value...;
  635. private:
  636. template <typename, typename, typename, typename>
  637. friend class co_composed_promise_return_overload;
  638. };
  639. template <typename Executors, typename Handler, typename Return>
  640. class co_composed_promise
  641. : public co_composed_promise_return<Executors, Handler, Return>
  642. {
  643. public:
  644. template <typename... Args>
  645. void* operator new(std::size_t size,
  646. co_composed_state<Executors, Handler, Return>& state, Args&&...)
  647. {
  648. block_allocator_type allocator(
  649. (get_associated_allocator)(state.handler_,
  650. recycling_allocator<void>()));
  651. block* base_ptr = std::allocator_traits<block_allocator_type>::allocate(
  652. allocator, blocks(sizeof(allocator_type)) + blocks(size));
  653. new (static_cast<void*>(base_ptr)) allocator_type(std::move(allocator));
  654. return base_ptr + blocks(sizeof(allocator_type));
  655. }
  656. template <typename C, typename... Args>
  657. void* operator new(std::size_t size, C&&,
  658. co_composed_state<Executors, Handler, Return>& state, Args&&...)
  659. {
  660. return co_composed_promise::operator new(size, state);
  661. }
  662. void operator delete(void* ptr, std::size_t size)
  663. {
  664. block* base_ptr = static_cast<block*>(ptr) - blocks(sizeof(allocator_type));
  665. allocator_type* allocator_ptr = std::launder(
  666. static_cast<allocator_type*>(static_cast<void*>(base_ptr)));
  667. block_allocator_type block_allocator(std::move(*allocator_ptr));
  668. allocator_ptr->~allocator_type();
  669. std::allocator_traits<block_allocator_type>::deallocate(block_allocator,
  670. base_ptr, blocks(sizeof(allocator_type)) + blocks(size));
  671. }
  672. template <typename... Args>
  673. co_composed_promise(
  674. co_composed_state<Executors, Handler, Return>& state, Args&&...)
  675. : state_(state)
  676. {
  677. }
  678. template <typename C, typename... Args>
  679. co_composed_promise(C&&,
  680. co_composed_state<Executors, Handler, Return>& state, Args&&...)
  681. : state_(state)
  682. {
  683. }
  684. void destroy() noexcept
  685. {
  686. coroutine_handle<co_composed_promise>::from_promise(*this).destroy();
  687. }
  688. void resume(co_composed_promise*& owner, void* result,
  689. co_composed_on_suspend& on_suspend)
  690. {
  691. state_.on_suspend_ = &on_suspend;
  692. state_.clear_cancellation_slot();
  693. owner_ = &owner;
  694. result_ = result;
  695. coroutine_handle<co_composed_promise>::from_promise(*this).resume();
  696. }
  697. co_composed_state<Executors, Handler, Return>& state() noexcept
  698. {
  699. return state_;
  700. }
  701. void get_return_object() noexcept
  702. {
  703. }
  704. auto initial_suspend() noexcept
  705. {
  706. return suspend_never();
  707. }
  708. void unhandled_exception()
  709. {
  710. if (owner_)
  711. *owner_ = this;
  712. throw;
  713. }
  714. template <BOOST_ASIO_ASYNC_OPERATION Op>
  715. auto await_transform(Op&& op,
  716. #if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
  717. # if defined(BOOST_ASIO_HAS_SOURCE_LOCATION)
  718. boost::asio::detail::source_location location
  719. = boost::asio::detail::source_location::current(),
  720. # endif // defined(BOOST_ASIO_HAS_SOURCE_LOCATION)
  721. #endif // defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
  722. constraint_t<is_async_operation<Op>::value> = 0)
  723. {
  724. class [[nodiscard]] awaitable
  725. {
  726. public:
  727. awaitable(Op&& op, co_composed_promise& promise
  728. #if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
  729. # if defined(BOOST_ASIO_HAS_SOURCE_LOCATION)
  730. , const boost::asio::detail::source_location& location
  731. # endif // defined(BOOST_ASIO_HAS_SOURCE_LOCATION)
  732. #endif // defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
  733. )
  734. : op_(static_cast<Op&&>(op)),
  735. promise_(promise)
  736. #if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
  737. # if defined(BOOST_ASIO_HAS_SOURCE_LOCATION)
  738. , location_(location)
  739. # endif // defined(BOOST_ASIO_HAS_SOURCE_LOCATION)
  740. #endif // defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
  741. {
  742. }
  743. constexpr bool await_ready() const noexcept
  744. {
  745. return false;
  746. }
  747. void await_suspend(coroutine_handle<co_composed_promise>)
  748. {
  749. if (promise_.state_.check_for_cancellation_on_suspend(promise_))
  750. {
  751. promise_.state_.on_suspend_->arg_ = this;
  752. promise_.state_.on_suspend_->fn_ =
  753. [](void* p)
  754. {
  755. #if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
  756. # if defined(BOOST_ASIO_HAS_SOURCE_LOCATION)
  757. BOOST_ASIO_HANDLER_LOCATION((
  758. static_cast<awaitable*>(p)->location_.file_name(),
  759. static_cast<awaitable*>(p)->location_.line(),
  760. static_cast<awaitable*>(p)->location_.function_name()));
  761. # endif // defined(BOOST_ASIO_HAS_SOURCE_LOCATION)
  762. #endif // defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
  763. static_cast<Op&&>(static_cast<awaitable*>(p)->op_)(
  764. co_composed_handler<Executors, Handler,
  765. Return, completion_signature_of_t<Op>>(
  766. static_cast<awaitable*>(p)->promise_));
  767. };
  768. }
  769. }
  770. auto await_resume()
  771. {
  772. return co_composed_handler<Executors, Handler, Return,
  773. completion_signature_of_t<Op>>::on_resume(promise_.result_);
  774. }
  775. private:
  776. Op&& op_;
  777. co_composed_promise& promise_;
  778. #if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
  779. # if defined(BOOST_ASIO_HAS_SOURCE_LOCATION)
  780. boost::asio::detail::source_location location_;
  781. # endif // defined(BOOST_ASIO_HAS_SOURCE_LOCATION)
  782. #endif // defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
  783. };
  784. state_.check_for_cancellation_on_transform();
  785. return awaitable{static_cast<Op&&>(op), *this
  786. #if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
  787. # if defined(BOOST_ASIO_HAS_SOURCE_LOCATION)
  788. , location
  789. # endif // defined(BOOST_ASIO_HAS_SOURCE_LOCATION)
  790. #endif // defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
  791. };
  792. }
  793. template <typename... Args>
  794. auto yield_value(co_composed_completion<Args...>&& result)
  795. {
  796. class [[nodiscard]] awaitable
  797. {
  798. public:
  799. awaitable(co_composed_completion<Args...>&& result,
  800. co_composed_promise& promise)
  801. : result_(std::move(result)),
  802. promise_(promise)
  803. {
  804. }
  805. constexpr bool await_ready() const noexcept
  806. {
  807. return false;
  808. }
  809. void await_suspend(coroutine_handle<co_composed_promise>)
  810. {
  811. promise_.state_.work_.reset();
  812. promise_.state_.on_suspend_->arg_ = this;
  813. promise_.state_.on_suspend_->fn_ =
  814. [](void* p)
  815. {
  816. awaitable& a = *static_cast<awaitable*>(p);
  817. co_composed_handler_base<Executors, Handler,
  818. Return> composed_handler(a.promise_);
  819. Handler handler(std::move(a.promise_.state_.handler_));
  820. std::tuple<decay_t<Args>...> result(
  821. std::move(static_cast<std::tuple<Args&&...>&>(a.result_)));
  822. co_composed_handler_base<Executors, Handler,
  823. Return>(std::move(composed_handler));
  824. std::apply(std::move(handler), std::move(result));
  825. };
  826. }
  827. void await_resume() noexcept
  828. {
  829. }
  830. private:
  831. co_composed_completion<Args...> result_;
  832. co_composed_promise& promise_;
  833. };
  834. return awaitable{std::move(result), *this};
  835. }
  836. private:
  837. using allocator_type =
  838. associated_allocator_t<Handler, recycling_allocator<void>>;
  839. union block
  840. {
  841. std::max_align_t max_align;
  842. alignas(allocator_type) char pad[alignof(allocator_type)];
  843. };
  844. using block_allocator_type =
  845. typename std::allocator_traits<allocator_type>
  846. ::template rebind_alloc<block>;
  847. static constexpr std::size_t blocks(std::size_t size)
  848. {
  849. return (size + sizeof(block) - 1) / sizeof(block);
  850. }
  851. co_composed_state<Executors, Handler, Return>& state_;
  852. co_composed_promise** owner_ = nullptr;
  853. void* result_ = nullptr;
  854. };
  855. template <typename Implementation, typename Executors, typename... Signatures>
  856. class initiate_co_composed
  857. {
  858. public:
  859. using executor_type = typename composed_io_executors<Executors>::head_type;
  860. template <typename I>
  861. initiate_co_composed(I&& impl, composed_io_executors<Executors>&& executors)
  862. : implementation_(static_cast<I&&>(impl)),
  863. executors_(std::move(executors))
  864. {
  865. }
  866. executor_type get_executor() const noexcept
  867. {
  868. return executors_.head_;
  869. }
  870. template <typename Handler, typename... InitArgs>
  871. void operator()(Handler&& handler, InitArgs&&... init_args) const &
  872. {
  873. using handler_type = decay_t<Handler>;
  874. using returns_type = co_composed_returns<Signatures...>;
  875. co_composed_on_suspend on_suspend{};
  876. implementation_(
  877. co_composed_state<Executors, handler_type, returns_type>(
  878. executors_, static_cast<Handler&&>(handler), on_suspend),
  879. static_cast<InitArgs&&>(init_args)...);
  880. if (on_suspend.fn_)
  881. on_suspend.fn_(on_suspend.arg_);
  882. }
  883. template <typename Handler, typename... InitArgs>
  884. void operator()(Handler&& handler, InitArgs&&... init_args) &&
  885. {
  886. using handler_type = decay_t<Handler>;
  887. using returns_type = co_composed_returns<Signatures...>;
  888. co_composed_on_suspend on_suspend{};
  889. std::move(implementation_)(
  890. co_composed_state<Executors, handler_type, returns_type>(
  891. std::move(executors_), static_cast<Handler&&>(handler), on_suspend),
  892. static_cast<InitArgs&&>(init_args)...);
  893. if (on_suspend.fn_)
  894. on_suspend.fn_(on_suspend.arg_);
  895. }
  896. private:
  897. Implementation implementation_;
  898. composed_io_executors<Executors> executors_;
  899. };
  900. template <typename Implementation, typename... Signatures>
  901. class initiate_co_composed<Implementation, void(), Signatures...>
  902. {
  903. public:
  904. template <typename I>
  905. initiate_co_composed(I&& impl, composed_io_executors<void()>&&)
  906. : implementation_(static_cast<I&&>(impl))
  907. {
  908. }
  909. template <typename Handler, typename... InitArgs>
  910. void operator()(Handler&& handler, InitArgs&&... init_args) const &
  911. {
  912. using handler_type = decay_t<Handler>;
  913. using returns_type = co_composed_returns<Signatures...>;
  914. co_composed_on_suspend on_suspend{};
  915. implementation_(
  916. co_composed_state<void(), handler_type, returns_type>(
  917. composed_io_executors<void()>(),
  918. static_cast<Handler&&>(handler), on_suspend),
  919. static_cast<InitArgs&&>(init_args)...);
  920. if (on_suspend.fn_)
  921. on_suspend.fn_(on_suspend.arg_);
  922. }
  923. template <typename Handler, typename... InitArgs>
  924. void operator()(Handler&& handler, InitArgs&&... init_args) &&
  925. {
  926. using handler_type = decay_t<Handler>;
  927. using returns_type = co_composed_returns<Signatures...>;
  928. co_composed_on_suspend on_suspend{};
  929. std::move(implementation_)(
  930. co_composed_state<void(), handler_type, returns_type>(
  931. composed_io_executors<void()>(),
  932. static_cast<Handler&&>(handler), on_suspend),
  933. static_cast<InitArgs&&>(init_args)...);
  934. if (on_suspend.fn_)
  935. on_suspend.fn_(on_suspend.arg_);
  936. }
  937. private:
  938. Implementation implementation_;
  939. };
  940. template <typename... Signatures, typename Implementation, typename Executors>
  941. inline initiate_co_composed<decay_t<Implementation>, Executors, Signatures...>
  942. make_initiate_co_composed(Implementation&& implementation,
  943. composed_io_executors<Executors>&& executors)
  944. {
  945. return initiate_co_composed<
  946. decay_t<Implementation>, Executors, Signatures...>(
  947. static_cast<Implementation&&>(implementation), std::move(executors));
  948. }
  949. } // namespace detail
  950. #if !defined(GENERATING_DOCUMENTATION)
  951. template <template <typename, typename> class Associator,
  952. typename Executors, typename Handler, typename Return,
  953. typename Signature, typename DefaultCandidate>
  954. struct associator<Associator,
  955. detail::co_composed_handler<Executors, Handler, Return, Signature>,
  956. DefaultCandidate>
  957. : Associator<Handler, DefaultCandidate>
  958. {
  959. static typename Associator<Handler, DefaultCandidate>::type get(
  960. const detail::co_composed_handler<
  961. Executors, Handler, Return, Signature>& h) noexcept
  962. {
  963. return Associator<Handler, DefaultCandidate>::get(
  964. h.promise().state().handler());
  965. }
  966. static auto get(
  967. const detail::co_composed_handler<
  968. Executors, Handler, Return, Signature>& h,
  969. const DefaultCandidate& c) noexcept
  970. -> decltype(
  971. Associator<Handler, DefaultCandidate>::get(
  972. h.promise().state().handler(), c))
  973. {
  974. return Associator<Handler, DefaultCandidate>::get(
  975. h.promise().state().handler(), c);
  976. }
  977. };
  978. #endif // !defined(GENERATING_DOCUMENTATION)
  979. } // namespace asio
  980. } // namespace boost
  981. #if !defined(GENERATING_DOCUMENTATION)
  982. # if defined(BOOST_ASIO_HAS_STD_COROUTINE)
  983. namespace std {
  984. # else // defined(BOOST_ASIO_HAS_STD_COROUTINE)
  985. namespace std { namespace experimental {
  986. # endif // defined(BOOST_ASIO_HAS_STD_COROUTINE)
  987. template <typename C, typename Executors,
  988. typename Handler, typename Return, typename... Args>
  989. struct coroutine_traits<void, C&,
  990. boost::asio::detail::co_composed_state<Executors, Handler, Return>, Args...>
  991. {
  992. using promise_type =
  993. boost::asio::detail::co_composed_promise<Executors, Handler, Return>;
  994. };
  995. template <typename C, typename Executors,
  996. typename Handler, typename Return, typename... Args>
  997. struct coroutine_traits<void, C&&,
  998. boost::asio::detail::co_composed_state<Executors, Handler, Return>, Args...>
  999. {
  1000. using promise_type =
  1001. boost::asio::detail::co_composed_promise<Executors, Handler, Return>;
  1002. };
  1003. template <typename Executors, typename Handler,
  1004. typename Return, typename... Args>
  1005. struct coroutine_traits<void,
  1006. boost::asio::detail::co_composed_state<Executors, Handler, Return>, Args...>
  1007. {
  1008. using promise_type =
  1009. boost::asio::detail::co_composed_promise<Executors, Handler, Return>;
  1010. };
  1011. # if defined(BOOST_ASIO_HAS_STD_COROUTINE)
  1012. } // namespace std
  1013. # else // defined(BOOST_ASIO_HAS_STD_COROUTINE)
  1014. }} // namespace std::experimental
  1015. # endif // defined(BOOST_ASIO_HAS_STD_COROUTINE)
  1016. #endif // !defined(GENERATING_DOCUMENTATION)
  1017. namespace boost {
  1018. namespace asio {
  1019. /// Creates an initiation function object that may be used to launch a
  1020. /// coroutine-based composed asynchronous operation.
  1021. /**
  1022. * The co_composed utility simplifies the implementation of composed
  1023. * asynchronous operations by automatically adapting a coroutine to be an
  1024. * initiation function object for use with @c async_initiate. When awaiting
  1025. * asynchronous operations, the coroutine automatically uses a conforming
  1026. * intermediate completion handler.
  1027. *
  1028. * @param implementation A function object that contains the coroutine-based
  1029. * implementation of the composed asynchronous operation. The first argument to
  1030. * the function object represents the state of the operation, and may be used
  1031. * to test for cancellation. The remaining arguments are those passed to @c
  1032. * async_initiate after the completion token.
  1033. *
  1034. * @param io_objects_or_executors Zero or more I/O objects or I/O executors for
  1035. * which outstanding work must be maintained while the operation is incomplete.
  1036. *
  1037. * @par Per-Operation Cancellation
  1038. * By default, terminal per-operation cancellation is enabled for composed
  1039. * operations that use co_composed. To disable cancellation for the composed
  1040. * operation, or to alter its supported cancellation types, call the state's
  1041. * @c reset_cancellation_state function.
  1042. *
  1043. * @par Examples
  1044. * The following example illustrates manual error handling and explicit checks
  1045. * for cancellation. The completion handler is invoked via a @c co_yield to the
  1046. * state's @c complete function, which never returns.
  1047. *
  1048. * @code template <typename CompletionToken>
  1049. * auto async_echo(tcp::socket& socket,
  1050. * CompletionToken&& token)
  1051. * {
  1052. * return boost::asio::async_initiate<
  1053. * CompletionToken, void(boost::system::error_code)>(
  1054. * boost::asio::co_composed(
  1055. * [](auto state, tcp::socket& socket) -> void
  1056. * {
  1057. * state.reset_cancellation_state(
  1058. * boost::asio::enable_terminal_cancellation());
  1059. *
  1060. * while (!state.cancelled())
  1061. * {
  1062. * char data[1024];
  1063. * auto [e1, n1] =
  1064. * co_await socket.async_read_some(
  1065. * boost::asio::buffer(data));
  1066. *
  1067. * if (e1)
  1068. * co_yield state.complete(e1);
  1069. *
  1070. * if (!!state.cancelled())
  1071. * co_yield state.complete(
  1072. * make_error_code(boost::asio::error::operation_aborted));
  1073. *
  1074. * auto [e2, n2] =
  1075. * co_await boost::asio::async_write(socket,
  1076. * boost::asio::buffer(data, n1));
  1077. *
  1078. * if (e2)
  1079. * co_yield state.complete(e2);
  1080. * }
  1081. * }, socket),
  1082. * token, std::ref(socket));
  1083. * } @endcode
  1084. *
  1085. * This next example shows exception-based error handling and implicit checks
  1086. * for cancellation. The completion handler is invoked after returning from the
  1087. * coroutine via @c co_return. Valid @c co_return values are specified using
  1088. * completion signatures passed to the @c co_composed function.
  1089. *
  1090. * @code template <typename CompletionToken>
  1091. * auto async_echo(tcp::socket& socket,
  1092. * CompletionToken&& token)
  1093. * {
  1094. * return boost::asio::async_initiate<
  1095. * CompletionToken, void(boost::system::error_code)>(
  1096. * boost::asio::co_composed<
  1097. * void(boost::system::error_code)>(
  1098. * [](auto state, tcp::socket& socket) -> void
  1099. * {
  1100. * try
  1101. * {
  1102. * state.throw_if_cancelled(true);
  1103. * state.reset_cancellation_state(
  1104. * boost::asio::enable_terminal_cancellation());
  1105. *
  1106. * for (;;)
  1107. * {
  1108. * char data[1024];
  1109. * std::size_t n = co_await socket.async_read_some(
  1110. * boost::asio::buffer(data));
  1111. *
  1112. * co_await boost::asio::async_write(socket,
  1113. * boost::asio::buffer(data, n));
  1114. * }
  1115. * }
  1116. * catch (const boost::system::system_error& e)
  1117. * {
  1118. * co_return {e.code()};
  1119. * }
  1120. * }, socket),
  1121. * token, std::ref(socket));
  1122. * } @endcode
  1123. */
  1124. template <BOOST_ASIO_COMPLETION_SIGNATURE... Signatures,
  1125. typename Implementation, typename... IoObjectsOrExecutors>
  1126. inline auto co_composed(Implementation&& implementation,
  1127. IoObjectsOrExecutors&&... io_objects_or_executors)
  1128. {
  1129. return detail::make_initiate_co_composed<Signatures...>(
  1130. static_cast<Implementation&&>(implementation),
  1131. detail::make_composed_io_executors(
  1132. detail::get_composed_io_executor(
  1133. static_cast<IoObjectsOrExecutors&&>(
  1134. io_objects_or_executors))...));
  1135. }
  1136. } // namespace asio
  1137. } // namespace boost
  1138. #include <boost/asio/detail/pop_options.hpp>
  1139. #endif // defined(BOOST_ASIO_HAS_CO_AWAIT) || defined(GENERATING_DOCUMENTATION)
  1140. #endif // BOOST_ASIO_CO_COMPOSED_HPP