1223 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			C++
		
	
	
	
		
		
			
		
	
	
			1223 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			C++
		
	
	
	
|  | //
 | ||
|  | // experimental/impl/coro.hpp
 | ||
|  | // ~~~~~~~~~~~~~~~~~~~~~~~~~~
 | ||
|  | //
 | ||
|  | // Copyright (c) 2021-2023 Klemens D. Morgenstern
 | ||
|  | //                         (klemens dot morgenstern at gmx dot net)
 | ||
|  | //
 | ||
|  | // 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_EXPERIMENTAL_IMPL_CORO_HPP
 | ||
|  | #define ASIO_EXPERIMENTAL_IMPL_CORO_HPP
 | ||
|  | 
 | ||
|  | #if defined(_MSC_VER) && (_MSC_VER >= 1200)
 | ||
|  | # pragma once
 | ||
|  | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
 | ||
|  | 
 | ||
|  | #include "asio/detail/config.hpp"
 | ||
|  | #include "asio/append.hpp"
 | ||
|  | #include "asio/associated_cancellation_slot.hpp"
 | ||
|  | #include "asio/bind_allocator.hpp"
 | ||
|  | #include "asio/deferred.hpp"
 | ||
|  | #include "asio/experimental/detail/coro_completion_handler.hpp"
 | ||
|  | #include "asio/detail/push_options.hpp"
 | ||
|  | 
 | ||
