// // experimental/impl/co_composed.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_IMPL_EXPERIMENTAL_CO_COMPOSED_HPP #define ASIO_IMPL_EXPERIMENTAL_CO_COMPOSED_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include <new> #include <tuple> #include <variant> #include "asio/associated_cancellation_slot.hpp" #include "asio/associator.hpp" #include "asio/async_result.hpp" #include "asio/cancellation_state.hpp" #include "asio/detail/composed_work.hpp" #include "asio/detail/recycling_allocator.hpp" #include "asio/detail/throw_error.hpp" #include "asio/detail/type_traits.hpp" #include "asio/error.hpp" #if defined(ASIO_HAS_STD_COROUTINE) # include <coroutine> #else // defined(ASIO_HAS_STD_COROUTINE) # include <experimental/coroutine> #endif // defined(ASIO_HAS_STD_COROUTINE) #if defined(ASIO_ENABLE_HANDLER_TRACKING) # if defined(ASIO_HAS_SOURCE_LOCATION) # include "asio/detail/source_location.hpp" # endif // defined(ASIO_HAS_SOURCE_LOCATION) #endif // defined(ASIO_ENABLE_HANDLER_TRACKING) #include "asio/detail/push_options.hpp" namespace asio { namespace experimental { namespace detail { #if defined(ASIO_HAS_STD_COROUTINE) using std::coroutine_handle; using std::suspend_always; using std::suspend_never; #else // defined(ASIO_HAS_STD_COROUTINE) using std::experimental::coroutine_handle; using std::experimental::suspend_always; using std::experimental::suspend_never; #endif // defined(ASIO_HAS_STD_COROUTINE) using asio::detail::composed_io_executors; using asio::detail::composed_work; using asio::detail::composed_work_guard; using asio::detail::get_composed_io_executor; using asio::detail::make_composed_io_executors; using asio::detail::recycling_allocator; using asio::detail::throw_error; template <typename Executors, typename Handler, typename Return> class co_composed_state; template <typename Executors, typename Handler, typename Return> class co_composed_handler_base; template <typename Executors, typename Handler, typename Return> class co_composed_promise; template <completion_signature... Signatures> class co_composed_returns { }; struct co_composed_on_suspend { void (*fn_)(void*) = nullptr; void* arg_ = nullptr; }; template <typename... T> struct co_composed_completion : std::tuple<T&&...> { template <typename... U> co_composed_completion(U&&... u) noexcept : std::tuple<T&&...>(std::forward<U>(u)...) { } }; template <typename Executors, typename Handler, typename Return, typename Signature> class co_composed_state_return_overload; template <typename Executors, typename Handler, typename Return, typename R, typename... Args> class co_composed_state_return_overload< Executors, Handler, Return, R(Args...)> { public: using derived_type = co_composed_state<Executors, Handler, Return>; using promise_type = co_composed_promise<Executors, Handler, Return>; using return_type = std::tuple<Args...>; void on_cancellation_complete_with(Args... args) { derived_type& state = *static_cast<derived_type*>(this); state.return_value_ = std::make_tuple(std::move(args)...); state.cancellation_on_suspend_fn( [](void* p) { auto& promise = *static_cast<promise_type*>(p); co_composed_handler_base<Executors, Handler, Return> composed_handler(promise); Handler handler(std::move(promise.state().handler_)); return_type result( std::move(std::get<return_type>(promise.state().return_value_))); co_composed_handler_base<Executors, Handler, Return>(std::move(composed_handler)); std::apply(std::move(handler), std::move(result)); }); } }; template <typename Executors, typename Handler, typename Return> class co_composed_state_return; template <typename Executors, typename Handler, typename... Signatures> class co_composed_state_return< Executors, Handler, co_composed_returns<Signatures...>> : public co_composed_state_return_overload<Executors, Handler, co_composed_returns<Signatures...>, Signatures>... { public: using co_composed_state_return_overload<Executors, Handler, co_composed_returns<Signatures...>, Signatures>::on_cancellation_complete_with...; private: template <typename, typename, typename, typename> friend class co_composed_promise_return_overload; template <typename, typename, typename, typename> friend class co_composed_state_return_overload; std::variant<std::monostate, typename co_composed_state_return_overload< Executors, Handler, co_composed_returns<Signatures...>, Signatures>::return_type...> return_value_; }; template <typename Executors, typename Handler, typename Return, typename... Signatures> struct co_composed_state_default_cancellation_on_suspend_impl; template <typename Executors, typename Handler, typename Return> struct co_composed_state_default_cancellation_on_suspend_impl< Executors, Handler, Return> { static constexpr void (*fn())(void*) { return nullptr; } }; template <typename Executors, typename Handler, typename Return, typename R, typename... Args, typename... Signatures> struct co_composed_state_default_cancellation_on_suspend_impl< Executors, Handler, Return, R(Args...), Signatures...> { static constexpr void (*fn())(void*) { return co_composed_state_default_cancellation_on_suspend_impl< Executors, Handler, Return, Signatures...>::fn(); } }; template <typename Executors, typename Handler, typename Return, typename R, typename... Args, typename... Signatures> struct co_composed_state_default_cancellation_on_suspend_impl<Executors, Handler, Return, R(asio::error_code, Args...), Signatures...> { using promise_type = co_composed_promise<Executors, Handler, Return>; using return_type = std::tuple<asio::error_code, Args...>; static constexpr void (*fn())(void*) { if constexpr ((is_constructible<Args>::value && ...)) { return [](void* p) { auto& promise = *static_cast<promise_type*>(p); co_composed_handler_base<Executors, Handler, Return> composed_handler(promise); Handler handler(std::move(promise.state().handler_)); co_composed_handler_base<Executors, Handler, Return>(std::move(composed_handler)); std::move(handler)( asio::error_code(asio::error::operation_aborted), Args{}...); }; } else { return co_composed_state_default_cancellation_on_suspend_impl< Executors, Handler, Return, Signatures...>::fn(); } } }; template <typename Executors, typename Handler, typename Return, typename R, typename... Args, typename... Signatures> struct co_composed_state_default_cancellation_on_suspend_impl<Executors, Handler, Return, R(std::exception_ptr, Args...), Signatures...> { using promise_type = co_composed_promise<Executors, Handler, Return>; using return_type = std::tuple<std::exception_ptr, Args...>; static constexpr void (*fn())(void*) { if constexpr ((is_constructible<Args>::value && ...)) { return [](void* p) { auto& promise = *static_cast<promise_type*>(p); co_composed_handler_base<Executors, Handler, Return> composed_handler(promise); Handler handler(std::move(promise.state().handler_)); co_composed_handler_base<Executors, Handler, Return>(std::move(composed_handler)); std::move(handler)( std::make_exception_ptr( asio::system_error( asio::error::operation_aborted, "co_await")), Args{}...); }; } else { return co_composed_state_default_cancellation_on_suspend_impl< Executors, Handler, Return, Signatures...>::fn(); } } }; template <typename Executors, typename Handler, typename Return> struct co_composed_state_default_cancellation_on_suspend; template <typename Executors, typename Handler, typename... Signatures> struct co_composed_state_default_cancellation_on_suspend< Executors, Handler, co_composed_returns<Signatures...>> : co_composed_state_default_cancellation_on_suspend_impl<Executors, Handler, co_composed_returns<Signatures...>, Signatures...> { }; template <typename Executors, typename Handler, typename Return> class co_composed_state_cancellation { public: using cancellation_slot_type = cancellation_slot; cancellation_slot_type get_cancellation_slot() const noexcept { return cancellation_state_.slot(); } cancellation_state get_cancellation_state() const noexcept { return cancellation_state_; } void reset_cancellation_state() { cancellation_state_ = cancellation_state( (get_associated_cancellation_slot)( static_cast<co_composed_state<Executors, Handler, Return>*>( this)->handler())); } template <typename Filter> void reset_cancellation_state(Filter filter) { cancellation_state_ = cancellation_state( (get_associated_cancellation_slot)( static_cast<co_composed_state<Executors, Handler, Return>*>( this)->handler()), filter, filter); } template <typename InFilter, typename OutFilter> void reset_cancellation_state(InFilter&& in_filter, OutFilter&& out_filter) { cancellation_state_ = cancellation_state( (get_associated_cancellation_slot)( static_cast<co_composed_state<Executors, Handler, Return>*>( this)->handler()), std::forward<InFilter>(in_filter), std::forward<OutFilter>(out_filter)); } cancellation_type_t cancelled() const noexcept { return cancellation_state_.cancelled(); } void clear_cancellation_slot() noexcept { cancellation_state_.slot().clear(); } [[nodiscard]] bool throw_if_cancelled() const noexcept { return throw_if_cancelled_; } void throw_if_cancelled(bool b) noexcept { throw_if_cancelled_ = b; } [[nodiscard]] bool complete_if_cancelled() const noexcept { return complete_if_cancelled_; } void complete_if_cancelled(bool b) noexcept { complete_if_cancelled_ = b; } private: template <typename, typename, typename> friend class co_composed_promise; template <typename, typename, typename, typename> friend class co_composed_state_return_overload; void cancellation_on_suspend_fn(void (*fn)(void*)) { cancellation_on_suspend_fn_ = fn; } void check_for_cancellation_on_transform() { if (throw_if_cancelled_ && !!cancelled()) throw_error(asio::error::operation_aborted, "co_await"); } bool check_for_cancellation_on_suspend( co_composed_promise<Executors, Handler, Return>& promise) noexcept { if (complete_if_cancelled_ && !!cancelled() && cancellation_on_suspend_fn_) { promise.state().work_.reset(); promise.state().on_suspend_->fn_ = cancellation_on_suspend_fn_; promise.state().on_suspend_->arg_ = &promise; return false; } return true; } cancellation_state cancellation_state_; void (*cancellation_on_suspend_fn_)(void*) = co_composed_state_default_cancellation_on_suspend< Executors, Handler, Return>::fn(); bool throw_if_cancelled_ = false; bool complete_if_cancelled_ = true; }; template <typename Executors, typename Handler, typename Return> requires is_same< typename associated_cancellation_slot< Handler, cancellation_slot >::asio_associated_cancellation_slot_is_unspecialised, void>::value class co_composed_state_cancellation<Executors, Handler, Return> { public: void reset_cancellation_state() { } template <typename Filter> void reset_cancellation_state(Filter) { } template <typename InFilter, typename OutFilter> void reset_cancellation_state(InFilter&&, OutFilter&&) { } cancellation_type_t cancelled() const noexcept { return cancellation_type::none; } void clear_cancellation_slot() noexcept { } [[nodiscard]] bool throw_if_cancelled() const noexcept { return false; } void throw_if_cancelled(bool) noexcept { } [[nodiscard]] bool complete_if_cancelled() const noexcept { return false; } void complete_if_cancelled(bool) noexcept { } private: template <typename, typename, typename> friend class co_composed_promise; template <typename, typename, typename, typename> friend class co_composed_state_return_overload; void cancellation_on_suspend_fn(void (*)(void*)) { } void check_for_cancellation_on_transform() noexcept { } bool check_for_cancellation_on_suspend( co_composed_promise<Executors, Handler, Return>&) noexcept { return true; } }; template <typename Executors, typename Handler, typename Return> class co_composed_state : public co_composed_state_return<Executors, Handler, Return>, public co_composed_state_cancellation<Executors, Handler, Return> { public: using io_executor_type = typename composed_work_guard< typename composed_work<Executors>::head_type>::executor_type; template <typename H> co_composed_state(composed_io_executors<Executors>&& executors, H&& h, co_composed_on_suspend& on_suspend) : work_(std::move(executors)), handler_(std::forward<H>(h)), on_suspend_(&on_suspend) { this->reset_cancellation_state(enable_terminal_cancellation()); } io_executor_type get_io_executor() const noexcept { return work_.head_.get_executor(); } template <typename... Args> [[nodiscard]] co_composed_completion<Args...> complete(Args&&... args) requires requires { declval<Handler>()(std::forward<Args>(args)...); } { return co_composed_completion<Args...>(std::forward<Args>(args)...); } const Handler& handler() const noexcept { return handler_; } private: template <typename, typename, typename> friend class co_composed_handler_base; template <typename, typename, typename> friend class co_composed_promise; template <typename, typename, typename, typename> friend class co_composed_promise_return_overload; template <typename, typename, typename> friend class co_composed_state_cancellation; template <typename, typename, typename, typename> friend class co_composed_state_return_overload; template <typename, typename, typename, typename...> friend struct co_composed_state_default_cancellation_on_suspend_impl; composed_work<Executors> work_; Handler handler_; co_composed_on_suspend* on_suspend_; }; template <typename Executors, typename Handler, typename Return> class co_composed_handler_cancellation { public: using cancellation_slot_type = cancellation_slot; cancellation_slot_type get_cancellation_slot() const noexcept { return static_cast< const co_composed_handler_base<Executors, Handler, Return>*>( this)->promise().state().get_cancellation_slot(); } }; template <typename Executors, typename Handler, typename Return> requires is_same< typename associated_cancellation_slot< Handler, cancellation_slot >::asio_associated_cancellation_slot_is_unspecialised, void>::value class co_composed_handler_cancellation<Executors, Handler, Return> { }; template <typename Executors, typename Handler, typename Return> class co_composed_handler_base : public co_composed_handler_cancellation<Executors, Handler, Return> { public: co_composed_handler_base( co_composed_promise<Executors, Handler, Return>& p) noexcept : p_(&p) { } co_composed_handler_base(co_composed_handler_base&& other) noexcept : p_(std::exchange(other.p_, nullptr)) { } ~co_composed_handler_base() { if (p_) [[unlikely]] p_->destroy(); } co_composed_promise<Executors, Handler, Return>& promise() const noexcept { return *p_; } protected: void resume(void* result) { co_composed_on_suspend on_suspend{}; std::exchange(p_, nullptr)->resume(p_, result, on_suspend); if (on_suspend.fn_) on_suspend.fn_(on_suspend.arg_); } private: co_composed_promise<Executors, Handler, Return>* p_; }; template <typename Executors, typename Handler, typename Return, typename Signature> class co_composed_handler; template <typename Executors, typename Handler, typename Return, typename R, typename... Args> class co_composed_handler<Executors, Handler, Return, R(Args...)> : public co_composed_handler_base<Executors, Handler, Return> { public: using co_composed_handler_base<Executors, Handler, Return>::co_composed_handler_base; using result_type = std::tuple<decay_t<Args>...>; template <typename... T> void operator()(T&&... args) { result_type result(std::forward<T>(args)...); this->resume(&result); } static auto on_resume(void* result) { auto& args = *static_cast<result_type*>(result); if constexpr (sizeof...(Args) == 0) return; else if constexpr (sizeof...(Args) == 1) return std::move(std::get<0>(args)); else return std::move(args); } }; template <typename Executors, typename Handler, typename Return, typename R, typename... Args> class co_composed_handler<Executors, Handler, Return, R(asio::error_code, Args...)> : public co_composed_handler_base<Executors, Handler, Return> { public: using co_composed_handler_base<Executors, Handler, Return>::co_composed_handler_base; using args_type = std::tuple<decay_t<Args>...>; using result_type = std::tuple<asio::error_code, args_type>; template <typename... T> void operator()(const asio::error_code& ec, T&&... args) { result_type result(ec, args_type(std::forward<T>(args)...)); this->resume(&result); } static auto on_resume(void* result) { auto& [ec, args] = *static_cast<result_type*>(result); throw_error(ec); if constexpr (sizeof...(Args) == 0) return; else if constexpr (sizeof...(Args) == 1) return std::move(std::get<0>(args)); else return std::move(args); } }; template <typename Executors, typename Handler, typename Return, typename R, typename... Args> class co_composed_handler<Executors, Handler, Return, R(std::exception_ptr, Args...)> : public co_composed_handler_base<Executors, Handler, Return> { public: using co_composed_handler_base<Executors, Handler, Return>::co_composed_handler_base; using args_type = std::tuple<decay_t<Args>...>; using result_type = std::tuple<std::exception_ptr, args_type>; template <typename... T> void operator()(std::exception_ptr ex, T&&... args) { result_type result(std::move(ex), args_type(std::forward<T>(args)...)); this->resume(&result); } static auto on_resume(void* result) { auto& [ex, args] = *static_cast<result_type*>(result); if (ex) std::rethrow_exception(ex); if constexpr (sizeof...(Args) == 0) return; else if constexpr (sizeof...(Args) == 1) return std::move(std::get<0>(args)); else return std::move(args); } }; template <typename Executors, typename Handler, typename Return> class co_composed_promise_return; template <typename Executors, typename Handler> class co_composed_promise_return<Executors, Handler, co_composed_returns<>> { public: auto final_suspend() noexcept { return suspend_never(); } void return_void() noexcept { } }; template <typename Executors, typename Handler, typename Return, typename Signature> class co_composed_promise_return_overload; template <typename Executors, typename Handler, typename Return, typename R, typename... Args> class co_composed_promise_return_overload< Executors, Handler, Return, R(Args...)> { public: using derived_type = co_composed_promise<Executors, Handler, Return>; using return_type = std::tuple<Args...>; void return_value(std::tuple<Args...>&& value) { derived_type& promise = *static_cast<derived_type*>(this); promise.state().return_value_ = std::move(value); promise.state().work_.reset(); promise.state().on_suspend_->arg_ = this; promise.state().on_suspend_->fn_ = [](void* p) { auto& promise = *static_cast<derived_type*>(p); co_composed_handler_base<Executors, Handler, Return> composed_handler(promise); Handler handler(std::move(promise.state().handler_)); return_type result( std::move(std::get<return_type>(promise.state().return_value_))); co_composed_handler_base<Executors, Handler, Return>(std::move(composed_handler)); std::apply(std::move(handler), std::move(result)); }; } }; template <typename Executors, typename Handler, typename... Signatures> class co_composed_promise_return<Executors, Handler, co_composed_returns<Signatures...>> : public co_composed_promise_return_overload<Executors, Handler, co_composed_returns<Signatures...>, Signatures>... { public: auto final_suspend() noexcept { return suspend_always(); } using co_composed_promise_return_overload<Executors, Handler, co_composed_returns<Signatures...>, Signatures>::return_value...; private: template <typename, typename, typename, typename> friend class co_composed_promise_return_overload; }; template <typename Executors, typename Handler, typename Return> class co_composed_promise : public co_composed_promise_return<Executors, Handler, Return> { public: template <typename... Args> void* operator new(std::size_t size, co_composed_state<Executors, Handler, Return>& state, Args&&...) { block_allocator_type allocator( (get_associated_allocator)(state.handler_, recycling_allocator<void>())); block* base_ptr = std::allocator_traits<block_allocator_type>::allocate( allocator, blocks(sizeof(allocator_type)) + blocks(size)); new (static_cast<void*>(base_ptr)) allocator_type(std::move(allocator)); return base_ptr + blocks(sizeof(allocator_type)); } template <typename C, typename... Args> void* operator new(std::size_t size, C&&, co_composed_state<Executors, Handler, Return>& state, Args&&...) { return co_composed_promise::operator new(size, state); } void operator delete(void* ptr, std::size_t size) { block* base_ptr = static_cast<block*>(ptr) - blocks(sizeof(allocator_type)); allocator_type* allocator_ptr = std::launder( static_cast<allocator_type*>(static_cast<void*>(base_ptr))); block_allocator_type block_allocator(std::move(*allocator_ptr)); allocator_ptr->~allocator_type(); std::allocator_traits<block_allocator_type>::deallocate(block_allocator, base_ptr, blocks(sizeof(allocator_type)) + blocks(size)); } template <typename... Args> co_composed_promise( co_composed_state<Executors, Handler, Return>& state, Args&&...) : state_(state) { } template <typename C, typename... Args> co_composed_promise(C&&, co_composed_state<Executors, Handler, Return>& state, Args&&...) : state_(state) { } void destroy() noexcept { coroutine_handle<co_composed_promise>::from_promise(*this).destroy(); } void resume(co_composed_promise*& owner, void* result, co_composed_on_suspend& on_suspend) { state_.on_suspend_ = &on_suspend; state_.clear_cancellation_slot(); owner_ = &owner; result_ = result; coroutine_handle<co_composed_promise>::from_promise(*this).resume(); } co_composed_state<Executors, Handler, Return>& state() noexcept { return state_; } void get_return_object() noexcept { } auto initial_suspend() noexcept { return suspend_never(); } void unhandled_exception() { if (owner_) *owner_ = this; throw; } template <async_operation Op> auto await_transform(Op&& op #if defined(ASIO_ENABLE_HANDLER_TRACKING) # if defined(ASIO_HAS_SOURCE_LOCATION) , asio::detail::source_location location = asio::detail::source_location::current() # endif // defined(ASIO_HAS_SOURCE_LOCATION) #endif // defined(ASIO_ENABLE_HANDLER_TRACKING) ) { class [[nodiscard]] awaitable { public: awaitable(Op&& op, co_composed_promise& promise #if defined(ASIO_ENABLE_HANDLER_TRACKING) # if defined(ASIO_HAS_SOURCE_LOCATION) , const asio::detail::source_location& location # endif // defined(ASIO_HAS_SOURCE_LOCATION) #endif // defined(ASIO_ENABLE_HANDLER_TRACKING) ) : op_(std::forward<Op>(op)), promise_(promise) #if defined(ASIO_ENABLE_HANDLER_TRACKING) # if defined(ASIO_HAS_SOURCE_LOCATION) , location_(location) # endif // defined(ASIO_HAS_SOURCE_LOCATION) #endif // defined(ASIO_ENABLE_HANDLER_TRACKING) { } constexpr bool await_ready() const noexcept { return false; } void await_suspend(coroutine_handle<co_composed_promise>) { if (promise_.state_.check_for_cancellation_on_suspend(promise_)) { promise_.state_.on_suspend_->arg_ = this; promise_.state_.on_suspend_->fn_ = [](void* p) { #if defined(ASIO_ENABLE_HANDLER_TRACKING) # if defined(ASIO_HAS_SOURCE_LOCATION) ASIO_HANDLER_LOCATION(( static_cast<awaitable*>(p)->location_.file_name(), static_cast<awaitable*>(p)->location_.line(), static_cast<awaitable*>(p)->location_.function_name())); # endif // defined(ASIO_HAS_SOURCE_LOCATION) #endif // defined(ASIO_ENABLE_HANDLER_TRACKING) std::forward<Op>(static_cast<awaitable*>(p)->op_)( co_composed_handler<Executors, Handler, Return, completion_signature_of_t<Op>>( static_cast<awaitable*>(p)->promise_)); }; } } auto await_resume() { return co_composed_handler<Executors, Handler, Return, completion_signature_of_t<Op>>::on_resume(promise_.result_); } private: Op&& op_; co_composed_promise& promise_; #if defined(ASIO_ENABLE_HANDLER_TRACKING) # if defined(ASIO_HAS_SOURCE_LOCATION) asio::detail::source_location location_; # endif // defined(ASIO_HAS_SOURCE_LOCATION) #endif // defined(ASIO_ENABLE_HANDLER_TRACKING) }; state_.check_for_cancellation_on_transform(); return awaitable{std::forward<Op>(op), *this #if defined(ASIO_ENABLE_HANDLER_TRACKING) # if defined(ASIO_HAS_SOURCE_LOCATION) , location # endif // defined(ASIO_HAS_SOURCE_LOCATION) #endif // defined(ASIO_ENABLE_HANDLER_TRACKING) }; } template <typename... Args> auto yield_value(co_composed_completion<Args...>&& result) { class [[nodiscard]] awaitable { public: awaitable(co_composed_completion<Args...>&& result, co_composed_promise& promise) : result_(std::move(result)), promise_(promise) { } constexpr bool await_ready() const noexcept { return false; } void await_suspend(coroutine_handle<co_composed_promise>) { promise_.state_.work_.reset(); promise_.state_.on_suspend_->arg_ = this; promise_.state_.on_suspend_->fn_ = [](void* p) { awaitable& a = *static_cast<awaitable*>(p); co_composed_handler_base<Executors, Handler, Return> composed_handler(a.promise_); Handler handler(std::move(a.promise_.state_.handler_)); std::tuple<decay_t<Args>...> result( std::move(static_cast<std::tuple<Args&&...>>(a.result_))); co_composed_handler_base<Executors, Handler, Return>(std::move(composed_handler)); std::apply(std::move(handler), std::move(result)); }; } void await_resume() noexcept { } private: co_composed_completion<Args...> result_; co_composed_promise& promise_; }; return awaitable{std::move(result), *this}; } private: using allocator_type = associated_allocator_t<Handler, recycling_allocator<void>>; union block { std::max_align_t max_align; alignas(allocator_type) char pad[alignof(allocator_type)]; }; using block_allocator_type = typename std::allocator_traits<allocator_type> ::template rebind_alloc<block>; static constexpr std::size_t blocks(std::size_t size) { return (size + sizeof(block) - 1) / sizeof(block); } co_composed_state<Executors, Handler, Return>& state_; co_composed_promise** owner_ = nullptr; void* result_ = nullptr; }; template <typename Implementation, typename Executors, typename... Signatures> class initiate_co_composed { public: using executor_type = typename composed_io_executors<Executors>::head_type; template <typename I> initiate_co_composed(I&& impl, composed_io_executors<Executors>&& executors) : implementation_(std::forward<I>(impl)), executors_(std::move(executors)) { } executor_type get_executor() const noexcept { return executors_.head_; } template <typename Handler, typename... InitArgs> void operator()(Handler&& handler, InitArgs&&... init_args) const & { using handler_type = decay_t<Handler>; using returns_type = co_composed_returns<Signatures...>; co_composed_on_suspend on_suspend{}; implementation_( co_composed_state<Executors, handler_type, returns_type>( executors_, std::forward<Handler>(handler), on_suspend), std::forward<InitArgs>(init_args)...); if (on_suspend.fn_) on_suspend.fn_(on_suspend.arg_); } template <typename Handler, typename... InitArgs> void operator()(Handler&& handler, InitArgs&&... init_args) && { using handler_type = decay_t<Handler>; using returns_type = co_composed_returns<Signatures...>; co_composed_on_suspend on_suspend{}; std::move(implementation_)( co_composed_state<Executors, handler_type, returns_type>( std::move(executors_), std::forward<Handler>(handler), on_suspend), std::forward<InitArgs>(init_args)...); if (on_suspend.fn_) on_suspend.fn_(on_suspend.arg_); } private: Implementation implementation_; composed_io_executors<Executors> executors_; }; template <typename... Signatures, typename Implementation, typename Executors> inline initiate_co_composed<Implementation, Executors, Signatures...> make_initiate_co_composed(Implementation&& implementation, composed_io_executors<Executors>&& executors) { return initiate_co_composed< decay_t<Implementation>, Executors, Signatures...>( std::forward<Implementation>(implementation), std::move(executors)); } } // namespace detail template <completion_signature... Signatures, typename Implementation, typename... IoObjectsOrExecutors> inline auto co_composed(Implementation&& implementation, IoObjectsOrExecutors&&... io_objects_or_executors) { return detail::make_initiate_co_composed<Signatures...>( std::forward<Implementation>(implementation), detail::make_composed_io_executors( detail::get_composed_io_executor( std::forward<IoObjectsOrExecutors>( io_objects_or_executors))...)); } } // namespace experimental #if !defined(GENERATING_DOCUMENTATION) template <template <typename, typename> class Associator, typename Executors, typename Handler, typename Return, typename Signature, typename DefaultCandidate> struct associator<Associator, experimental::detail::co_composed_handler< Executors, Handler, Return, Signature>, DefaultCandidate> : Associator<Handler, DefaultCandidate> { static typename Associator<Handler, DefaultCandidate>::type get( const experimental::detail::co_composed_handler< Executors, Handler, Return, Signature>& h) noexcept { return Associator<Handler, DefaultCandidate>::get( h.promise().state().handler()); } static auto get( const experimental::detail::co_composed_handler< Executors, Handler, Return, Signature>& h, const DefaultCandidate& c) noexcept -> decltype( Associator<Handler, DefaultCandidate>::get( h.promise().state().handler(), c)) { return Associator<Handler, DefaultCandidate>::get( h.promise().state().handler(), c); } }; #endif // !defined(GENERATING_DOCUMENTATION) } // namespace asio #if !defined(GENERATING_DOCUMENTATION) # if defined(ASIO_HAS_STD_COROUTINE) namespace std { # else // defined(ASIO_HAS_STD_COROUTINE) namespace std { namespace experimental { # endif // defined(ASIO_HAS_STD_COROUTINE) template <typename C, typename Executors, typename Handler, typename Return, typename... Args> struct coroutine_traits<void, C&, asio::experimental::detail::co_composed_state< Executors, Handler, Return>, Args...> { using promise_type = asio::experimental::detail::co_composed_promise< Executors, Handler, Return>; }; template <typename C, typename Executors, typename Handler, typename Return, typename... Args> struct coroutine_traits<void, C&&, asio::experimental::detail::co_composed_state< Executors, Handler, Return>, Args...> { using promise_type = asio::experimental::detail::co_composed_promise< Executors, Handler, Return>; }; template <typename Executors, typename Handler, typename Return, typename... Args> struct coroutine_traits<void, asio::experimental::detail::co_composed_state< Executors, Handler, Return>, Args...> { using promise_type = asio::experimental::detail::co_composed_promise< Executors, Handler, Return>; }; # if defined(ASIO_HAS_STD_COROUTINE) } // namespace std # else // defined(ASIO_HAS_STD_COROUTINE) }} // namespace std::experimental # endif // defined(ASIO_HAS_STD_COROUTINE) #endif // !defined(GENERATING_DOCUMENTATION) #include "asio/detail/pop_options.hpp" #endif // ASIO_IMPL_EXPERIMENTAL_CO_COMPOSED_HPP