Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-04-26 08:25:30

0001 //
0002 // co_composed.hpp
0003 // ~~~~~~~~~~~~~~~
0004 //
0005 // Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com)
0006 //
0007 // Distributed under the Boost Software License, Version 1.0. (See accompanying
0008 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
0009 //
0010 
0011 #ifndef BOOST_ASIO_CO_COMPOSED_HPP
0012 #define BOOST_ASIO_CO_COMPOSED_HPP
0013 
0014 #if defined(_MSC_VER) && (_MSC_VER >= 1200)
0015 # pragma once
0016 #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
0017 
0018 #include <boost/asio/detail/config.hpp>
0019 
0020 #if defined(BOOST_ASIO_HAS_CO_AWAIT) || defined(GENERATING_DOCUMENTATION)
0021 
0022 #include <new>
0023 #include <tuple>
0024 #include <variant>
0025 #include <boost/asio/associated_cancellation_slot.hpp>
0026 #include <boost/asio/associator.hpp>
0027 #include <boost/asio/async_result.hpp>
0028 #include <boost/asio/cancellation_state.hpp>
0029 #include <boost/asio/detail/composed_work.hpp>
0030 #include <boost/asio/detail/recycling_allocator.hpp>
0031 #include <boost/asio/detail/throw_error.hpp>
0032 #include <boost/asio/detail/type_traits.hpp>
0033 #include <boost/asio/error.hpp>
0034 
0035 #if defined(BOOST_ASIO_HAS_STD_COROUTINE)
0036 # include <coroutine>
0037 #else // defined(BOOST_ASIO_HAS_STD_COROUTINE)
0038 # include <experimental/coroutine>
0039 #endif // defined(BOOST_ASIO_HAS_STD_COROUTINE)
0040 
0041 #if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
0042 # if defined(BOOST_ASIO_HAS_SOURCE_LOCATION)
0043 #  include <boost/asio/detail/source_location.hpp>
0044 # endif // defined(BOOST_ASIO_HAS_SOURCE_LOCATION)
0045 #endif // defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
0046 
0047 #include <boost/asio/detail/push_options.hpp>
0048 
0049 namespace boost {
0050 namespace asio {
0051 namespace detail {
0052 
0053 #if defined(BOOST_ASIO_HAS_STD_COROUTINE)
0054 using std::coroutine_handle;
0055 using std::suspend_always;
0056 using std::suspend_never;
0057 #else // defined(BOOST_ASIO_HAS_STD_COROUTINE)
0058 using std::experimental::coroutine_handle;
0059 using std::experimental::suspend_always;
0060 using std::experimental::suspend_never;
0061 #endif // defined(BOOST_ASIO_HAS_STD_COROUTINE)
0062 
0063 using boost::asio::detail::composed_io_executors;
0064 using boost::asio::detail::composed_work;
0065 using boost::asio::detail::composed_work_guard;
0066 using boost::asio::detail::get_composed_io_executor;
0067 using boost::asio::detail::make_composed_io_executors;
0068 using boost::asio::detail::recycling_allocator;
0069 using boost::asio::detail::throw_error;
0070 
0071 template <typename Executors, typename Handler, typename Return>
0072 class co_composed_state;
0073 
0074 template <typename Executors, typename Handler, typename Return>
0075 class co_composed_handler_base;
0076 
0077 template <typename Executors, typename Handler, typename Return>
0078 class co_composed_promise;
0079 
0080 template <BOOST_ASIO_COMPLETION_SIGNATURE... Signatures>
0081 class co_composed_returns
0082 {
0083 };
0084 
0085 struct co_composed_on_suspend
0086 {
0087   void (*fn_)(void*) = nullptr;
0088   void* arg_ = nullptr;
0089 };
0090 
0091 template <typename... T>
0092 struct co_composed_completion : std::tuple<T&&...>
0093 {
0094   template <typename... U>
0095   co_composed_completion(U&&... u) noexcept
0096     : std::tuple<T&&...>(static_cast<U&&>(u)...)
0097   {
0098   }
0099 };
0100 
0101 template <typename Executors, typename Handler,
0102     typename Return, typename Signature>
0103 class co_composed_state_return_overload;
0104 
0105 template <typename Executors, typename Handler,
0106     typename Return, typename R, typename... Args>
0107 class co_composed_state_return_overload<
0108     Executors, Handler, Return, R(Args...)>
0109 {
0110 public:
0111   using derived_type = co_composed_state<Executors, Handler, Return>;
0112   using promise_type = co_composed_promise<Executors, Handler, Return>;
0113   using return_type = std::tuple<Args...>;
0114 
0115   void on_cancellation_complete_with(Args... args)
0116   {
0117     derived_type& state = *static_cast<derived_type*>(this);
0118     state.return_value_ = std::make_tuple(std::move(args)...);
0119     state.cancellation_on_suspend_fn(
0120         [](void* p)
0121         {
0122           auto& promise = *static_cast<promise_type*>(p);
0123 
0124           co_composed_handler_base<Executors, Handler,
0125             Return> composed_handler(promise);
0126 
0127           Handler handler(std::move(promise.state().handler_));
0128           return_type result(
0129               std::move(std::get<return_type>(promise.state().return_value_)));
0130 
0131           co_composed_handler_base<Executors, Handler,
0132             Return>(std::move(composed_handler));
0133 
0134           std::apply(std::move(handler), std::move(result));
0135         });
0136   }
0137 };
0138 
0139 template <typename Executors, typename Handler, typename Return>
0140 class co_composed_state_return;
0141 
0142 template <typename Executors, typename Handler, typename... Signatures>
0143 class co_composed_state_return<
0144     Executors, Handler, co_composed_returns<Signatures...>>
0145   : public co_composed_state_return_overload<Executors,
0146       Handler, co_composed_returns<Signatures...>, Signatures>...
0147 {
0148 public:
0149   using co_composed_state_return_overload<Executors,
0150     Handler, co_composed_returns<Signatures...>,
0151       Signatures>::on_cancellation_complete_with...;
0152 
0153 private:
0154   template <typename, typename, typename, typename>
0155     friend class co_composed_promise_return_overload;
0156   template <typename, typename, typename, typename>
0157     friend class co_composed_state_return_overload;
0158 
0159   std::variant<std::monostate,
0160     typename co_composed_state_return_overload<
0161       Executors, Handler, co_composed_returns<Signatures...>,
0162         Signatures>::return_type...> return_value_;
0163 };
0164 
0165 template <typename Executors, typename Handler,
0166     typename Return, typename... Signatures>
0167 struct co_composed_state_default_cancellation_on_suspend_impl;
0168 
0169 template <typename Executors, typename Handler, typename Return>
0170 struct co_composed_state_default_cancellation_on_suspend_impl<
0171     Executors, Handler, Return>
0172 {
0173   static constexpr void (*fn())(void*)
0174   {
0175     return nullptr;
0176   }
0177 };
0178 
0179 template <typename Executors, typename Handler, typename Return,
0180     typename R, typename... Args, typename... Signatures>
0181 struct co_composed_state_default_cancellation_on_suspend_impl<
0182     Executors, Handler, Return, R(Args...), Signatures...>
0183 {
0184   static constexpr void (*fn())(void*)
0185   {
0186     return co_composed_state_default_cancellation_on_suspend_impl<
0187       Executors, Handler, Return, Signatures...>::fn();
0188   }
0189 };
0190 
0191 template <typename Executors, typename Handler, typename Return,
0192     typename R, typename... Args, typename... Signatures>
0193 struct co_composed_state_default_cancellation_on_suspend_impl<Executors,
0194     Handler, Return, R(boost::system::error_code, Args...), Signatures...>
0195 {
0196   using promise_type = co_composed_promise<Executors, Handler, Return>;
0197   using return_type = std::tuple<boost::system::error_code, Args...>;
0198 
0199   static constexpr void (*fn())(void*)
0200   {
0201     if constexpr ((is_constructible<Args>::value && ...))
0202     {
0203       return [](void* p)
0204       {
0205         auto& promise = *static_cast<promise_type*>(p);
0206 
0207         co_composed_handler_base<Executors, Handler,
0208           Return> composed_handler(promise);
0209 
0210         Handler handler(std::move(promise.state().handler_));
0211 
0212         co_composed_handler_base<Executors, Handler,
0213           Return>(std::move(composed_handler));
0214 
0215         std::move(handler)(
0216             boost::system::error_code(boost::asio::error::operation_aborted),
0217             Args{}...);
0218       };
0219     }
0220     else
0221     {
0222       return co_composed_state_default_cancellation_on_suspend_impl<
0223         Executors, Handler, Return, Signatures...>::fn();
0224     }
0225   }
0226 };
0227 
0228 template <typename Executors, typename Handler, typename Return,
0229     typename R, typename... Args, typename... Signatures>
0230 struct co_composed_state_default_cancellation_on_suspend_impl<Executors,
0231     Handler, Return, R(std::exception_ptr, Args...), Signatures...>
0232 {
0233   using promise_type = co_composed_promise<Executors, Handler, Return>;
0234   using return_type = std::tuple<std::exception_ptr, Args...>;
0235 
0236   static constexpr void (*fn())(void*)
0237   {
0238     if constexpr ((is_constructible<Args>::value && ...))
0239     {
0240       return [](void* p)
0241       {
0242         auto& promise = *static_cast<promise_type*>(p);
0243 
0244         co_composed_handler_base<Executors, Handler,
0245           Return> composed_handler(promise);
0246 
0247         Handler handler(std::move(promise.state().handler_));
0248 
0249         co_composed_handler_base<Executors, Handler,
0250           Return>(std::move(composed_handler));
0251 
0252         std::move(handler)(
0253             std::make_exception_ptr(
0254               boost::system::system_error(
0255                 boost::asio::error::operation_aborted, "co_await")),
0256             Args{}...);
0257       };
0258     }
0259     else
0260     {
0261       return co_composed_state_default_cancellation_on_suspend_impl<
0262         Executors, Handler, Return, Signatures...>::fn();
0263     }
0264   }
0265 };
0266 
0267 template <typename Executors, typename Handler, typename Return>
0268 struct co_composed_state_default_cancellation_on_suspend;
0269 
0270 template <typename Executors, typename Handler, typename... Signatures>
0271 struct co_composed_state_default_cancellation_on_suspend<
0272     Executors, Handler, co_composed_returns<Signatures...>>
0273   : co_composed_state_default_cancellation_on_suspend_impl<Executors,
0274       Handler, co_composed_returns<Signatures...>, Signatures...>
0275 {
0276 };
0277 
0278 template <typename Executors, typename Handler, typename Return>
0279 class co_composed_state_cancellation
0280 {
0281 public:
0282   using cancellation_slot_type = cancellation_slot;
0283 
0284   cancellation_slot_type get_cancellation_slot() const noexcept
0285   {
0286     return cancellation_state_.slot();
0287   }
0288 
0289   cancellation_state get_cancellation_state() const noexcept
0290   {
0291     return cancellation_state_;
0292   }
0293 
0294   void reset_cancellation_state()
0295   {
0296     cancellation_state_ = cancellation_state(
0297         (get_associated_cancellation_slot)(
0298           static_cast<co_composed_state<Executors, Handler, Return>*>(
0299             this)->handler()));
0300   }
0301 
0302   template <typename Filter>
0303   void reset_cancellation_state(Filter filter)
0304   {
0305     cancellation_state_ = cancellation_state(
0306         (get_associated_cancellation_slot)(
0307           static_cast<co_composed_state<Executors, Handler, Return>*>(
0308             this)->handler()), filter, filter);
0309   }
0310 
0311   template <typename InFilter, typename OutFilter>
0312   void reset_cancellation_state(InFilter&& in_filter, OutFilter&& out_filter)
0313   {
0314     cancellation_state_ = cancellation_state(
0315         (get_associated_cancellation_slot)(
0316           static_cast<co_composed_state<Executors, Handler, Return>*>(
0317             this)->handler()),
0318         static_cast<InFilter&&>(in_filter),
0319         static_cast<OutFilter&&>(out_filter));
0320   }
0321 
0322   cancellation_type_t cancelled() const noexcept
0323   {
0324     return cancellation_state_.cancelled();
0325   }
0326 
0327   void clear_cancellation_slot() noexcept
0328   {
0329     cancellation_state_.slot().clear();
0330   }
0331 
0332   [[nodiscard]] bool throw_if_cancelled() const noexcept
0333   {
0334     return throw_if_cancelled_;
0335   }
0336 
0337   void throw_if_cancelled(bool b) noexcept
0338   {
0339     throw_if_cancelled_ = b;
0340   }
0341 
0342   [[nodiscard]] bool complete_if_cancelled() const noexcept
0343   {
0344     return complete_if_cancelled_;
0345   }
0346 
0347   void complete_if_cancelled(bool b) noexcept
0348   {
0349     complete_if_cancelled_ = b;
0350   }
0351 
0352 private:
0353   template <typename, typename, typename>
0354     friend class co_composed_promise;
0355   template <typename, typename, typename, typename>
0356     friend class co_composed_state_return_overload;
0357 
0358   void cancellation_on_suspend_fn(void (*fn)(void*))
0359   {
0360     cancellation_on_suspend_fn_ = fn;
0361   }
0362 
0363   void check_for_cancellation_on_transform()
0364   {
0365     if (throw_if_cancelled_ && !!cancelled())
0366       throw_error(boost::asio::error::operation_aborted, "co_await");
0367   }
0368 
0369   bool check_for_cancellation_on_suspend(
0370       co_composed_promise<Executors, Handler, Return>& promise) noexcept
0371   {
0372     if (complete_if_cancelled_ && !!cancelled() && cancellation_on_suspend_fn_)
0373     {
0374       promise.state().work_.reset();
0375       promise.state().on_suspend_->fn_ = cancellation_on_suspend_fn_;
0376       promise.state().on_suspend_->arg_ = &promise;
0377       return false;
0378     }
0379     return true;
0380   }
0381 
0382   cancellation_state cancellation_state_;
0383   void (*cancellation_on_suspend_fn_)(void*) =
0384     co_composed_state_default_cancellation_on_suspend<
0385       Executors, Handler, Return>::fn();
0386   bool throw_if_cancelled_ = false;
0387   bool complete_if_cancelled_ = true;
0388 };
0389 
0390 template <typename Executors, typename Handler, typename Return>
0391   requires is_same<
0392     typename associated_cancellation_slot<
0393       Handler, cancellation_slot
0394     >::asio_associated_cancellation_slot_is_unspecialised,
0395     void>::value
0396 class co_composed_state_cancellation<Executors, Handler, Return>
0397 {
0398 public:
0399   void reset_cancellation_state()
0400   {
0401   }
0402 
0403   template <typename Filter>
0404   void reset_cancellation_state(Filter)
0405   {
0406   }
0407 
0408   template <typename InFilter, typename OutFilter>
0409   void reset_cancellation_state(InFilter&&, OutFilter&&)
0410   {
0411   }
0412 
0413   cancellation_type_t cancelled() const noexcept
0414   {
0415     return cancellation_type::none;
0416   }
0417 
0418   void clear_cancellation_slot() noexcept
0419   {
0420   }
0421 
0422   [[nodiscard]] bool throw_if_cancelled() const noexcept
0423   {
0424     return false;
0425   }
0426 
0427   void throw_if_cancelled(bool) noexcept
0428   {
0429   }
0430 
0431   [[nodiscard]] bool complete_if_cancelled() const noexcept
0432   {
0433     return false;
0434   }
0435 
0436   void complete_if_cancelled(bool) noexcept
0437   {
0438   }
0439 
0440 private:
0441   template <typename, typename, typename>
0442     friend class co_composed_promise;
0443   template <typename, typename, typename, typename>
0444     friend class co_composed_state_return_overload;
0445 
0446   void cancellation_on_suspend_fn(void (*)(void*))
0447   {
0448   }
0449 
0450   void check_for_cancellation_on_transform() noexcept
0451   {
0452   }
0453 
0454   bool check_for_cancellation_on_suspend(
0455       co_composed_promise<Executors, Handler, Return>&) noexcept
0456   {
0457     return true;
0458   }
0459 };
0460 
0461 template <typename Executors, typename Handler, typename Return>
0462 class co_composed_state
0463   : public co_composed_state_return<Executors, Handler, Return>,
0464     public co_composed_state_cancellation<Executors, Handler, Return>
0465 {
0466 public:
0467   using io_executor_type = typename composed_work_guard<
0468     typename composed_work<Executors>::head_type>::executor_type;
0469 
0470   template <typename H>
0471   co_composed_state(composed_io_executors<Executors>&& executors,
0472       H&& h, co_composed_on_suspend& on_suspend)
0473     : work_(std::move(executors)),
0474       handler_(static_cast<H&&>(h)),
0475       on_suspend_(&on_suspend)
0476   {
0477     this->reset_cancellation_state(enable_terminal_cancellation());
0478   }
0479 
0480   io_executor_type get_io_executor() const noexcept
0481   {
0482     return work_.head_.get_executor();
0483   }
0484 
0485   template <typename... Args>
0486   [[nodiscard]] co_composed_completion<Args...> complete(Args&&... args)
0487     requires requires { declval<Handler>()(static_cast<Args&&>(args)...); }
0488   {
0489     return co_composed_completion<Args...>(static_cast<Args&&>(args)...);
0490   }
0491 
0492   const Handler& handler() const noexcept
0493   {
0494     return handler_;
0495   }
0496 
0497 private:
0498   template <typename, typename, typename>
0499     friend class co_composed_handler_base;
0500   template <typename, typename, typename>
0501     friend class co_composed_promise;
0502   template <typename, typename, typename, typename>
0503     friend class co_composed_promise_return_overload;
0504   template <typename, typename, typename>
0505     friend class co_composed_state_cancellation;
0506   template <typename, typename, typename, typename>
0507     friend class co_composed_state_return_overload;
0508   template <typename, typename, typename, typename...>
0509     friend struct co_composed_state_default_cancellation_on_suspend_impl;
0510 
0511   composed_work<Executors> work_;
0512   Handler handler_;
0513   co_composed_on_suspend* on_suspend_;
0514 };
0515 
0516 template <typename Executors, typename Handler, typename Return>
0517 class co_composed_handler_cancellation
0518 {
0519 public:
0520   using cancellation_slot_type = cancellation_slot;
0521 
0522   cancellation_slot_type get_cancellation_slot() const noexcept
0523   {
0524     return static_cast<
0525       const co_composed_handler_base<Executors, Handler, Return>*>(
0526         this)->promise().state().get_cancellation_slot();
0527   }
0528 };
0529 
0530 template <typename Executors, typename Handler, typename Return>
0531   requires is_same<
0532     typename associated_cancellation_slot<
0533       Handler, cancellation_slot
0534     >::asio_associated_cancellation_slot_is_unspecialised,
0535     void>::value
0536 class co_composed_handler_cancellation<Executors, Handler, Return>
0537 {
0538 };
0539 
0540 template <typename Executors, typename Handler, typename Return>
0541 class co_composed_handler_base :
0542   public co_composed_handler_cancellation<Executors, Handler, Return>
0543 {
0544 public:
0545   co_composed_handler_base(
0546       co_composed_promise<Executors, Handler, Return>& p) noexcept
0547     : p_(&p)
0548   {
0549   }
0550 
0551   co_composed_handler_base(co_composed_handler_base&& other) noexcept
0552     : p_(std::exchange(other.p_, nullptr))
0553   {
0554   }
0555 
0556   ~co_composed_handler_base()
0557   {
0558     if (p_) [[unlikely]]
0559       p_->destroy();
0560   }
0561 
0562   co_composed_promise<Executors, Handler, Return>& promise() const noexcept
0563   {
0564     return *p_;
0565   }
0566 
0567 protected:
0568   void resume(void* result)
0569   {
0570     co_composed_on_suspend on_suspend{};
0571     std::exchange(p_, nullptr)->resume(p_, result, on_suspend);
0572     if (on_suspend.fn_)
0573       on_suspend.fn_(on_suspend.arg_);
0574   }
0575 
0576 private:
0577   co_composed_promise<Executors, Handler, Return>* p_;
0578 };
0579 
0580 template <typename Executors, typename Handler,
0581     typename Return, typename Signature>
0582 class co_composed_handler;
0583 
0584 template <typename Executors, typename Handler,
0585     typename Return, typename R, typename... Args>
0586 class co_composed_handler<Executors, Handler, Return, R(Args...)>
0587   : public co_composed_handler_base<Executors, Handler, Return>
0588 {
0589 public:
0590   using co_composed_handler_base<Executors,
0591     Handler, Return>::co_composed_handler_base;
0592 
0593   using result_type = std::tuple<decay_t<Args>...>;
0594 
0595   template <typename... T>
0596   void operator()(T&&... args)
0597   {
0598     result_type result(static_cast<T&&>(args)...);
0599     this->resume(&result);
0600   }
0601 
0602   static auto on_resume(void* result)
0603   {
0604     auto& args = *static_cast<result_type*>(result);
0605     if constexpr (sizeof...(Args) == 0)
0606       return;
0607     else if constexpr (sizeof...(Args) == 1)
0608       return std::move(std::get<0>(args));
0609     else
0610       return std::move(args);
0611   }
0612 };
0613 
0614 template <typename Executors, typename Handler,
0615     typename Return, typename R, typename... Args>
0616 class co_composed_handler<Executors, Handler,
0617     Return, R(boost::system::error_code, Args...)>
0618   : public co_composed_handler_base<Executors, Handler, Return>
0619 {
0620 public:
0621   using co_composed_handler_base<Executors,
0622     Handler, Return>::co_composed_handler_base;
0623 
0624   using args_type = std::tuple<decay_t<Args>...>;
0625   using result_type = std::tuple<boost::system::error_code, args_type>;
0626 
0627   template <typename... T>
0628   void operator()(const boost::system::error_code& ec, T&&... args)
0629   {
0630     result_type result(ec, args_type(static_cast<T&&>(args)...));
0631     this->resume(&result);
0632   }
0633 
0634   static auto on_resume(void* result)
0635   {
0636     auto& [ec, args] = *static_cast<result_type*>(result);
0637     throw_error(ec);
0638     if constexpr (sizeof...(Args) == 0)
0639       return;
0640     else if constexpr (sizeof...(Args) == 1)
0641       return std::move(std::get<0>(args));
0642     else
0643       return std::move(args);
0644   }
0645 };
0646 
0647 template <typename Executors, typename Handler,
0648     typename Return, typename R, typename... Args>
0649 class co_composed_handler<Executors, Handler,
0650     Return, R(std::exception_ptr, Args...)>
0651   : public co_composed_handler_base<Executors, Handler, Return>
0652 {
0653 public:
0654   using co_composed_handler_base<Executors,
0655     Handler, Return>::co_composed_handler_base;
0656 
0657   using args_type = std::tuple<decay_t<Args>...>;
0658   using result_type = std::tuple<std::exception_ptr, args_type>;
0659 
0660   template <typename... T>
0661   void operator()(std::exception_ptr ex, T&&... args)
0662   {
0663     result_type result(std::move(ex), args_type(static_cast<T&&>(args)...));
0664     this->resume(&result);
0665   }
0666 
0667   static auto on_resume(void* result)
0668   {
0669     auto& [ex, args] = *static_cast<result_type*>(result);
0670     if (ex)
0671       std::rethrow_exception(ex);
0672     if constexpr (sizeof...(Args) == 0)
0673       return;
0674     else if constexpr (sizeof...(Args) == 1)
0675       return std::move(std::get<0>(args));
0676     else
0677       return std::move(args);
0678   }
0679 };
0680 
0681 template <typename Executors, typename Handler, typename Return>
0682 class co_composed_promise_return;
0683 
0684 template <typename Executors, typename Handler>
0685 class co_composed_promise_return<Executors, Handler, co_composed_returns<>>
0686 {
0687 public:
0688   auto final_suspend() noexcept
0689   {
0690     return suspend_never();
0691   }
0692 
0693   void return_void() noexcept
0694   {
0695   }
0696 };
0697 
0698 template <typename Executors, typename Handler,
0699     typename Return, typename Signature>
0700 class co_composed_promise_return_overload;
0701 
0702 template <typename Executors, typename Handler,
0703     typename Return, typename R, typename... Args>
0704 class co_composed_promise_return_overload<
0705     Executors, Handler, Return, R(Args...)>
0706 {
0707 public:
0708   using derived_type = co_composed_promise<Executors, Handler, Return>;
0709   using return_type = std::tuple<Args...>;
0710 
0711   void return_value(std::tuple<Args...>&& value)
0712   {
0713     derived_type& promise = *static_cast<derived_type*>(this);
0714     promise.state().return_value_ = std::move(value);
0715     promise.state().work_.reset();
0716     promise.state().on_suspend_->arg_ = this;
0717     promise.state().on_suspend_->fn_ =
0718       [](void* p)
0719       {
0720         auto& promise = *static_cast<derived_type*>(p);
0721 
0722         co_composed_handler_base<Executors, Handler,
0723           Return> composed_handler(promise);
0724 
0725         Handler handler(std::move(promise.state().handler_));
0726         return_type result(
0727             std::move(std::get<return_type>(promise.state().return_value_)));
0728 
0729         co_composed_handler_base<Executors, Handler,
0730           Return>(std::move(composed_handler));
0731 
0732         std::apply(std::move(handler), std::move(result));
0733       };
0734   }
0735 };
0736 
0737 template <typename Executors, typename Handler, typename... Signatures>
0738 class co_composed_promise_return<Executors,
0739     Handler, co_composed_returns<Signatures...>>
0740   : public co_composed_promise_return_overload<Executors,
0741       Handler, co_composed_returns<Signatures...>, Signatures>...
0742 {
0743 public:
0744   auto final_suspend() noexcept
0745   {
0746     return suspend_always();
0747   }
0748 
0749   using co_composed_promise_return_overload<Executors, Handler,
0750     co_composed_returns<Signatures...>, Signatures>::return_value...;
0751 
0752 private:
0753   template <typename, typename, typename, typename>
0754     friend class co_composed_promise_return_overload;
0755 };
0756 
0757 template <typename Executors, typename Handler, typename Return>
0758 class co_composed_promise
0759   : public co_composed_promise_return<Executors, Handler, Return>
0760 {
0761 public:
0762   template <typename... Args>
0763   void* operator new(std::size_t size,
0764       co_composed_state<Executors, Handler, Return>& state, Args&&...)
0765   {
0766     block_allocator_type allocator(
0767       (get_associated_allocator)(state.handler_,
0768         recycling_allocator<void>()));
0769 
0770     block* base_ptr = std::allocator_traits<block_allocator_type>::allocate(
0771         allocator, blocks(sizeof(allocator_type)) + blocks(size));
0772 
0773     new (static_cast<void*>(base_ptr)) allocator_type(std::move(allocator));
0774 
0775     return base_ptr + blocks(sizeof(allocator_type));
0776   }
0777 
0778   template <typename C, typename... Args>
0779   void* operator new(std::size_t size, C&&,
0780       co_composed_state<Executors, Handler, Return>& state, Args&&...)
0781   {
0782     return co_composed_promise::operator new(size, state);
0783   }
0784 
0785   void operator delete(void* ptr, std::size_t size)
0786   {
0787     block* base_ptr = static_cast<block*>(ptr) - blocks(sizeof(allocator_type));
0788 
0789     allocator_type* allocator_ptr = std::launder(
0790         static_cast<allocator_type*>(static_cast<void*>(base_ptr)));
0791 
0792     block_allocator_type block_allocator(std::move(*allocator_ptr));
0793     allocator_ptr->~allocator_type();
0794 
0795     std::allocator_traits<block_allocator_type>::deallocate(block_allocator,
0796         base_ptr, blocks(sizeof(allocator_type)) + blocks(size));
0797   }
0798 
0799   template <typename... Args>
0800   co_composed_promise(
0801       co_composed_state<Executors, Handler, Return>& state, Args&&...)
0802     : state_(state)
0803   {
0804   }
0805 
0806   template <typename C, typename... Args>
0807   co_composed_promise(C&&,
0808       co_composed_state<Executors, Handler, Return>& state, Args&&...)
0809     : state_(state)
0810   {
0811   }
0812 
0813   void destroy() noexcept
0814   {
0815     coroutine_handle<co_composed_promise>::from_promise(*this).destroy();
0816   }
0817 
0818   void resume(co_composed_promise*& owner, void* result,
0819       co_composed_on_suspend& on_suspend)
0820   {
0821     state_.on_suspend_ = &on_suspend;
0822     state_.clear_cancellation_slot();
0823     owner_ = &owner;
0824     result_ = result;
0825     coroutine_handle<co_composed_promise>::from_promise(*this).resume();
0826   }
0827 
0828   co_composed_state<Executors, Handler, Return>& state() noexcept
0829   {
0830     return state_;
0831   }
0832 
0833   void get_return_object() noexcept
0834   {
0835   }
0836 
0837   auto initial_suspend() noexcept
0838   {
0839     return suspend_never();
0840   }
0841 
0842   void unhandled_exception()
0843   {
0844     if (owner_)
0845       *owner_ = this;
0846     throw;
0847   }
0848 
0849   template <async_operation Op>
0850   auto await_transform(Op&& op
0851 #if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
0852 # if defined(BOOST_ASIO_HAS_SOURCE_LOCATION)
0853       , boost::asio::detail::source_location location
0854         = boost::asio::detail::source_location::current()
0855 # endif // defined(BOOST_ASIO_HAS_SOURCE_LOCATION)
0856 #endif // defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
0857     )
0858   {
0859     class [[nodiscard]] awaitable
0860     {
0861     public:
0862       awaitable(Op&& op, co_composed_promise& promise
0863 #if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
0864 # if defined(BOOST_ASIO_HAS_SOURCE_LOCATION)
0865           , const boost::asio::detail::source_location& location
0866 # endif // defined(BOOST_ASIO_HAS_SOURCE_LOCATION)
0867 #endif // defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
0868         )
0869         : op_(static_cast<Op&&>(op)),
0870           promise_(promise)
0871 #if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
0872 # if defined(BOOST_ASIO_HAS_SOURCE_LOCATION)
0873         , location_(location)
0874 # endif // defined(BOOST_ASIO_HAS_SOURCE_LOCATION)
0875 #endif // defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
0876       {
0877       }
0878 
0879       constexpr bool await_ready() const noexcept
0880       {
0881         return false;
0882       }
0883 
0884       void await_suspend(coroutine_handle<co_composed_promise>)
0885       {
0886         if (promise_.state_.check_for_cancellation_on_suspend(promise_))
0887         {
0888           promise_.state_.on_suspend_->arg_ = this;
0889           promise_.state_.on_suspend_->fn_ =
0890             [](void* p)
0891             {
0892 #if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
0893 # if defined(BOOST_ASIO_HAS_SOURCE_LOCATION)
0894               BOOST_ASIO_HANDLER_LOCATION((
0895                   static_cast<awaitable*>(p)->location_.file_name(),
0896                   static_cast<awaitable*>(p)->location_.line(),
0897                   static_cast<awaitable*>(p)->location_.function_name()));
0898 # endif // defined(BOOST_ASIO_HAS_SOURCE_LOCATION)
0899 #endif // defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
0900               static_cast<Op&&>(static_cast<awaitable*>(p)->op_)(
0901                   co_composed_handler<Executors, Handler,
0902                     Return, completion_signature_of_t<Op>>(
0903                       static_cast<awaitable*>(p)->promise_));
0904             };
0905         }
0906       }
0907 
0908       auto await_resume()
0909       {
0910         return co_composed_handler<Executors, Handler, Return,
0911           completion_signature_of_t<Op>>::on_resume(promise_.result_);
0912       }
0913 
0914     private:
0915       Op&& op_;
0916       co_composed_promise& promise_;
0917 #if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
0918 # if defined(BOOST_ASIO_HAS_SOURCE_LOCATION)
0919       boost::asio::detail::source_location location_;
0920 # endif // defined(BOOST_ASIO_HAS_SOURCE_LOCATION)
0921 #endif // defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
0922     };
0923 
0924     state_.check_for_cancellation_on_transform();
0925     return awaitable{static_cast<Op&&>(op), *this
0926 #if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
0927 # if defined(BOOST_ASIO_HAS_SOURCE_LOCATION)
0928         , location
0929 # endif // defined(BOOST_ASIO_HAS_SOURCE_LOCATION)
0930 #endif // defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
0931       };
0932   }
0933 
0934   template <typename... Args>
0935   auto yield_value(co_composed_completion<Args...>&& result)
0936   {
0937     class [[nodiscard]] awaitable
0938     {
0939     public:
0940       awaitable(co_composed_completion<Args...>&& result,
0941           co_composed_promise& promise)
0942         : result_(std::move(result)),
0943           promise_(promise)
0944       {
0945       }
0946 
0947       constexpr bool await_ready() const noexcept
0948       {
0949         return false;
0950       }
0951 
0952       void await_suspend(coroutine_handle<co_composed_promise>)
0953       {
0954         promise_.state_.work_.reset();
0955         promise_.state_.on_suspend_->arg_ = this;
0956         promise_.state_.on_suspend_->fn_ =
0957           [](void* p)
0958           {
0959             awaitable& a = *static_cast<awaitable*>(p);
0960 
0961             co_composed_handler_base<Executors, Handler,
0962               Return> composed_handler(a.promise_);
0963 
0964             Handler handler(std::move(a.promise_.state_.handler_));
0965             std::tuple<decay_t<Args>...> result(
0966                 std::move(static_cast<std::tuple<Args&&...>&>(a.result_)));
0967 
0968             co_composed_handler_base<Executors, Handler,
0969               Return>(std::move(composed_handler));
0970 
0971             std::apply(std::move(handler), std::move(result));
0972           };
0973       }
0974 
0975       void await_resume() noexcept
0976       {
0977       }
0978 
0979     private:
0980       co_composed_completion<Args...> result_;
0981       co_composed_promise& promise_;
0982     };
0983 
0984     return awaitable{std::move(result), *this};
0985   }
0986 
0987 private:
0988   using allocator_type =
0989     associated_allocator_t<Handler, recycling_allocator<void>>;
0990 
0991   union block
0992   {
0993     std::max_align_t max_align;
0994     alignas(allocator_type) char pad[alignof(allocator_type)];
0995   };
0996 
0997   using block_allocator_type =
0998     typename std::allocator_traits<allocator_type>
0999       ::template rebind_alloc<block>;
1000 
1001   static constexpr std::size_t blocks(std::size_t size)
1002   {
1003     return (size + sizeof(block) - 1) / sizeof(block);
1004   }
1005 
1006   co_composed_state<Executors, Handler, Return>& state_;
1007   co_composed_promise** owner_ = nullptr;
1008   void* result_ = nullptr;
1009 };
1010 
1011 template <typename Implementation, typename Executors, typename... Signatures>
1012 class initiate_co_composed
1013 {
1014 public:
1015   using executor_type = typename composed_io_executors<Executors>::head_type;
1016 
1017   template <typename I>
1018   initiate_co_composed(I&& impl, composed_io_executors<Executors>&& executors)
1019     : implementation_(static_cast<I&&>(impl)),
1020       executors_(std::move(executors))
1021   {
1022   }
1023 
1024   executor_type get_executor() const noexcept
1025   {
1026     return executors_.head_;
1027   }
1028 
1029   template <typename Handler, typename... InitArgs>
1030   void operator()(Handler&& handler, InitArgs&&... init_args) const &
1031   {
1032     using handler_type = decay_t<Handler>;
1033     using returns_type = co_composed_returns<Signatures...>;
1034     co_composed_on_suspend on_suspend{};
1035     implementation_(
1036         co_composed_state<Executors, handler_type, returns_type>(
1037           executors_, static_cast<Handler&&>(handler), on_suspend),
1038         static_cast<InitArgs&&>(init_args)...);
1039     if (on_suspend.fn_)
1040       on_suspend.fn_(on_suspend.arg_);
1041   }
1042 
1043   template <typename Handler, typename... InitArgs>
1044   void operator()(Handler&& handler, InitArgs&&... init_args) &&
1045   {
1046     using handler_type = decay_t<Handler>;
1047     using returns_type = co_composed_returns<Signatures...>;
1048     co_composed_on_suspend on_suspend{};
1049     std::move(implementation_)(
1050         co_composed_state<Executors, handler_type, returns_type>(
1051           std::move(executors_), static_cast<Handler&&>(handler), on_suspend),
1052         static_cast<InitArgs&&>(init_args)...);
1053     if (on_suspend.fn_)
1054       on_suspend.fn_(on_suspend.arg_);
1055   }
1056 
1057 private:
1058   Implementation implementation_;
1059   composed_io_executors<Executors> executors_;
1060 };
1061 
1062 template <typename Implementation, typename... Signatures>
1063 class initiate_co_composed<Implementation, void(), Signatures...>
1064 {
1065 public:
1066   template <typename I>
1067   initiate_co_composed(I&& impl, composed_io_executors<void()>&&)
1068     : implementation_(static_cast<I&&>(impl))
1069   {
1070   }
1071 
1072   template <typename Handler, typename... InitArgs>
1073   void operator()(Handler&& handler, InitArgs&&... init_args) const &
1074   {
1075     using handler_type = decay_t<Handler>;
1076     using returns_type = co_composed_returns<Signatures...>;
1077     co_composed_on_suspend on_suspend{};
1078     implementation_(
1079         co_composed_state<void(), handler_type, returns_type>(
1080           composed_io_executors<void()>(),
1081           static_cast<Handler&&>(handler), on_suspend),
1082         static_cast<InitArgs&&>(init_args)...);
1083     if (on_suspend.fn_)
1084       on_suspend.fn_(on_suspend.arg_);
1085   }
1086 
1087   template <typename Handler, typename... InitArgs>
1088   void operator()(Handler&& handler, InitArgs&&... init_args) &&
1089   {
1090     using handler_type = decay_t<Handler>;
1091     using returns_type = co_composed_returns<Signatures...>;
1092     co_composed_on_suspend on_suspend{};
1093     std::move(implementation_)(
1094         co_composed_state<void(), handler_type, returns_type>(
1095           composed_io_executors<void()>(),
1096           static_cast<Handler&&>(handler), on_suspend),
1097         static_cast<InitArgs&&>(init_args)...);
1098     if (on_suspend.fn_)
1099       on_suspend.fn_(on_suspend.arg_);
1100   }
1101 
1102 private:
1103   Implementation implementation_;
1104 };
1105 
1106 template <typename... Signatures, typename Implementation, typename Executors>
1107 inline initiate_co_composed<decay_t<Implementation>, Executors, Signatures...>
1108 make_initiate_co_composed(Implementation&& implementation,
1109     composed_io_executors<Executors>&& executors)
1110 {
1111   return initiate_co_composed<
1112     decay_t<Implementation>, Executors, Signatures...>(
1113         static_cast<Implementation&&>(implementation), std::move(executors));
1114 }
1115 
1116 } // namespace detail
1117 
1118 #if !defined(GENERATING_DOCUMENTATION)
1119 
1120 template <template <typename, typename> class Associator,
1121     typename Executors, typename Handler, typename Return,
1122     typename Signature, typename DefaultCandidate>
1123 struct associator<Associator,
1124     detail::co_composed_handler<Executors, Handler, Return, Signature>,
1125     DefaultCandidate>
1126   : Associator<Handler, DefaultCandidate>
1127 {
1128   static typename Associator<Handler, DefaultCandidate>::type get(
1129       const detail::co_composed_handler<
1130         Executors, Handler, Return, Signature>& h) noexcept
1131   {
1132     return Associator<Handler, DefaultCandidate>::get(
1133         h.promise().state().handler());
1134   }
1135 
1136   static auto get(
1137       const detail::co_composed_handler<
1138         Executors, Handler, Return, Signature>& h,
1139       const DefaultCandidate& c) noexcept
1140     -> decltype(
1141       Associator<Handler, DefaultCandidate>::get(
1142         h.promise().state().handler(), c))
1143   {
1144     return Associator<Handler, DefaultCandidate>::get(
1145         h.promise().state().handler(), c);
1146   }
1147 };
1148 
1149 #endif // !defined(GENERATING_DOCUMENTATION)
1150 
1151 } // namespace asio
1152 } // namespace boost
1153 
1154 #if !defined(GENERATING_DOCUMENTATION)
1155 # if defined(BOOST_ASIO_HAS_STD_COROUTINE)
1156 namespace std {
1157 # else // defined(BOOST_ASIO_HAS_STD_COROUTINE)
1158 namespace std { namespace experimental {
1159 # endif // defined(BOOST_ASIO_HAS_STD_COROUTINE)
1160 
1161 template <typename C, typename Executors,
1162     typename Handler, typename Return, typename... Args>
1163 struct coroutine_traits<void, C&,
1164     boost::asio::detail::co_composed_state<Executors, Handler, Return>, Args...>
1165 {
1166   using promise_type =
1167     boost::asio::detail::co_composed_promise<Executors, Handler, Return>;
1168 };
1169 
1170 template <typename C, typename Executors,
1171     typename Handler, typename Return, typename... Args>
1172 struct coroutine_traits<void, C&&,
1173     boost::asio::detail::co_composed_state<Executors, Handler, Return>, Args...>
1174 {
1175   using promise_type =
1176     boost::asio::detail::co_composed_promise<Executors, Handler, Return>;
1177 };
1178 
1179 template <typename Executors, typename Handler,
1180     typename Return, typename... Args>
1181 struct coroutine_traits<void,
1182     boost::asio::detail::co_composed_state<Executors, Handler, Return>, Args...>
1183 {
1184   using promise_type =
1185     boost::asio::detail::co_composed_promise<Executors, Handler, Return>;
1186 };
1187 
1188 # if defined(BOOST_ASIO_HAS_STD_COROUTINE)
1189 } // namespace std
1190 # else // defined(BOOST_ASIO_HAS_STD_COROUTINE)
1191 }} // namespace std::experimental
1192 # endif // defined(BOOST_ASIO_HAS_STD_COROUTINE)
1193 #endif // !defined(GENERATING_DOCUMENTATION)
1194 
1195 namespace boost {
1196 namespace asio {
1197 
1198 /// Creates an initiation function object that may be used to launch a
1199 /// coroutine-based composed asynchronous operation.
1200 /**
1201  * The co_composed utility simplifies the implementation of composed
1202  * asynchronous operations by automatically adapting a coroutine to be an
1203  * initiation function object for use with @c async_initiate. When awaiting
1204  * asynchronous operations, the coroutine automatically uses a conforming
1205  * intermediate completion handler.
1206  *
1207  * @param implementation A function object that contains the coroutine-based
1208  * implementation of the composed asynchronous operation. The first argument to
1209  * the function object represents the state of the operation, and may be used
1210  * to test for cancellation. The remaining arguments are those passed to @c
1211  * async_initiate after the completion token.
1212  *
1213  * @param io_objects_or_executors Zero or more I/O objects or I/O executors for
1214  * which outstanding work must be maintained while the operation is incomplete.
1215  *
1216  * @par Per-Operation Cancellation
1217  * By default, terminal per-operation cancellation is enabled for composed
1218  * operations that use co_composed. To disable cancellation for the composed
1219  * operation, or to alter its supported cancellation types, call the state's
1220  * @c reset_cancellation_state function.
1221  *
1222  * @par Examples
1223  * The following example illustrates manual error handling and explicit checks
1224  * for cancellation. The completion handler is invoked via a @c co_yield to the
1225  * state's @c complete function, which never returns.
1226  *
1227  * @code template <typename CompletionToken>
1228  * auto async_echo(tcp::socket& socket,
1229  *     CompletionToken&& token)
1230  * {
1231  *   return boost::asio::async_initiate<
1232  *     CompletionToken, void(boost::system::error_code)>(
1233  *       boost::asio::co_composed(
1234  *         [](auto state, tcp::socket& socket) -> void
1235  *         {
1236  *           state.reset_cancellation_state(
1237  *             boost::asio::enable_terminal_cancellation());
1238  *
1239  *           while (!state.cancelled())
1240  *           {
1241  *             char data[1024];
1242  *             auto [e1, n1] =
1243  *               co_await socket.async_read_some(
1244  *                 boost::asio::buffer(data));
1245  *
1246  *             if (e1)
1247  *               co_yield state.complete(e1);
1248  *
1249  *             if (!!state.cancelled())
1250  *               co_yield state.complete(
1251  *                 make_error_code(boost::asio::error::operation_aborted));
1252  *
1253  *             auto [e2, n2] =
1254  *               co_await boost::asio::async_write(socket,
1255  *                 boost::asio::buffer(data, n1));
1256  *
1257  *             if (e2)
1258  *               co_yield state.complete(e2);
1259  *           }
1260  *         }, socket),
1261  *       token, std::ref(socket));
1262  * } @endcode
1263  *
1264  * This next example shows exception-based error handling and implicit checks
1265  * for cancellation. The completion handler is invoked after returning from the
1266  * coroutine via @c co_return. Valid @c co_return values are specified using
1267  * completion signatures passed to the @c co_composed function.
1268  *
1269  * @code template <typename CompletionToken>
1270  * auto async_echo(tcp::socket& socket,
1271  *     CompletionToken&& token)
1272  * {
1273  *   return boost::asio::async_initiate<
1274  *     CompletionToken, void(boost::system::error_code)>(
1275  *       boost::asio::co_composed<
1276  *         void(boost::system::error_code)>(
1277  *           [](auto state, tcp::socket& socket) -> void
1278  *           {
1279  *             try
1280  *             {
1281  *               state.throw_if_cancelled(true);
1282  *               state.reset_cancellation_state(
1283  *                 boost::asio::enable_terminal_cancellation());
1284  *
1285  *               for (;;)
1286  *               {
1287  *                 char data[1024];
1288  *                 std::size_t n = co_await socket.async_read_some(
1289  *                     boost::asio::buffer(data));
1290  *
1291  *                 co_await boost::asio::async_write(socket,
1292  *                     boost::asio::buffer(data, n));
1293  *               }
1294  *             }
1295  *             catch (const boost::system::system_error& e)
1296  *             {
1297  *               co_return {e.code()};
1298  *             }
1299  *           }, socket),
1300  *       token, std::ref(socket));
1301  * } @endcode
1302  */
1303 template <BOOST_ASIO_COMPLETION_SIGNATURE... Signatures,
1304     typename Implementation, typename... IoObjectsOrExecutors>
1305 inline auto co_composed(Implementation&& implementation,
1306     IoObjectsOrExecutors&&... io_objects_or_executors)
1307 {
1308   return detail::make_initiate_co_composed<Signatures...>(
1309       static_cast<Implementation&&>(implementation),
1310       detail::make_composed_io_executors(
1311         detail::get_composed_io_executor(
1312           static_cast<IoObjectsOrExecutors&&>(
1313             io_objects_or_executors))...));
1314 }
1315 
1316 } // namespace asio
1317 } // namespace boost
1318 
1319 #include <boost/asio/detail/pop_options.hpp>
1320 
1321 #endif // defined(BOOST_ASIO_HAS_CO_AWAIT) || defined(GENERATING_DOCUMENTATION)
1322 
1323 #endif // BOOST_ASIO_CO_COMPOSED_HPP