|  | namespace asio { | ||
|  | namespace experimental { | ||
|  | 
 | ||
|  | template <typename Yield, typename Return, | ||
|  |     typename Executor, typename Allocator> | ||
|  | struct coro; | ||
|  | 
 | ||
|  | namespace detail { | ||
|  | 
 | ||
|  | struct coro_cancellation_source | ||
|  | { | ||
|  |   cancellation_slot slot; | ||
|  |   cancellation_state state; | ||
|  |   bool throw_if_cancelled_ = true; | ||
|  | 
 | ||
|  |   void reset_cancellation_state() | ||
|  |   { | ||
|  |     state = cancellation_state(slot); | ||
|  |   } | ||
|  | 
 | ||
|  |   template <typename Filter> | ||
|  |   void reset_cancellation_state(ASIO_MOVE_ARG(Filter) filter) | ||
|  |   { | ||
|  |     state = cancellation_state(slot, ASIO_MOVE_CAST(Filter)(filter)); | ||
|  |   } | ||
|  | 
 | ||
|  |   template <typename InFilter, typename OutFilter> | ||
|  |   void reset_cancellation_state(ASIO_MOVE_ARG(InFilter) in_filter, | ||
|  |       ASIO_MOVE_ARG(OutFilter) out_filter) | ||
|  |   { | ||
|  |     state = cancellation_state(slot, | ||
|  |         ASIO_MOVE_CAST(InFilter)(in_filter), | ||
|  |         ASIO_MOVE_CAST(OutFilter)(out_filter)); | ||
|  |   } | ||
|  | 
 | ||
|  |   bool throw_if_cancelled() const | ||
|  |   { | ||
|  |     return throw_if_cancelled_; | ||
|  |   } | ||
|  | 
 | ||
|  |   void throw_if_cancelled(bool value) | ||
|  |   { | ||
|  |     throw_if_cancelled_ = value; | ||
|  |   } | ||
|  | }; | ||
|  | 
 | ||
|  | template <typename Signature, typename Return, | ||
|  |     typename Executor, typename Allocator> | ||
|  | struct coro_promise; | ||
|  | 
 | ||
|  | template <typename T> | ||
|  | struct is_noexcept : std::false_type | ||
|  | { | ||
|  | }; | ||
|  | 
 | ||
|  | template <typename Return, typename... Args> | ||
|  | struct is_noexcept<Return(Args...)> : std::false_type | ||
|  | { | ||
|  | }; | ||
|  | 
 | ||
|  | template <typename Return, typename... Args> | ||
|  | struct is_noexcept<Return(Args...) noexcept> : std::true_type | ||
|  | { | ||
|  | }; | ||
|  | 
 | ||
|  | template <typename T> | ||
|  | constexpr bool is_noexcept_v = is_noexcept<T>::value; | ||
|  | 
 | ||
|  | template <typename T> | ||
|  | struct coro_error; | ||
|  | 
 | ||
|  | template <> | ||
|  | struct coro_error<asio::error_code> | ||
|  | { | ||
|  |   static asio::error_code invalid() | ||
|  |   { | ||
|  |     return asio::error::fault; | ||
|  |   } | ||
|  | 
 | ||
|  |   static asio::error_code cancelled() | ||
|  |   { | ||
|  |     return asio::error::operation_aborted; | ||
|  |   } | ||
|  | 
 | ||
|  |   static asio::error_code interrupted() | ||
|  |   { | ||
|  |     return asio::error::interrupted; | ||
|  |   } | ||
|  | 
 | ||
|  |   static asio::error_code done() | ||
|  |   { | ||
|  |     return asio::error::broken_pipe; | ||
|  |   } | ||
|  | }; | ||
|  | 
 | ||
|  | template <> | ||
|  | struct coro_error<std::exception_ptr> | ||
|  | { | ||
|  |   static std::exception_ptr invalid() | ||
|  |   { | ||
|  |     return std::make_exception_ptr( | ||
|  |         asio::system_error( | ||
|  |           coro_error<asio::error_code>::invalid())); | ||
|  |   } | ||
|  | 
 | ||
|  |   static std::exception_ptr cancelled() | ||
|  |   { | ||
|  |     return std::make_exception_ptr( | ||
|  |         asio::system_error( | ||
|  |           coro_error<asio::error_code>::cancelled())); | ||
|  |   } | ||
|  | 
 | ||
|  |   static std::exception_ptr interrupted() | ||
|  |   { | ||
|  |     return std::make_exception_ptr( | ||
|  |         asio::system_error( | ||
|  |           coro_error<asio::error_code>::interrupted())); | ||
|  |   } | ||
|  | 
 | ||
|  |   static std::exception_ptr done() | ||
|  |   { | ||
|  |     return std::make_exception_ptr( | ||
|  |         asio::system_error( | ||
|  |           coro_error<asio::error_code>::done())); | ||
|  |   } | ||
|  | }; | ||
|  | 
 | ||
|  | template <typename T, typename Coroutine > | ||
|  | struct coro_with_arg | ||
|  | { | ||
|  |   using coro_t = Coroutine; | ||
|  |   T value; | ||
|  |   coro_t& coro; | ||
|  | 
 | ||
|  |   struct awaitable_t | ||
|  |   { | ||
|  |     T value; | ||
|  |     coro_t& coro; | ||
|  | 
 | ||
|  |     constexpr static bool await_ready() { return false; } | ||
|  | 
 | ||
|  |     template <typename Y, typename R, typename E, typename A> | ||
|  |     auto await_suspend(coroutine_handle<coro_promise<Y, R, E, A>> h) | ||
|  |       -> coroutine_handle<> | ||
|  |     { | ||
|  |       auto& hp = h.promise(); | ||
|  | 
 | ||
|  |       if constexpr (!coro_promise<Y, R, E, A>::is_noexcept) | ||
|  |       { | ||
|  |         if ((hp.cancel->state.cancelled() != cancellation_type::none) | ||
|  |             && hp.cancel->throw_if_cancelled_) | ||
|  |         { | ||
|  |           asio::detail::throw_error( | ||
|  |               asio::error::operation_aborted, "coro-cancelled"); | ||
|  |         } | ||
|  |       } | ||
|  | 
 | ||
|  |       if (hp.get_executor() == coro.get_executor()) | ||
|  |       { | ||
|  |         coro.coro_->awaited_from = h; | ||
|  |         coro.coro_->reset_error(); | ||
|  |         coro.coro_->input_ = std::move(value); | ||
|  |         coro.coro_->cancel = hp.cancel; | ||
|  |         return coro.coro_->get_handle(); | ||
|  |       } | ||
|  |       else | ||
|  |       { | ||
|  |         coro.coro_->awaited_from = | ||
|  |           dispatch_coroutine( | ||
|  |               asio::prefer(hp.get_executor(), | ||
|  |                 execution::outstanding_work.tracked), | ||
|  |                 [h]() mutable { h.resume(); }).handle; | ||
|  | 
 | ||
|  |         coro.coro_->reset_error(); | ||
|  |         coro.coro_->input_ = std::move(value); | ||
|  | 
 | ||
|  |         struct cancel_handler | ||
|  |         { | ||
|  |           using src = std::pair<cancellation_signal, | ||
|  |                 detail::coro_cancellation_source>; | ||
|  | 
 | ||
|  |           std::shared_ptr<src> st = std::make_shared<src>(); | ||
|  | 
 | ||
|  |           cancel_handler(E e, coro_t& coro) : e(e), coro_(coro.coro_) | ||
|  |           { | ||
|  |             st->second.state = | ||
|  |               cancellation_state(st->second.slot = st->first.slot()); | ||
|  |           } | ||
|  | 
 | ||
|  |           E e; | ||
|  |           typename coro_t::promise_type* coro_; | ||
|  | 
 | ||
|  |           void operator()(cancellation_type ct) | ||
|  |           { | ||
|  |             asio::dispatch(e, [ct, st = st]() mutable | ||
|  |             { | ||
|  |               auto & [sig, state] = *st; | ||
|  |               sig.emit(ct); | ||
|  |             }); | ||
|  |           } | ||
|  |         }; | ||
|  | 
 | ||
|  |         if (hp.cancel->state.slot().is_connected()) | ||
|  |         { | ||
|  |           hp.cancel->state.slot().template emplace<cancel_handler>( | ||
|  |               coro.get_executor(), coro); | ||
|  |         } | ||
|  | 
 | ||
|  |         auto hh = detail::coroutine_handle< | ||
|  |           typename coro_t::promise_type>::from_promise(*coro.coro_); | ||
|  | 
 | ||
|  |         return dispatch_coroutine( | ||
|  |             coro.coro_->get_executor(), [hh]() mutable { hh.resume(); }).handle; | ||
|  |       } | ||
|  |     } | ||
|  | 
 | ||
|  |     auto await_resume() -> typename coro_t::result_type | ||
|  |     { | ||
|  |       coro.coro_->cancel = nullptr; | ||
|  |       coro.coro_->rethrow_if(); | ||
|  |       return std::move(coro.coro_->result_); | ||
|  |     } | ||
|  |   }; | ||
|  | 
 | ||
|  |   template <typename CompletionToken> | ||
|  |   auto async_resume(CompletionToken&& token) && | ||
|  |   { | ||
|  |     return coro.async_resume(std::move(value), | ||
|  |         std::forward<CompletionToken>(token)); | ||
|  |   } | ||
|  | 
 | ||
|  |   auto operator co_await() && | ||
|  |   { | ||
|  |     return awaitable_t{std::move(value), coro}; | ||
|  |   } | ||
|  | }; | ||
|  | 
 | ||
|  | template <bool IsNoexcept> | ||
|  | struct coro_promise_error; | ||
|  | 
 | ||
|  | template <> | ||
|  | struct coro_promise_error<false> | ||
|  | { | ||
|  |   std::exception_ptr error_; | ||
|  | 
 | ||
|  |   void reset_error() | ||
|  |   { | ||
|  |     error_ = std::exception_ptr{}; | ||
|  |   } | ||
|  | 
 | ||
|  |   void unhandled_exception() | ||
|  |   { | ||
|  |     error_ = std::current_exception(); | ||
|  |   } | ||
|  | 
 | ||
|  |   void rethrow_if() | ||
|  |   { | ||
|  |     if (error_) | ||
|  |       std::rethrow_exception(error_); | ||
|  |   } | ||
|  | }; | ||
|  | 
 | ||
|  | #if defined(__GNUC__)
 | ||
|  | # pragma GCC diagnostic push
 | ||
|  | # if defined(__clang__)
 | ||
|  | #  pragma GCC diagnostic ignored "-Wexceptions"
 | ||
|  | # else
 | ||
|  | #  pragma GCC diagnostic ignored "-Wterminate"
 | ||
|  | # endif
 | ||
|  | #elif defined(_MSC_VER)
 | ||
|  | # pragma warning(push)
 | ||
|  | # pragma warning (disable:4297)
 | ||
|  | #endif
 | ||
|  | 
 | ||
|  | template <> | ||
|  | struct coro_promise_error<true> | ||
|  | { | ||
|  |   void reset_error() | ||
|  |   { | ||
|  |   } | ||
|  | 
 | ||
|  |   void unhandled_exception() noexcept | ||
|  |   { | ||
|  |     throw; | ||
|  |   } | ||
|  | 
 | ||
|  |   void rethrow_if() | ||
|  |   { | ||
|  |   } | ||
|  | }; | ||
|  | 
 | ||
|  | #if defined(__GNUC__)
 | ||
|  | # pragma GCC diagnostic pop
 | ||
|  | #elif defined(_MSC_VER)
 | ||
|  | # pragma warning(pop)
 | ||
|  | #endif
 | ||
|  | 
 | ||
|  | template <typename T = void> | ||
|  | struct yield_input | ||
|  | { | ||
|  |   T& value; | ||
|  |   coroutine_handle<> awaited_from{noop_coroutine()}; | ||
|  | 
 | ||
|  |   bool await_ready() const noexcept | ||
|  |   { | ||
|  |     return false; | ||
|  |   } | ||
|  | 
 | ||
|  |   template <typename U> | ||
|  |   coroutine_handle<> await_suspend(coroutine_handle<U>) noexcept | ||
|  |   { | ||
|  |     return std::exchange(awaited_from, noop_coroutine()); | ||
|  |   } | ||
|  | 
 | ||
|  |   T await_resume() const noexcept | ||
|  |   { | ||
|  |     return std::move(value); | ||
|  |   } | ||
|  | }; | ||
|  | 
 | ||
|  | template <> | ||
|  | struct yield_input<void> | ||
|  | { | ||
|  |   coroutine_handle<> awaited_from{noop_coroutine()}; | ||
|  | 
 | ||
|  |   bool await_ready() const noexcept | ||
|  |   { | ||
|  |     return false; | ||
|  |   } | ||
|  | 
 | ||
|  |   auto await_suspend(coroutine_handle<>) noexcept | ||
|  |   { | ||
|  |     return std::exchange(awaited_from, noop_coroutine()); | ||
|  |   } | ||
|  | 
 | ||
|  |   constexpr void await_resume() const noexcept | ||
|  |   { | ||
|  |   } | ||
|  | }; | ||
|  | 
 | ||
|  | struct coro_awaited_from | ||
|  | { | ||
|  |   coroutine_handle<> awaited_from{noop_coroutine()}; | ||
|  | 
 | ||
|  |   auto final_suspend() noexcept | ||
|  |   { | ||
|  |     struct suspendor | ||
|  |     { | ||
|  |       coroutine_handle<> awaited_from; | ||
|  | 
 | ||
|  |       constexpr static bool await_ready() noexcept | ||
|  |       { | ||
|  |         return false; | ||
|  |       } | ||
|  | 
 | ||
|  |       auto await_suspend(coroutine_handle<>) noexcept | ||
|  |       { | ||
|  |         return std::exchange(awaited_from, noop_coroutine()); | ||
|  |       } | ||
|  | 
 | ||
|  |       constexpr static void await_resume() noexcept | ||
|  |       { | ||
|  |       } | ||
|  |     }; | ||
|  | 
 | ||
|  |     return suspendor{std::exchange(awaited_from, noop_coroutine())}; | ||
|  |   } | ||
|  | 
 | ||
|  |   ~coro_awaited_from() | ||
|  |   { | ||
|  |     awaited_from.resume(); | ||
|  |   }//must be on the right executor
 | ||
|  | }; | ||
|  | 
 | ||
|  | template <typename Yield, typename Input, typename Return> | ||
|  | struct coro_promise_exchange : coro_awaited_from | ||
|  | { | ||
|  |   using result_type = coro_result_t<Yield, Return>; | ||
|  | 
 | ||
|  |   result_type result_; | ||
|  |   Input input_; | ||
|  | 
 | ||
|  |   auto yield_value(Yield&& y) | ||
|  |   { | ||
|  |     result_ = std::move(y); | ||
|  |     return yield_input<Input>{std::move(input_), | ||
|  |         std::exchange(awaited_from, noop_coroutine())}; | ||
|  |   } | ||
|  | 
 | ||
|  |   auto yield_value(const Yield& y) | ||
|  |   { | ||
|  |     result_ = y; | ||
|  |     return yield_input<Input>{std::move(input_), | ||
|  |         std::exchange(awaited_from, noop_coroutine())}; | ||
|  |   } | ||
|  | 
 | ||
|  |   void return_value(const Return& r) | ||
|  |   { | ||
|  |     result_ = r; | ||
|  |   } | ||
|  | 
 | ||
|  |   void return_value(Return&& r) | ||
|  |   { | ||
|  |     result_ = std::move(r); | ||
|  |   } | ||
|  | }; | ||
|  | 
 | ||
|  | template <typename YieldReturn> | ||
|  | struct coro_promise_exchange<YieldReturn, void, YieldReturn> : coro_awaited_from | ||
|  | { | ||
|  |   using result_type = coro_result_t<YieldReturn, YieldReturn>; | ||
|  | 
 | ||
|  |   result_type result_; | ||
|  | 
 | ||
|  |   auto yield_value(const YieldReturn& y) | ||
|  |   { | ||
|  |     result_ = y; | ||
|  |     return yield_input<void>{std::exchange(awaited_from, noop_coroutine())}; | ||
|  |   } | ||
|  | 
 | ||
|  |   auto yield_value(YieldReturn&& y) | ||
|  |   { | ||
|  |     result_ = std::move(y); | ||
|  |     return yield_input<void>{std::exchange(awaited_from, noop_coroutine())}; | ||
|  |   } | ||
|  | 
 | ||
|  |   void return_value(const YieldReturn& r) | ||
|  |   { | ||
|  |     result_ = r; | ||
|  |   } | ||
|  | 
 | ||
|  |   void return_value(YieldReturn&& r) | ||
|  |   { | ||
|  |     result_ = std::move(r); | ||
|  |   } | ||
|  | }; | ||
|  | 
 | ||
|  | template <typename Yield, typename Return> | ||
|  | struct coro_promise_exchange<Yield, void, Return> : coro_awaited_from | ||
|  | { | ||
|  |   using result_type = coro_result_t<Yield, Return>; | ||
|  | 
 | ||
|  |   result_type result_; | ||
|  | 
 | ||
|  |   auto yield_value(const Yield& y) | ||
|  |   { | ||
|  |     result_.template emplace<0>(y); | ||
|  |     return yield_input<void>{std::exchange(awaited_from, noop_coroutine())}; | ||
|  |   } | ||
|  | 
 | ||
|  |   auto yield_value(Yield&& y) | ||
|  |   { | ||
|  |     result_.template emplace<0>(std::move(y)); | ||
|  |     return yield_input<void>{std::exchange(awaited_from, noop_coroutine())}; | ||
|  |   } | ||
|  | 
 | ||
|  |   void return_value(const Return& r) | ||
|  |   { | ||
|  |     result_.template emplace<1>(r); | ||
|  |   } | ||
|  | 
 | ||
|  |   void return_value(Return&& r) | ||
|  |   { | ||
|  |     result_.template emplace<1>(std::move(r)); | ||
|  |   } | ||
|  | }; | ||
|  | 
 | ||
|  | template <typename Yield, typename Input> | ||
|  | struct coro_promise_exchange<Yield, Input, void> : coro_awaited_from | ||
|  | { | ||
|  |   using result_type = coro_result_t<Yield, void>; | ||
|  | 
 | ||
|  |   result_type result_; | ||
|  |   Input input_; | ||
|  | 
 | ||
|  |   auto yield_value(Yield&& y) | ||
|  |   { | ||
|  |     result_ = std::move(y); | ||
|  |     return yield_input<Input>{input_, | ||
|  |                               std::exchange(awaited_from, noop_coroutine())}; | ||
|  |   } | ||
|  | 
 | ||
|  |   auto yield_value(const Yield& y) | ||
|  |   { | ||
|  |     result_ = y; | ||
|  |     return yield_input<Input>{input_, | ||
|  |                               std::exchange(awaited_from, noop_coroutine())}; | ||
|  |   } | ||
|  | 
 | ||
|  |   void return_void() | ||
|  |   { | ||
|  |     result_.reset(); | ||
|  |   } | ||
|  | }; | ||
|  | 
 | ||
|  | template <typename Return> | ||
|  | struct coro_promise_exchange<void, void, Return> : coro_awaited_from | ||
|  | { | ||
|  |   using result_type = coro_result_t<void, Return>; | ||
|  | 
 | ||
|  |   result_type result_; | ||
|  | 
 | ||
|  |   void yield_value(); | ||
|  | 
 | ||
|  |   void return_value(const Return& r) | ||
|  |   { | ||
|  |     result_ = r; | ||
|  |   } | ||
|  | 
 | ||
|  |   void return_value(Return&& r) | ||
|  |   { | ||
|  |     result_ = std::move(r); | ||
|  |   } | ||
|  | }; | ||
|  | 
 | ||
|  | template <> | ||
|  | struct coro_promise_exchange<void, void, void> : coro_awaited_from | ||
|  | { | ||
|  |   void return_void() {} | ||
|  | 
 | ||
|  |   void yield_value(); | ||
|  | }; | ||
|  | 
 | ||
|  | template <typename Yield> | ||
|  | struct coro_promise_exchange<Yield, void, void> : coro_awaited_from | ||
|  | { | ||
|  |   using result_type = coro_result_t<Yield, void>; | ||
|  | 
 | ||
|  |   result_type result_; | ||
|  | 
 | ||
|  |   auto yield_value(const Yield& y) | ||
|  |   { | ||
|  |     result_ = y; | ||
|  |     return yield_input<void>{std::exchange(awaited_from, noop_coroutine())}; | ||
|  |   } | ||
|  | 
 | ||
|  |   auto yield_value(Yield&& y) | ||
|  |   { | ||
|  |     result_ = std::move(y); | ||
|  |     return yield_input<void>{std::exchange(awaited_from, noop_coroutine())}; | ||
|  |   } | ||
|  | 
 | ||
|  |   void return_void() | ||
|  |   { | ||
|  |     result_.reset(); | ||
|  |   } | ||
|  | }; | ||
|  | 
 | ||
|  | template <typename Yield, typename Return, | ||
|  |     typename Executor, typename Allocator> | ||
|  | struct coro_promise final : | ||
|  |   coro_promise_allocator<Allocator>, | ||
|  |   coro_promise_error<coro_traits<Yield, Return, Executor>::is_noexcept>, | ||
|  |   coro_promise_exchange< | ||
|  |       typename coro_traits<Yield, Return, Executor>::yield_type, | ||
|  |       typename coro_traits<Yield, Return, Executor>::input_type, | ||
|  |       typename coro_traits<Yield, Return, Executor>::return_type> | ||
|  | { | ||
|  |   using coro_type = coro<Yield, Return, Executor, Allocator>; | ||
|  | 
 | ||
|  |   auto handle() | ||
|  |   { | ||
|  |     return coroutine_handle<coro_promise>::from_promise(this); | ||
|  |   } | ||
|  | 
 | ||
|  |   using executor_type = Executor; | ||
|  | 
 | ||
|  |   executor_type executor_; | ||
|  | 
 | ||
|  |   std::optional<coro_cancellation_source> cancel_source; | ||
|  |   coro_cancellation_source * cancel; | ||
|  | 
 | ||
|  |   using cancellation_slot_type = asio::cancellation_slot; | ||
|  | 
 | ||
|  |   cancellation_slot_type get_cancellation_slot() const noexcept | ||
|  |   { | ||
|  |     return cancel ? cancel->slot : cancellation_slot_type{}; | ||
|  |   } | ||
|  | 
 | ||
|  |   using allocator_type = | ||
|  |     typename std::allocator_traits<associated_allocator_t<Executor>>:: | ||
|  |       template rebind_alloc<std::byte>; | ||
|  |   using traits = coro_traits<Yield, Return, Executor>; | ||
|  | 
 | ||
|  |   using input_type = typename traits::input_type; | ||
|  |   using yield_type = typename traits::yield_type; | ||
|  |   using return_type = typename traits::return_type; | ||
|  |   using error_type = typename traits::error_type; | ||
|  |   using result_type = typename traits::result_type; | ||
|  |   constexpr static bool is_noexcept = traits::is_noexcept; | ||
|  | 
 | ||
|  |   auto get_executor() const -> Executor | ||
|  |   { | ||
|  |     return executor_; | ||
|  |   } | ||
|  | 
 | ||
|  |   auto get_handle() | ||
|  |   { | ||
|  |     return coroutine_handle<coro_promise>::from_promise(*this); | ||
|  |   } | ||
|  | 
 | ||
|  |   template <typename... Args> | ||
|  |   coro_promise(Executor executor, Args&&... args) noexcept | ||
|  |     : coro_promise_allocator<Allocator>( | ||
|  |         executor, std::forward<Args>(args)...), | ||
|  |       executor_(std::move(executor)) | ||
|  |   { | ||
|  |   } | ||
|  | 
 | ||
|  |   template <typename First, typename... Args> | ||
|  |   coro_promise(First&& f, Executor executor, Args&&... args) noexcept | ||
|  |     : coro_promise_allocator<Allocator>( | ||
|  |         f, executor, std::forward<Args>(args)...), | ||
|  |       executor_(std::move(executor)) | ||
|  |   { | ||
|  |   } | ||
|  | 
 | ||
|  |   template <typename First, detail::execution_context Context, typename... Args> | ||
|  |   coro_promise(First&& f, Context&& ctx, Args&&... args) noexcept | ||
|  |     : coro_promise_allocator<Allocator>( | ||
|  |         f, ctx, std::forward<Args>(args)...), | ||
|  |       executor_(ctx.get_executor()) | ||
|  |   { | ||
|  |   } | ||
|  | 
 | ||
|  |   template <detail::execution_context Context, typename... Args> | ||
|  |   coro_promise(Context&& ctx, Args&&... args) noexcept | ||
|  |     : coro_promise_allocator<Allocator>( | ||
|  |         ctx, std::forward<Args>(args)...), | ||
|  |       executor_(ctx.get_executor()) | ||
|  |   { | ||
|  |   } | ||
|  | 
 | ||
|  |   auto get_return_object() | ||
|  |   { | ||
|  |     return coro<Yield, Return, Executor, Allocator>{this}; | ||
|  |   } | ||
|  | 
 | ||
|  |   auto initial_suspend() noexcept | ||
|  |   { | ||
|  |     return suspend_always{}; | ||
|  |   } | ||
|  | 
 | ||
|  |   using coro_promise_exchange< | ||
|  |       typename coro_traits<Yield, Return, Executor>::yield_type, | ||
|  |       typename coro_traits<Yield, Return, Executor>::input_type, | ||
|  |       typename coro_traits<Yield, Return, Executor>::return_type>::yield_value; | ||
|  | 
 | ||
|  |   auto await_transform(this_coro::executor_t) const | ||
|  |   { | ||
|  |     struct exec_helper | ||
|  |     { | ||
|  |       const executor_type& value; | ||
|  | 
 | ||
|  |       constexpr static bool await_ready() noexcept | ||
|  |       { | ||
|  |         return true; | ||
|  |       } | ||
|  | 
 | ||
|  |       constexpr static void await_suspend(coroutine_handle<>) noexcept | ||
|  |       { | ||
|  |       } | ||
|  | 
 | ||
|  |       executor_type await_resume() const noexcept | ||
|  |       { | ||
|  |         return value; | ||
|  |       } | ||
|  |     }; | ||
|  | 
 | ||
|  |     return exec_helper{executor_}; | ||
|  |   } | ||
|  | 
 | ||
|  |   auto await_transform(this_coro::cancellation_state_t) const | ||
|  |   { | ||
|  |     struct exec_helper | ||
|  |     { | ||
|  |       const asio::cancellation_state& value; | ||
|  | 
 | ||
|  |       constexpr static bool await_ready() noexcept | ||
|  |       { | ||
|  |         return true; | ||
|  |       } | ||
|  | 
 | ||
|  |       constexpr static void await_suspend(coroutine_handle<>) noexcept | ||
|  |       { | ||
|  |       } | ||
|  | 
 | ||
|  |       asio::cancellation_state await_resume() const noexcept | ||
|  |       { | ||
|  |         return value; | ||
|  |       } | ||
|  |     }; | ||
|  |     assert(cancel); | ||
|  |     return exec_helper{cancel->state}; | ||
|  |   } | ||
|  | 
 | ||
|  |   // This await transformation resets the associated cancellation state.
 | ||
|  |   auto await_transform(this_coro::reset_cancellation_state_0_t) noexcept | ||
|  |   { | ||
|  |     struct result | ||
|  |     { | ||
|  |       detail::coro_cancellation_source * src_; | ||
|  | 
 | ||
|  |       bool await_ready() const noexcept | ||
|  |       { | ||
|  |         return true; | ||
|  |       } | ||
|  | 
 | ||
|  |       void await_suspend(coroutine_handle<void>) noexcept | ||
|  |       { | ||
|  |       } | ||
|  | 
 | ||
|  |       auto await_resume() const | ||
|  |       { | ||
|  |         return src_->reset_cancellation_state(); | ||
|  |       } | ||
|  |     }; | ||
|  | 
 | ||
|  |     return result{cancel}; | ||
|  |   } | ||
|  | 
 | ||
|  |   // This await transformation resets the associated cancellation state.
 | ||
|  |   template <typename Filter> | ||
|  |   auto await_transform( | ||
|  |       this_coro::reset_cancellation_state_1_t<Filter> reset) noexcept | ||
|  |   { | ||
|  |     struct result | ||
|  |     { | ||
|  |       detail::coro_cancellation_source* src_; | ||
|  |       Filter filter_; | ||
|  | 
 | ||
|  |       bool await_ready() const noexcept | ||
|  |       { | ||
|  |         return true; | ||
|  |       } | ||
|  | 
 | ||
|  |       void await_suspend(coroutine_handle<void>) noexcept | ||
|  |       { | ||
|  |       } | ||
|  | 
 | ||
|  |       auto await_resume() | ||
|  |       { | ||
|  |         return src_->reset_cancellation_state( | ||
|  |             ASIO_MOVE_CAST(Filter)(filter_)); | ||
|  |       } | ||
|  |     }; | ||
|  | 
 | ||
|  |     return result{cancel, ASIO_MOVE_CAST(Filter)(reset.filter)}; | ||
|  |   } | ||
|  | 
 | ||
|  |   // This await transformation resets the associated cancellation state.
 | ||
|  |   template <typename InFilter, typename OutFilter> | ||
|  |   auto await_transform( | ||
|  |       this_coro::reset_cancellation_state_2_t<InFilter, OutFilter> reset) | ||
|  |   noexcept | ||
|  |   { | ||
|  |     struct result | ||
|  |     { | ||
|  |       detail::coro_cancellation_source* src_; | ||
|  |       InFilter in_filter_; | ||
|  |       OutFilter out_filter_; | ||
|  | 
 | ||
|  |       bool await_ready() const noexcept | ||
|  |       { | ||
|  |         return true; | ||
|  |       } | ||
|  | 
 | ||
|  |       void await_suspend(coroutine_handle<void>) noexcept | ||
|  |       { | ||
|  |       } | ||
|  | 
 | ||
|  |       auto await_resume() | ||
|  |       { | ||
|  |         return src_->reset_cancellation_state( | ||
|  |             ASIO_MOVE_CAST(InFilter)(in_filter_), | ||
|  |             ASIO_MOVE_CAST(OutFilter)(out_filter_)); | ||
|  |       } | ||
|  |     }; | ||
|  | 
 | ||
|  |     return result{cancel, | ||
|  |         ASIO_MOVE_CAST(InFilter)(reset.in_filter), | ||
|  |         ASIO_MOVE_CAST(OutFilter)(reset.out_filter)}; | ||
|  |   } | ||
|  | 
 | ||
|  |   // This await transformation determines whether cancellation is propagated as
 | ||
|  |   // an exception.
 | ||
|  |   auto await_transform(this_coro::throw_if_cancelled_0_t) noexcept | ||
|  |     requires (!is_noexcept) | ||
|  |   { | ||
|  |     struct result | ||
|  |     { | ||
|  |       detail::coro_cancellation_source* src_; | ||
|  | 
 | ||
|  |       bool await_ready() const noexcept | ||
|  |       { | ||
|  |         return true; | ||
|  |       } | ||
|  | 
 | ||
|  |       void await_suspend(coroutine_handle<void>) noexcept | ||
|  |       { | ||
|  |       } | ||
|  | 
 | ||
|  |       auto await_resume() | ||
|  |       { | ||
|  |         return src_->throw_if_cancelled(); | ||
|  |       } | ||
|  |     }; | ||
|  | 
 | ||
|  |     return result{cancel}; | ||
|  |   } | ||
|  | 
 | ||
|  |   // This await transformation sets whether cancellation is propagated as an
 | ||
|  |   // exception.
 | ||
|  |   auto await_transform( | ||
|  |       this_coro::throw_if_cancelled_1_t throw_if_cancelled) noexcept | ||
|  |     requires (!is_noexcept) | ||
|  |   { | ||
|  |     struct result | ||
|  |     { | ||
|  |       detail::coro_cancellation_source* src_; | ||
|  |       bool value_; | ||
|  | 
 | ||
|  |       bool await_ready() const noexcept | ||
|  |       { | ||
|  |         return true; | ||
|  |       } | ||
|  | 
 | ||
|  |       void await_suspend(coroutine_handle<void>) noexcept | ||
|  |       { | ||
|  |       } | ||
|  | 
 | ||
|  |       auto await_resume() | ||
|  |       { | ||
|  |         src_->throw_if_cancelled(value_); | ||
|  |       } | ||
|  |     }; | ||
|  | 
 | ||
|  |     return result{cancel, throw_if_cancelled.value}; | ||
|  |   } | ||
|  | 
 | ||
|  |   template <typename Yield_, typename Return_, | ||
|  |       typename Executor_, typename Allocator_> | ||
|  |   auto await_transform(coro<Yield_, Return_, Executor_, Allocator_>& kr) | ||
|  |     -> decltype(auto) | ||
|  |   { | ||
|  |     return kr; | ||
|  |   } | ||
|  | 
 | ||
|  |   template <typename Yield_, typename Return_, | ||
|  |       typename Executor_, typename Allocator_> | ||
|  |   auto await_transform(coro<Yield_, Return_, Executor_, Allocator_>&& kr) | ||
|  |   { | ||
|  |     return std::move(kr); | ||
|  |   } | ||
|  | 
 | ||
|  |   template <typename T_, typename Coroutine > | ||
|  |   auto await_transform(coro_with_arg<T_, Coroutine>&& kr) -> decltype(auto) | ||
|  |   { | ||
|  |     return std::move(kr); | ||
|  |   } | ||
|  | 
 | ||
|  |   template <typename T_> | ||
|  |     requires requires(T_ t) {{ t.async_wait(deferred) }; } | ||
|  |   auto await_transform(T_& t) -> decltype(auto) | ||
|  |   { | ||
|  |     return await_transform(t.async_wait(deferred)); | ||
|  |   } | ||
|  | 
 | ||
|  |   template <typename Op> | ||
|  |   auto await_transform(Op&& op, | ||
|  |       typename constraint<is_async_operation<Op>::value>::type = 0) | ||
|  |   { | ||
|  |     if ((cancel->state.cancelled() != cancellation_type::none) | ||
|  |         && cancel->throw_if_cancelled_) | ||
|  |     { | ||
|  |       asio::detail::throw_error( | ||
|  |           asio::error::operation_aborted, "coro-cancelled"); | ||
|  |     } | ||
|  |     using signature = typename completion_signature_of<Op>::type; | ||
|  |     using result_type = detail::coro_completion_handler_type_t<signature>; | ||
|  |     using handler_type = | ||
|  |       typename detail::coro_completion_handler_type<signature>::template | ||
|  |         completion_handler<coro_promise>; | ||
|  | 
 | ||
|  |     struct aw_t | ||
|  |     { | ||
|  |       Op op; | ||
|  |       std::optional<result_type> result; | ||
|  | 
 | ||
|  |       constexpr static bool await_ready() | ||
|  |       { | ||
|  |         return false; | ||
|  |       } | ||
|  | 
 | ||
|  |       void await_suspend(coroutine_handle<coro_promise> h) | ||
|  |       { | ||
|  |         std::move(op)(handler_type{h, result}); | ||
|  |       } | ||
|  | 
 | ||
|  |       auto await_resume() | ||
|  |       { | ||
|  |         if constexpr (is_noexcept) | ||
|  |         { | ||
|  |           if constexpr (std::tuple_size_v<result_type> == 0u) | ||
|  |             return; | ||
|  |           else if constexpr (std::tuple_size_v<result_type> == 1u) | ||
|  |             return std::get<0>(std::move(result).value()); | ||
|  |           else | ||
|  |             return std::move(result).value(); | ||
|  |         } | ||
|  |         else | ||
|  |           return detail::coro_interpret_result(std::move(result).value()); | ||
|  |       } | ||
|  |     }; | ||
|  | 
 | ||
|  |     return aw_t{std::move(op), {}}; | ||
|  |   } | ||
|  | }; | ||
|  | 
 | ||
|  | } // namespace detail
 | ||
|  | 
 | ||
|  | template <typename Yield, typename Return, | ||
|  |     typename Executor, typename Allocator> | ||
|  | struct coro<Yield, Return, Executor, Allocator>::awaitable_t | ||
|  | { | ||
|  |   coro& coro_; | ||
|  | 
 | ||
|  |   constexpr static bool await_ready() { return false; } | ||
|  | 
 | ||
|  |   template <typename Y, typename R, typename E, typename A> | ||
|  |   auto await_suspend( | ||
|  |       detail::coroutine_handle<detail::coro_promise<Y, R, E, A>> h) | ||
|  |     -> detail::coroutine_handle<> | ||
|  |   { | ||
|  |     auto& hp = h.promise(); | ||
|  | 
 | ||
|  |     if constexpr (!detail::coro_promise<Y, R, E, A>::is_noexcept) | ||
|  |     { | ||
|  |       if ((hp.cancel->state.cancelled() != cancellation_type::none) | ||
|  |           && hp.cancel->throw_if_cancelled_) | ||
|  |       { | ||
|  |         asio::detail::throw_error( | ||
|  |             asio::error::operation_aborted, "coro-cancelled"); | ||
|  |       } | ||
|  |     } | ||
|  | 
 | ||
|  |     if (hp.get_executor() == coro_.get_executor()) | ||
|  |     { | ||
|  |       coro_.coro_->awaited_from  = h; | ||
|  |       coro_.coro_->cancel = hp.cancel; | ||
|  |       coro_.coro_->reset_error(); | ||
|  | 
 | ||
|  |       return coro_.coro_->get_handle(); | ||
|  |     } | ||
|  |     else | ||
|  |     { | ||
|  |       coro_.coro_->awaited_from = detail::dispatch_coroutine( | ||
|  |           asio::prefer(hp.get_executor(), | ||
|  |             execution::outstanding_work.tracked), | ||
|  |           [h]() mutable | ||
|  |           { | ||
|  |             h.resume(); | ||
|  |           }).handle; | ||
|  | 
 | ||
|  |       coro_.coro_->reset_error(); | ||
|  | 
 | ||
|  |       struct cancel_handler | ||
|  |       { | ||
|  |         std::shared_ptr<std::pair<cancellation_signal, | ||
|  |           detail::coro_cancellation_source>> st = std::make_shared< | ||
|  |             std::pair<cancellation_signal, detail::coro_cancellation_source>>(); | ||
|  | 
 | ||
|  |         cancel_handler(E e, coro& coro) : e(e), coro_(coro.coro_) | ||
|  |         { | ||
|  |           st->second.state = cancellation_state( | ||
|  |               st->second.slot = st->first.slot()); | ||
|  |         } | ||
|  | 
 | ||
|  |         E e; | ||
|  |         typename coro::promise_type* coro_; | ||
|  | 
 | ||
|  |         void operator()(cancellation_type ct) | ||
|  |         { | ||
|  |           asio::dispatch(e, | ||
|  |               [ct, st = st]() mutable | ||
|  |               { | ||
|  |                 auto & [sig, state] = *st; | ||
|  |                 sig.emit(ct); | ||
|  |               }); | ||
|  |         } | ||
|  |       }; | ||
|  | 
 | ||
|  |       if (hp.cancel->state.slot().is_connected()) | ||
|  |       { | ||
|  |         hp.cancel->state.slot().template emplace<cancel_handler>( | ||
|  |             coro_.get_executor(), coro_); | ||
|  |       } | ||
|  | 
 | ||
|  |       auto hh = detail::coroutine_handle< | ||
|  |         detail::coro_promise<Yield, Return, Executor, Allocator>>::from_promise( | ||
|  |             *coro_.coro_); | ||
|  | 
 | ||
|  |       return detail::dispatch_coroutine( | ||
|  |           coro_.coro_->get_executor(), | ||
|  |           [hh]() mutable { hh.resume(); }).handle; | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  |   auto await_resume() -> result_type | ||
|  |   { | ||
|  |     coro_.coro_->cancel = nullptr; | ||
|  |     coro_.coro_->rethrow_if(); | ||
|  |     if constexpr (!std::is_void_v<result_type>) | ||
|  |       return std::move(coro_.coro_->result_); | ||
|  |   } | ||
|  | }; | ||
|  | 
 | ||
|  | template <typename Yield, typename Return, | ||
|  |     typename Executor, typename Allocator> | ||
|  | struct coro<Yield, Return, Executor, Allocator>::initiate_async_resume | ||
|  | { | ||
|  |   typedef Executor executor_type; | ||
|  |   typedef Allocator allocator_type; | ||
|  |   typedef asio::cancellation_slot cancellation_slot_type; | ||
|  | 
 | ||
|  |   explicit initiate_async_resume(coro* self) | ||
|  |     : coro_(self->coro_) | ||
|  |   { | ||
|  |   } | ||
|  | 
 | ||
|  |   executor_type get_executor() const noexcept | ||
|  |   { | ||
|  |     return coro_->get_executor(); | ||
|  |   } | ||
|  | 
 | ||
|  |   allocator_type get_allocator() const noexcept | ||
|  |   { | ||
|  |     return coro_->get_allocator(); | ||
|  |   } | ||
|  | 
 | ||
|  |   template <typename E, typename WaitHandler> | ||
|  |   auto handle(E exec, WaitHandler&& handler, | ||
|  |       std::true_type /* error is noexcept */, | ||
|  |       std::true_type /* result is void */)  //noexcept
 | ||
|  |   { | ||
|  |     return [this, coro = coro_, | ||
|  |         h = std::forward<WaitHandler>(handler), | ||
|  |         exec = std::move(exec)]() mutable | ||
|  |     { | ||
|  |       assert(coro); | ||
|  | 
 | ||
|  |       auto ch = detail::coroutine_handle<promise_type>::from_promise(*coro); | ||
|  |       assert(ch && !ch.done()); | ||
|  | 
 | ||
|  |       coro->awaited_from = post_coroutine(std::move(exec), std::move(h)); | ||
|  |       coro->reset_error(); | ||
|  |       ch.resume(); | ||
|  |     }; | ||
|  |   } | ||
|  | 
 | ||
|  |   template <typename E, typename WaitHandler> | ||
|  |   requires (!std::is_void_v<result_type>) | ||
|  |   auto handle(E exec, WaitHandler&& handler, | ||
|  |       std::true_type /* error is noexcept */, | ||
|  |       std::false_type  /* result is void */)  //noexcept
 | ||
|  |   { | ||
|  |     return [coro = coro_, | ||
|  |         h = std::forward<WaitHandler>(handler), | ||
|  |         exec = std::move(exec)]() mutable | ||
|  |     { | ||
|  |       assert(coro); | ||
|  | 
 | ||
|  |       auto ch = detail::coroutine_handle<promise_type>::from_promise(*coro); | ||
|  |       assert(ch && !ch.done()); | ||
|  | 
 | ||
|  |       coro->awaited_from = detail::post_coroutine( | ||
|  |           exec, std::move(h), coro->result_).handle; | ||
|  |       coro->reset_error(); | ||
|  |       ch.resume(); | ||
|  |     }; | ||
|  |   } | ||
|  | 
 | ||
|  |   template <typename E, typename WaitHandler> | ||
|  |   auto handle(E exec, WaitHandler&& handler, | ||
|  |       std::false_type /* error is noexcept */, | ||
|  |       std::true_type /* result is void */) | ||
|  |   { | ||
|  |     return [coro = coro_, | ||
|  |         h = std::forward<WaitHandler>(handler), | ||
|  |         exec = std::move(exec)]() mutable | ||
|  |     { | ||
|  |       if (!coro) | ||
|  |         return asio::post(exec, | ||
|  |             asio::append(std::move(h), | ||
|  |               detail::coro_error<error_type>::invalid())); | ||
|  | 
 | ||
|  |       auto ch = detail::coroutine_handle<promise_type>::from_promise(*coro); | ||
|  |       if (!ch) | ||
|  |         return asio::post(exec, | ||
|  |             asio::append(std::move(h), | ||
|  |               detail::coro_error<error_type>::invalid())); | ||
|  |       else if (ch.done()) | ||
|  |         return asio::post(exec, | ||
|  |             asio::append(std::move(h), | ||
|  |               detail::coro_error<error_type>::done())); | ||
|  |       else | ||
|  |       { | ||
|  |         coro->awaited_from = detail::post_coroutine( | ||
|  |             exec, std::move(h), coro->error_).handle; | ||
|  |         coro->reset_error(); | ||
|  |         ch.resume(); | ||
|  |       } | ||
|  |     }; | ||
|  |   } | ||
|  | 
 | ||
|  |   template <typename E, typename WaitHandler> | ||
|  |   auto handle(E exec, WaitHandler&& handler, | ||
|  |       std::false_type /* error is noexcept */, | ||
|  |       std::false_type  /* result is void */) | ||
|  |   { | ||
|  |     return [coro = coro_, | ||
|  |         h = std::forward<WaitHandler>(handler), | ||
|  |         exec = std::move(exec)]() mutable | ||
|  |     { | ||
|  |       if (!coro) | ||
|  |         return asio::post(exec, | ||
|  |             asio::append(std::move(h), | ||
|  |               detail::coro_error<error_type>::invalid(), result_type{})); | ||
|  | 
 | ||
|  |       auto ch = | ||
|  |         detail::coroutine_handle<promise_type>::from_promise(*coro); | ||
|  |       if (!ch) | ||
|  |         return asio::post(exec, | ||
|  |             asio::append(std::move(h), | ||
|  |               detail::coro_error<error_type>::invalid(), result_type{})); | ||
|  |       else if (ch.done()) | ||
|  |         return asio::post(exec, | ||
|  |             asio::append(std::move(h), | ||
|  |               detail::coro_error<error_type>::done(), result_type{})); | ||
|  |       else | ||
|  |       { | ||
|  |         coro->awaited_from = detail::post_coroutine( | ||
|  |             exec, std::move(h), coro->error_, coro->result_).handle; | ||
|  |         coro->reset_error(); | ||
|  |         ch.resume(); | ||
|  |       } | ||
|  |     }; | ||
|  |   } | ||
|  | 
 | ||
|  |   template <typename WaitHandler> | ||
|  |   void operator()(WaitHandler&& handler) | ||
|  |   { | ||
|  |     const auto exec = asio::prefer( | ||
|  |         get_associated_executor(handler, get_executor()), | ||
|  |         execution::outstanding_work.tracked); | ||
|  | 
 | ||
|  |     coro_->cancel = &coro_->cancel_source.emplace(); | ||
|  |     coro_->cancel->state = cancellation_state( | ||
|  |         coro_->cancel->slot = get_associated_cancellation_slot(handler)); | ||
|  |     asio::dispatch(get_executor(), | ||
|  |         handle(exec, std::forward<WaitHandler>(handler), | ||
|  |           std::integral_constant<bool, is_noexcept>{}, | ||
|  |           std::is_void<result_type>{})); | ||
|  |   } | ||
|  | 
 | ||
|  |   template <typename WaitHandler, typename Input> | ||
|  |   void operator()(WaitHandler&& handler, Input&& input) | ||
|  |   { | ||
|  |     const auto exec = asio::prefer( | ||
|  |         get_associated_executor(handler, get_executor()), | ||
|  |         execution::outstanding_work.tracked); | ||
|  | 
 | ||
|  |     coro_->cancel = &coro_->cancel_source.emplace(); | ||
|  |     coro_->cancel->state = cancellation_state( | ||
|  |         coro_->cancel->slot = get_associated_cancellation_slot(handler)); | ||
|  |     asio::dispatch(get_executor(), | ||
|  |         [h = handle(exec, std::forward<WaitHandler>(handler), | ||
|  |             std::integral_constant<bool, is_noexcept>{}, | ||
|  |             std::is_void<result_type>{}), | ||
|  |             in = std::forward<Input>(input), coro = coro_]() mutable | ||
|  |         { | ||
|  |           coro->input_ = std::move(in); | ||
|  |           std::move(h)(); | ||
|  |         }); | ||
|  |   } | ||
|  | 
 | ||
|  | private: | ||
|  |   typename coro::promise_type* coro_; | ||
|  | }; | ||
|  | 
 | ||
|  | } // namespace experimental
 | ||
|  | } // namespace asio
 | ||
|  | 
 | ||
|  | #include "asio/detail/pop_options.hpp"
 | ||
|  | 
 | ||
|  | #endif // ASIO_EXPERIMENTAL_IMPL_CORO_HPP
 |