1196 lines
		
	
	
		
			30 KiB
		
	
	
	
		
			C++
		
	
	
	
		
		
			
		
	
	
			1196 lines
		
	
	
		
			30 KiB
		
	
	
	
		
			C++
		
	
	
	
|  | //
 | ||
|  | // impl/awaitable.hpp
 | ||
|  | // ~~~~~~~~~~~~~~~~~~
 | ||
|  | //
 | ||
|  | // Copyright (c) 2003-2023 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_AWAITABLE_HPP
 | ||
|  | #define ASIO_IMPL_AWAITABLE_HPP
 | ||
|  | 
 | ||
|  | #if defined(_MSC_VER) && (_MSC_VER >= 1200)
 | ||
|  | # pragma once
 | ||
|  | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
 | ||
|  | 
 | ||
|  | #include "asio/detail/config.hpp"
 | ||
|  | #include <exception>
 | ||
|  | #include <new>
 | ||
|  | #include <tuple>
 | ||
|  | #include "asio/cancellation_signal.hpp"
 | ||
|  | #include "asio/cancellation_state.hpp"
 | ||
|  | #include "asio/detail/thread_context.hpp"
 | ||
|  | #include "asio/detail/thread_info_base.hpp"
 | ||
|  | #include "asio/detail/throw_error.hpp"
 | ||
|  | #include "asio/detail/type_traits.hpp"
 | ||
|  | #include "asio/error.hpp"
 | ||
|  | #include "asio/post.hpp"
 | ||
|  | #include "asio/system_error.hpp"
 | ||
|  | #include "asio/this_coro.hpp"
 | ||
|  | 
 | ||
|  | #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 detail { | ||
|  | 
 | ||
|  | struct awaitable_thread_has_context_switched {}; | ||
|  | template <typename, typename> class awaitable_async_op_handler; | ||
|  | template <typename, typename, typename> class awaitable_async_op; | ||
|  | 
 | ||
|  | // An awaitable_thread represents a thread-of-execution that is composed of one
 | ||
|  | // or more "stack frames", with each frame represented by an awaitable_frame.
 | ||
|  | // All execution occurs in the context of the awaitable_thread's executor. An
 | ||
|  | // awaitable_thread continues to "pump" the stack frames by repeatedly resuming
 | ||
|  | // the top stack frame until the stack is empty, or until ownership of the
 | ||
|  | // stack is transferred to another awaitable_thread object.
 | ||
|  | //
 | ||
|  | //                +------------------------------------+
 | ||
|  | //                | top_of_stack_                      |
 | ||
|  | //                |                                    V
 | ||
|  | // +--------------+---+                            +-----------------+
 | ||
|  | // |                  |                            |                 |
 | ||
|  | // | awaitable_thread |<---------------------------+ awaitable_frame |
 | ||
|  | // |                  |           attached_thread_ |                 |
 | ||
|  | // +--------------+---+           (Set only when   +---+-------------+
 | ||
|  | //                |               frames are being     |
 | ||
|  | //                |               actively pumped      | caller_
 | ||
|  | //                |               by a thread, and     |
 | ||
|  | //                |               then only for        V
 | ||
|  | //                |               the top frame.)  +-----------------+
 | ||
|  | //                |                                |                 |
 | ||
|  | //                |                                | awaitable_frame |
 | ||
|  | //                |                                |                 |
 | ||
|  | //                |                                +---+-------------+
 | ||
|  | //                |                                    |
 | ||
|  | //                |                                    | caller_
 | ||
|  | //                |                                    :
 | ||
|  | //                |                                    :
 | ||
|  | //                |                                    |
 | ||
|  | //                |                                    V
 | ||
|  | //                |                                +-----------------+
 | ||
|  | //                | bottom_of_stack_               |                 |
 | ||
|  | //                +------------------------------->| awaitable_frame |
 | ||
|  | //                                                 |                 |
 | ||
|  | //                                                 +-----------------+
 | ||
|  | 
 | ||
|  | template <typename Executor> | ||
|  | class awaitable_frame_base | ||
|  | { | ||
|  | public: | ||
|  | #if !defined(ASIO_DISABLE_AWAITABLE_FRAME_RECYCLING)
 | ||
|  |   void* operator new(std::size_t size) | ||
|  |   { | ||
|  |     return asio::detail::thread_info_base::allocate( | ||
|  |         asio::detail::thread_info_base::awaitable_frame_tag(), | ||
|  |         asio::detail::thread_context::top_of_thread_call_stack(), | ||
|  |         size); | ||
|  |   } | ||
|  | 
 | ||
|  |   void operator delete(void* pointer, std::size_t size) | ||
|  |   { | ||
|  |     asio::detail::thread_info_base::deallocate( | ||
|  |         asio::detail::thread_info_base::awaitable_frame_tag(), | ||
|  |         asio::detail::thread_context::top_of_thread_call_stack(), | ||
|  |         pointer, size); | ||
|  |   } | ||
|  | #endif // !defined(ASIO_DISABLE_AWAITABLE_FRAME_RECYCLING)
 | ||
|  | 
 | ||
|  |   // The frame starts in a suspended state until the awaitable_thread object
 | ||
|  |   // pumps the stack.
 | ||
|  |   auto initial_suspend() noexcept | ||
|  |   { | ||
|  |     return suspend_always(); | ||
|  |   } | ||
|  | 
 | ||
|  |   // On final suspension the frame is popped from the top of the stack.
 | ||
|  |   auto final_suspend() noexcept | ||
|  |   { | ||
|  |     struct result | ||
|  |     { | ||
|  |       awaitable_frame_base* this_; | ||
|  | 
 | ||
|  |       bool await_ready() const noexcept | ||
|  |       { | ||
|  |         return false; | ||
|  |       } | ||
|  | 
 | ||
|  |       void await_suspend(coroutine_handle<void>) noexcept | ||
|  |       { | ||
|  |         this->this_->pop_frame(); | ||
|  |       } | ||
|  | 
 | ||
|  |       void await_resume() const noexcept | ||
|  |       { | ||
|  |       } | ||
|  |     }; | ||
|  | 
 | ||
|  |     return result{this}; | ||
|  |   } | ||
|  | 
 | ||
|  |   void set_except(std::exception_ptr e) noexcept | ||
|  |   { | ||
|  |     pending_exception_ = e; | ||
|  |   } | ||
|  | 
 | ||
|  |   void set_error(const asio::error_code& ec) | ||
|  |   { | ||
|  |     this->set_except(std::make_exception_ptr(asio::system_error(ec))); | ||
|  |   } | ||
|  | 
 | ||
|  |   void unhandled_exception() | ||
|  |   { | ||
|  |     set_except(std::current_exception()); | ||
|  |   } | ||
|  | 
 | ||
|  |   void rethrow_exception() | ||
|  |   { | ||
|  |     if (pending_exception_) | ||
|  |     { | ||
|  |       std::exception_ptr ex = std::exchange(pending_exception_, nullptr); | ||
|  |       std::rethrow_exception(ex); | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  |   void clear_cancellation_slot() | ||
|  |   { | ||
|  |     this->attached_thread_->entry_point()->cancellation_state_.slot().clear(); | ||
|  |   } | ||
|  | 
 | ||
|  |   template <typename T> | ||
|  |   auto await_transform(awaitable<T, Executor> a) const | ||
|  |   { | ||
|  |     if (attached_thread_->entry_point()->throw_if_cancelled_) | ||
|  |       if (!!attached_thread_->get_cancellation_state().cancelled()) | ||
|  |         throw_error(asio::error::operation_aborted, "co_await"); | ||
|  |     return a; | ||
|  |   } | ||
|  | 
 | ||
|  |   template <typename Op> | ||
|  |   auto await_transform(Op&& op, | ||
|  |       typename constraint<is_async_operation<Op>::value>::type = 0 | ||
|  | #if defined(ASIO_ENABLE_HANDLER_TRACKING)
 | ||
|  | # if defined(ASIO_HAS_SOURCE_LOCATION)
 | ||
|  |       , detail::source_location location = detail::source_location::current() | ||
|  | # endif // defined(ASIO_HAS_SOURCE_LOCATION)
 | ||
|  | #endif // defined(ASIO_ENABLE_HANDLER_TRACKING)
 | ||
|  |     ) | ||
|  |   { | ||
|  |     if (attached_thread_->entry_point()->throw_if_cancelled_) | ||
|  |       if (!!attached_thread_->get_cancellation_state().cancelled()) | ||
|  |         throw_error(asio::error::operation_aborted, "co_await"); | ||
|  | 
 | ||
|  |     return awaitable_async_op<typename completion_signature_of<Op>::type, | ||
|  |       typename decay<Op>::type, Executor>{ | ||
|  |         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)
 | ||
|  |       }; | ||
|  |   } | ||
|  | 
 | ||
|  |   // This await transformation obtains the associated executor of the thread of
 | ||
|  |   // execution.
 | ||
|  |   auto await_transform(this_coro::executor_t) noexcept | ||
|  |   { | ||
|  |     struct result | ||
|  |     { | ||
|  |       awaitable_frame_base* this_; | ||
|  | 
 | ||
|  |       bool await_ready() const noexcept | ||
|  |       { | ||
|  |         return true; | ||
|  |       } | ||
|  | 
 | ||
|  |       void await_suspend(coroutine_handle<void>) noexcept | ||
|  |       { | ||
|  |       } | ||
|  | 
 | ||
|  |       auto await_resume() const noexcept | ||
|  |       { | ||
|  |         return this_->attached_thread_->get_executor(); | ||
|  |       } | ||
|  |     }; | ||
|  | 
 | ||
|  |     return result{this}; | ||
|  |   } | ||
|  | 
 | ||
|  |   // This await transformation obtains the associated cancellation state of the
 | ||
|  |   // thread of execution.
 | ||
|  |   auto await_transform(this_coro::cancellation_state_t) noexcept | ||
|  |   { | ||
|  |     struct result | ||
|  |     { | ||
|  |       awaitable_frame_base* this_; | ||
|  | 
 | ||
|  |       bool await_ready() const noexcept | ||
|  |       { | ||
|  |         return true; | ||
|  |       } | ||
|  | 
 | ||
|  |       void await_suspend(coroutine_handle<void>) noexcept | ||
|  |       { | ||
|  |       } | ||
|  | 
 | ||
|  |       auto await_resume() const noexcept | ||
|  |       { | ||
|  |         return this_->attached_thread_->get_cancellation_state(); | ||
|  |       } | ||
|  |     }; | ||
|  | 
 | ||
|  |     return result{this}; | ||
|  |   } | ||
|  | 
 | ||
|  |   // This await transformation resets the associated cancellation state.
 | ||
|  |   auto await_transform(this_coro::reset_cancellation_state_0_t) noexcept | ||
|  |   { | ||
|  |     struct result | ||
|  |     { | ||
|  |       awaitable_frame_base* this_; | ||
|  | 
 | ||
|  |       bool await_ready() const noexcept | ||
|  |       { | ||
|  |         return true; | ||
|  |       } | ||
|  | 
 | ||
|  |       void await_suspend(coroutine_handle<void>) noexcept | ||
|  |       { | ||
|  |       } | ||
|  | 
 | ||
|  |       auto await_resume() const | ||
|  |       { | ||
|  |         return this_->attached_thread_->reset_cancellation_state(); | ||
|  |       } | ||
|  |     }; | ||
|  | 
 | ||
|  |     return result{this}; | ||
|  |   } | ||
|  | 
 | ||
|  |   // 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 | ||
|  |     { | ||
|  |       awaitable_frame_base* this_; | ||
|  |       Filter filter_; | ||
|  | 
 | ||
|  |       bool await_ready() const noexcept | ||
|  |       { | ||
|  |         return true; | ||
|  |       } | ||
|  | 
 | ||
|  |       void await_suspend(coroutine_handle<void>) noexcept | ||
|  |       { | ||
|  |       } | ||
|  | 
 | ||
|  |       auto await_resume() | ||
|  |       { | ||
|  |         return this_->attached_thread_->reset_cancellation_state( | ||
|  |             ASIO_MOVE_CAST(Filter)(filter_)); | ||
|  |       } | ||
|  |     }; | ||
|  | 
 | ||
|  |     return result{this, 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 | ||
|  |     { | ||
|  |       awaitable_frame_base* this_; | ||
|  |       InFilter in_filter_; | ||
|  |       OutFilter out_filter_; | ||
|  | 
 | ||
|  |       bool await_ready() const noexcept | ||
|  |       { | ||
|  |         return true; | ||
|  |       } | ||
|  | 
 | ||
|  |       void await_suspend(coroutine_handle<void>) noexcept | ||
|  |       { | ||
|  |       } | ||
|  | 
 | ||
|  |       auto await_resume() | ||
|  |       { | ||
|  |         return this_->attached_thread_->reset_cancellation_state( | ||
|  |             ASIO_MOVE_CAST(InFilter)(in_filter_), | ||
|  |             ASIO_MOVE_CAST(OutFilter)(out_filter_)); | ||
|  |       } | ||
|  |     }; | ||
|  | 
 | ||
|  |     return result{this, | ||
|  |         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 | ||
|  |   { | ||
|  |     struct result | ||
|  |     { | ||
|  |       awaitable_frame_base* this_; | ||
|  | 
 | ||
|  |       bool await_ready() const noexcept | ||
|  |       { | ||
|  |         return true; | ||
|  |       } | ||
|  | 
 | ||
|  |       void await_suspend(coroutine_handle<void>) noexcept | ||
|  |       { | ||
|  |       } | ||
|  | 
 | ||
|  |       auto await_resume() | ||
|  |       { | ||
|  |         return this_->attached_thread_->throw_if_cancelled(); | ||
|  |       } | ||
|  |     }; | ||
|  | 
 | ||
|  |     return result{this}; | ||
|  |   } | ||
|  | 
 | ||
|  |   // 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 | ||
|  |   { | ||
|  |     struct result | ||
|  |     { | ||
|  |       awaitable_frame_base* this_; | ||
|  |       bool value_; | ||
|  | 
 | ||
|  |       bool await_ready() const noexcept | ||
|  |       { | ||
|  |         return true; | ||
|  |       } | ||
|  | 
 | ||
|  |       void await_suspend(coroutine_handle<void>) noexcept | ||
|  |       { | ||
|  |       } | ||
|  | 
 | ||
|  |       auto await_resume() | ||
|  |       { | ||
|  |         this_->attached_thread_->throw_if_cancelled(value_); | ||
|  |       } | ||
|  |     }; | ||
|  | 
 | ||
|  |     return result{this, throw_if_cancelled.value}; | ||
|  |   } | ||
|  | 
 | ||
|  |   // This await transformation is used to run an async operation's initiation
 | ||
|  |   // function object after the coroutine has been suspended. This ensures that
 | ||
|  |   // immediate resumption of the coroutine in another thread does not cause a
 | ||
|  |   // race condition.
 | ||
|  |   template <typename Function> | ||
|  |   auto await_transform(Function f, | ||
|  |       typename enable_if< | ||
|  |         is_convertible< | ||
|  |           typename result_of<Function(awaitable_frame_base*)>::type, | ||
|  |           awaitable_thread<Executor>* | ||
|  |         >::value | ||
|  |       >::type* = nullptr) | ||
|  |   { | ||
|  |     struct result | ||
|  |     { | ||
|  |       Function function_; | ||
|  |       awaitable_frame_base* this_; | ||
|  | 
 | ||
|  |       bool await_ready() const noexcept | ||
|  |       { | ||
|  |         return false; | ||
|  |       } | ||
|  | 
 | ||
|  |       void await_suspend(coroutine_handle<void>) noexcept | ||
|  |       { | ||
|  |         this_->after_suspend( | ||
|  |             [](void* arg) | ||
|  |             { | ||
|  |               result* r = static_cast<result*>(arg); | ||
|  |               r->function_(r->this_); | ||
|  |             }, this); | ||
|  |       } | ||
|  | 
 | ||
|  |       void await_resume() const noexcept | ||
|  |       { | ||
|  |       } | ||
|  |     }; | ||
|  | 
 | ||
|  |     return result{std::move(f), this}; | ||
|  |   } | ||
|  | 
 | ||
|  |   // Access the awaitable thread's has_context_switched_ flag.
 | ||
|  |   auto await_transform(detail::awaitable_thread_has_context_switched) noexcept | ||
|  |   { | ||
|  |     struct result | ||
|  |     { | ||
|  |       awaitable_frame_base* this_; | ||
|  | 
 | ||
|  |       bool await_ready() const noexcept | ||
|  |       { | ||
|  |         return true; | ||
|  |       } | ||
|  | 
 | ||
|  |       void await_suspend(coroutine_handle<void>) noexcept | ||
|  |       { | ||
|  |       } | ||
|  | 
 | ||
|  |       bool& await_resume() const noexcept | ||
|  |       { | ||
|  |         return this_->attached_thread_->entry_point()->has_context_switched_; | ||
|  |       } | ||
|  |     }; | ||
|  | 
 | ||
|  |     return result{this}; | ||
|  |   } | ||
|  | 
 | ||
|  |   void attach_thread(awaitable_thread<Executor>* handler) noexcept | ||
|  |   { | ||
|  |     attached_thread_ = handler; | ||
|  |   } | ||
|  | 
 | ||
|  |   awaitable_thread<Executor>* detach_thread() noexcept | ||
|  |   { | ||
|  |     attached_thread_->entry_point()->has_context_switched_ = true; | ||
|  |     return std::exchange(attached_thread_, nullptr); | ||
|  |   } | ||
|  | 
 | ||
|  |   void push_frame(awaitable_frame_base<Executor>* caller) noexcept | ||
|  |   { | ||
|  |     caller_ = caller; | ||
|  |     attached_thread_ = caller_->attached_thread_; | ||
|  |     attached_thread_->entry_point()->top_of_stack_ = this; | ||
|  |     caller_->attached_thread_ = nullptr; | ||
|  |   } | ||
|  | 
 | ||
|  |   void pop_frame() noexcept | ||
|  |   { | ||
|  |     if (caller_) | ||
|  |       caller_->attached_thread_ = attached_thread_; | ||
|  |     attached_thread_->entry_point()->top_of_stack_ = caller_; | ||
|  |     attached_thread_ = nullptr; | ||
|  |     caller_ = nullptr; | ||
|  |   } | ||
|  | 
 | ||
|  |   struct resume_context | ||
|  |   { | ||
|  |     void (*after_suspend_fn_)(void*) = nullptr; | ||
|  |     void *after_suspend_arg_ = nullptr; | ||
|  |   }; | ||
|  | 
 | ||
|  |   void resume() | ||
|  |   { | ||
|  |     resume_context context; | ||
|  |     resume_context_ = &context; | ||
|  |     coro_.resume(); | ||
|  |     if (context.after_suspend_fn_) | ||
|  |       context.after_suspend_fn_(context.after_suspend_arg_); | ||
|  |   } | ||
|  | 
 | ||
|  |   void after_suspend(void (*fn)(void*), void* arg) | ||
|  |   { | ||
|  |     resume_context_->after_suspend_fn_ = fn; | ||
|  |     resume_context_->after_suspend_arg_ = arg; | ||
|  |   } | ||
|  | 
 | ||
|  |   void destroy() | ||
|  |   { | ||
|  |     coro_.destroy(); | ||
|  |   } | ||
|  | 
 | ||
|  | protected: | ||
|  |   coroutine_handle<void> coro_ = nullptr; | ||
|  |   awaitable_thread<Executor>* attached_thread_ = nullptr; | ||
|  |   awaitable_frame_base<Executor>* caller_ = nullptr; | ||
|  |   std::exception_ptr pending_exception_ = nullptr; | ||
|  |   resume_context* resume_context_ = nullptr; | ||
|  | }; | ||
|  | 
 | ||
|  | template <typename T, typename Executor> | ||
|  | class awaitable_frame | ||
|  |   : public awaitable_frame_base<Executor> | ||
|  | { | ||
|  | public: | ||
|  |   awaitable_frame() noexcept | ||
|  |   { | ||
|  |   } | ||
|  | 
 | ||
|  |   awaitable_frame(awaitable_frame&& other) noexcept | ||
|  |     : awaitable_frame_base<Executor>(std::move(other)) | ||
|  |   { | ||
|  |   } | ||
|  | 
 | ||
|  |   ~awaitable_frame() | ||
|  |   { | ||
|  |     if (has_result_) | ||
|  |       static_cast<T*>(static_cast<void*>(result_))->~T(); | ||
|  |   } | ||
|  | 
 | ||
|  |   awaitable<T, Executor> get_return_object() noexcept | ||
|  |   { | ||
|  |     this->coro_ = coroutine_handle<awaitable_frame>::from_promise(*this); | ||
|  |     return awaitable<T, Executor>(this); | ||
|  |   }; | ||
|  | 
 | ||
|  |   template <typename U> | ||
|  |   void return_value(U&& u) | ||
|  |   { | ||
|  |     new (&result_) T(std::forward<U>(u)); | ||
|  |     has_result_ = true; | ||
|  |   } | ||
|  | 
 | ||
|  |   template <typename... Us> | ||
|  |   void return_values(Us&&... us) | ||
|  |   { | ||
|  |     this->return_value(std::forward_as_tuple(std::forward<Us>(us)...)); | ||
|  |   } | ||
|  | 
 | ||
|  |   T get() | ||
|  |   { | ||
|  |     this->caller_ = nullptr; | ||
|  |     this->rethrow_exception(); | ||
|  |     return std::move(*static_cast<T*>(static_cast<void*>(result_))); | ||
|  |   } | ||
|  | 
 | ||
|  | private: | ||
|  |   alignas(T) unsigned char result_[sizeof(T)]; | ||
|  |   bool has_result_ = false; | ||
|  | }; | ||
|  | 
 | ||
|  | template <typename Executor> | ||
|  | class awaitable_frame<void, Executor> | ||
|  |   : public awaitable_frame_base<Executor> | ||
|  | { | ||
|  | public: | ||
|  |   awaitable<void, Executor> get_return_object() | ||
|  |   { | ||
|  |     this->coro_ = coroutine_handle<awaitable_frame>::from_promise(*this); | ||
|  |     return awaitable<void, Executor>(this); | ||
|  |   }; | ||
|  | 
 | ||
|  |   void return_void() | ||
|  |   { | ||
|  |   } | ||
|  | 
 | ||
|  |   void get() | ||
|  |   { | ||
|  |     this->caller_ = nullptr; | ||
|  |     this->rethrow_exception(); | ||
|  |   } | ||
|  | }; | ||
|  | 
 | ||
|  | struct awaitable_thread_entry_point {}; | ||
|  | 
 | ||
|  | template <typename Executor> | ||
|  | class awaitable_frame<awaitable_thread_entry_point, Executor> | ||
|  |   : public awaitable_frame_base<Executor> | ||
|  | { | ||
|  | public: | ||
|  |   awaitable_frame() | ||
|  |     : top_of_stack_(0), | ||
|  |       has_executor_(false), | ||
|  |       has_context_switched_(false), | ||
|  |       throw_if_cancelled_(true) | ||
|  |   { | ||
|  |   } | ||
|  | 
 | ||
|  |   ~awaitable_frame() | ||
|  |   { | ||
|  |     if (has_executor_) | ||
|  |       u_.executor_.~Executor(); | ||
|  |   } | ||
|  | 
 | ||
|  |   awaitable<awaitable_thread_entry_point, Executor> get_return_object() | ||
|  |   { | ||
|  |     this->coro_ = coroutine_handle<awaitable_frame>::from_promise(*this); | ||
|  |     return awaitable<awaitable_thread_entry_point, Executor>(this); | ||
|  |   }; | ||
|  | 
 | ||
|  |   void return_void() | ||
|  |   { | ||
|  |   } | ||
|  | 
 | ||
|  |   void get() | ||
|  |   { | ||
|  |     this->caller_ = nullptr; | ||
|  |     this->rethrow_exception(); | ||
|  |   } | ||
|  | 
 | ||
|  | private: | ||
|  |   template <typename> friend class awaitable_frame_base; | ||
|  |   template <typename, typename> friend class awaitable_async_op_handler; | ||
|  |   template <typename, typename> friend class awaitable_handler_base; | ||
|  |   template <typename> friend class awaitable_thread; | ||
|  | 
 | ||
|  |   union u | ||
|  |   { | ||
|  |     u() {} | ||
|  |     ~u() {} | ||
|  |     char c_; | ||
|  |     Executor executor_; | ||
|  |   } u_; | ||
|  | 
 | ||
|  |   awaitable_frame_base<Executor>* top_of_stack_; | ||
|  |   asio::cancellation_slot parent_cancellation_slot_; | ||
|  |   asio::cancellation_state cancellation_state_; | ||
|  |   bool has_executor_; | ||
|  |   bool has_context_switched_; | ||
|  |   bool throw_if_cancelled_; | ||
|  | }; | ||
|  | 
 | ||
|  | template <typename Executor> | ||
|  | class awaitable_thread | ||
|  | { | ||
|  | public: | ||
|  |   typedef Executor executor_type; | ||
|  |   typedef cancellation_slot cancellation_slot_type; | ||
|  | 
 | ||
|  |   // Construct from the entry point of a new thread of execution.
 | ||
|  |   awaitable_thread(awaitable<awaitable_thread_entry_point, Executor> p, | ||
|  |       const Executor& ex, cancellation_slot parent_cancel_slot, | ||
|  |       cancellation_state cancel_state) | ||
|  |     : bottom_of_stack_(std::move(p)) | ||
|  |   { | ||
|  |     bottom_of_stack_.frame_->top_of_stack_ = bottom_of_stack_.frame_; | ||
|  |     new (&bottom_of_stack_.frame_->u_.executor_) Executor(ex); | ||
|  |     bottom_of_stack_.frame_->has_executor_ = true; | ||
|  |     bottom_of_stack_.frame_->parent_cancellation_slot_ = parent_cancel_slot; | ||
|  |     bottom_of_stack_.frame_->cancellation_state_ = cancel_state; | ||
|  |   } | ||
|  | 
 | ||
|  |   // Transfer ownership from another awaitable_thread.
 | ||
|  |   awaitable_thread(awaitable_thread&& other) noexcept | ||
|  |     : bottom_of_stack_(std::move(other.bottom_of_stack_)) | ||
|  |   { | ||
|  |   } | ||
|  | 
 | ||
|  |   // Clean up with a last ditch effort to ensure the thread is unwound within
 | ||
|  |   // the context of the executor.
 | ||
|  |   ~awaitable_thread() | ||
|  |   { | ||
|  |     if (bottom_of_stack_.valid()) | ||
|  |     { | ||
|  |       // Coroutine "stack unwinding" must be performed through the executor.
 | ||
|  |       auto* bottom_frame = bottom_of_stack_.frame_; | ||
|  |       (post)(bottom_frame->u_.executor_, | ||
|  |           [a = std::move(bottom_of_stack_)]() mutable | ||
|  |           { | ||
|  |             (void)awaitable<awaitable_thread_entry_point, Executor>( | ||
|  |                 std::move(a)); | ||
|  |           }); | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  |   awaitable_frame<awaitable_thread_entry_point, Executor>* entry_point() | ||
|  |   { | ||
|  |     return bottom_of_stack_.frame_; | ||
|  |   } | ||
|  | 
 | ||
|  |   executor_type get_executor() const noexcept | ||
|  |   { | ||
|  |     return bottom_of_stack_.frame_->u_.executor_; | ||
|  |   } | ||
|  | 
 | ||
|  |   cancellation_state get_cancellation_state() const noexcept | ||
|  |   { | ||
|  |     return bottom_of_stack_.frame_->cancellation_state_; | ||
|  |   } | ||
|  | 
 | ||
|  |   void reset_cancellation_state() | ||
|  |   { | ||
|  |     bottom_of_stack_.frame_->cancellation_state_ = | ||
|  |       cancellation_state(bottom_of_stack_.frame_->parent_cancellation_slot_); | ||
|  |   } | ||
|  | 
 | ||
|  |   template <typename Filter> | ||
|  |   void reset_cancellation_state(ASIO_MOVE_ARG(Filter) filter) | ||
|  |   { | ||
|  |     bottom_of_stack_.frame_->cancellation_state_ = | ||
|  |       cancellation_state(bottom_of_stack_.frame_->parent_cancellation_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) | ||
|  |   { | ||
|  |     bottom_of_stack_.frame_->cancellation_state_ = | ||
|  |       cancellation_state(bottom_of_stack_.frame_->parent_cancellation_slot_, | ||
|  |         ASIO_MOVE_CAST(InFilter)(in_filter), | ||
|  |         ASIO_MOVE_CAST(OutFilter)(out_filter)); | ||
|  |   } | ||
|  | 
 | ||
|  |   bool throw_if_cancelled() const | ||
|  |   { | ||
|  |     return bottom_of_stack_.frame_->throw_if_cancelled_; | ||
|  |   } | ||
|  | 
 | ||
|  |   void throw_if_cancelled(bool value) | ||
|  |   { | ||
|  |     bottom_of_stack_.frame_->throw_if_cancelled_ = value; | ||
|  |   } | ||
|  | 
 | ||
|  |   cancellation_slot_type get_cancellation_slot() const noexcept | ||
|  |   { | ||
|  |     return bottom_of_stack_.frame_->cancellation_state_.slot(); | ||
|  |   } | ||
|  | 
 | ||
|  |   // Launch a new thread of execution.
 | ||
|  |   void launch() | ||
|  |   { | ||
|  |     bottom_of_stack_.frame_->top_of_stack_->attach_thread(this); | ||
|  |     pump(); | ||
|  |   } | ||
|  | 
 | ||
|  | protected: | ||
|  |   template <typename> friend class awaitable_frame_base; | ||
|  | 
 | ||
|  |   // Repeatedly resume the top stack frame until the stack is empty or until it
 | ||
|  |   // has been transferred to another resumable_thread object.
 | ||
|  |   void pump() | ||
|  |   { | ||
|  |     do | ||
|  |       bottom_of_stack_.frame_->top_of_stack_->resume(); | ||
|  |     while (bottom_of_stack_.frame_ && bottom_of_stack_.frame_->top_of_stack_); | ||
|  | 
 | ||
|  |     if (bottom_of_stack_.frame_) | ||
|  |     { | ||
|  |       awaitable<awaitable_thread_entry_point, Executor> a( | ||
|  |           std::move(bottom_of_stack_)); | ||
|  |       a.frame_->rethrow_exception(); | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  |   awaitable<awaitable_thread_entry_point, Executor> bottom_of_stack_; | ||
|  | }; | ||
|  | 
 | ||
|  | template <typename Signature, typename Executor> | ||
|  | class awaitable_async_op_handler; | ||
|  | 
 | ||
|  | template <typename R, typename Executor> | ||
|  | class awaitable_async_op_handler<R(), Executor> | ||
|  |   : public awaitable_thread<Executor> | ||
|  | { | ||
|  | public: | ||
|  |   struct result_type {}; | ||
|  | 
 | ||
|  |   awaitable_async_op_handler( | ||
|  |       awaitable_thread<Executor>* h, result_type&) | ||
|  |     : awaitable_thread<Executor>(std::move(*h)) | ||
|  |   { | ||
|  |   } | ||
|  | 
 | ||
|  |   void operator()() | ||
|  |   { | ||
|  |     this->entry_point()->top_of_stack_->attach_thread(this); | ||
|  |     this->entry_point()->top_of_stack_->clear_cancellation_slot(); | ||
|  |     this->pump(); | ||
|  |   } | ||
|  | 
 | ||
|  |   static void resume(result_type&) | ||
|  |   { | ||
|  |   } | ||
|  | }; | ||
|  | 
 | ||
|  | template <typename R, typename Executor> | ||
|  | class awaitable_async_op_handler<R(asio::error_code), Executor> | ||
|  |   : public awaitable_thread<Executor> | ||
|  | { | ||
|  | public: | ||
|  |   typedef asio::error_code* result_type; | ||
|  | 
 | ||
|  |   awaitable_async_op_handler( | ||
|  |       awaitable_thread<Executor>* h, result_type& result) | ||
|  |     : awaitable_thread<Executor>(std::move(*h)), | ||
|  |       result_(result) | ||
|  |   { | ||
|  |   } | ||
|  | 
 | ||
|  |   void operator()(asio::error_code ec) | ||
|  |   { | ||
|  |     result_ = &ec; | ||
|  |     this->entry_point()->top_of_stack_->attach_thread(this); | ||
|  |     this->entry_point()->top_of_stack_->clear_cancellation_slot(); | ||
|  |     this->pump(); | ||
|  |   } | ||
|  | 
 | ||
|  |   static void resume(result_type& result) | ||
|  |   { | ||
|  |     throw_error(*result); | ||
|  |   } | ||
|  | 
 | ||
|  | private: | ||
|  |   result_type& result_; | ||
|  | }; | ||
|  | 
 | ||
|  | template <typename R, typename Executor> | ||
|  | class awaitable_async_op_handler<R(std::exception_ptr), Executor> | ||
|  |   : public awaitable_thread<Executor> | ||
|  | { | ||
|  | public: | ||
|  |   typedef std::exception_ptr* result_type; | ||
|  | 
 | ||
|  |   awaitable_async_op_handler( | ||
|  |       awaitable_thread<Executor>* h, result_type& result) | ||
|  |     : awaitable_thread<Executor>(std::move(*h)), | ||
|  |       result_(result) | ||
|  |   { | ||
|  |   } | ||
|  | 
 | ||
|  |   void operator()(std::exception_ptr ex) | ||
|  |   { | ||
|  |     result_ = &ex; | ||
|  |     this->entry_point()->top_of_stack_->attach_thread(this); | ||
|  |     this->entry_point()->top_of_stack_->clear_cancellation_slot(); | ||
|  |     this->pump(); | ||
|  |   } | ||
|  | 
 | ||
|  |   static void resume(result_type& result) | ||
|  |   { | ||
|  |     if (*result) | ||
|  |     { | ||
|  |       std::exception_ptr ex = std::exchange(*result, nullptr); | ||
|  |       std::rethrow_exception(ex); | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  | private: | ||
|  |   result_type& result_; | ||
|  | }; | ||
|  | 
 | ||
|  | template <typename R, typename T, typename Executor> | ||
|  | class awaitable_async_op_handler<R(T), Executor> | ||
|  |   : public awaitable_thread<Executor> | ||
|  | { | ||
|  | public: | ||
|  |   typedef T* result_type; | ||
|  | 
 | ||
|  |   awaitable_async_op_handler( | ||
|  |       awaitable_thread<Executor>* h, result_type& result) | ||
|  |     : awaitable_thread<Executor>(std::move(*h)), | ||
|  |       result_(result) | ||
|  |   { | ||
|  |   } | ||
|  | 
 | ||
|  |   void operator()(T result) | ||
|  |   { | ||
|  |     result_ = &result; | ||
|  |     this->entry_point()->top_of_stack_->attach_thread(this); | ||
|  |     this->entry_point()->top_of_stack_->clear_cancellation_slot(); | ||
|  |     this->pump(); | ||
|  |   } | ||
|  | 
 | ||
|  |   static T resume(result_type& result) | ||
|  |   { | ||
|  |     return std::move(*result); | ||
|  |   } | ||
|  | 
 | ||
|  | private: | ||
|  |   result_type& result_; | ||
|  | }; | ||
|  | 
 | ||
|  | template <typename R, typename T, typename Executor> | ||
|  | class awaitable_async_op_handler<R(asio::error_code, T), Executor> | ||
|  |   : public awaitable_thread<Executor> | ||
|  | { | ||
|  | public: | ||
|  |   struct result_type | ||
|  |   { | ||
|  |     asio::error_code* ec_; | ||
|  |     T* value_; | ||
|  |   }; | ||
|  | 
 | ||
|  |   awaitable_async_op_handler( | ||
|  |       awaitable_thread<Executor>* h, result_type& result) | ||
|  |     : awaitable_thread<Executor>(std::move(*h)), | ||
|  |       result_(result) | ||
|  |   { | ||
|  |   } | ||
|  | 
 | ||
|  |   void operator()(asio::error_code ec, T value) | ||
|  |   { | ||
|  |     result_.ec_ = &ec; | ||
|  |     result_.value_ = &value; | ||
|  |     this->entry_point()->top_of_stack_->attach_thread(this); | ||
|  |     this->entry_point()->top_of_stack_->clear_cancellation_slot(); | ||
|  |     this->pump(); | ||
|  |   } | ||
|  | 
 | ||
|  |   static T resume(result_type& result) | ||
|  |   { | ||
|  |     throw_error(*result.ec_); | ||
|  |     return std::move(*result.value_); | ||
|  |   } | ||
|  | 
 | ||
|  | private: | ||
|  |   result_type& result_; | ||
|  | }; | ||
|  | 
 | ||
|  | template <typename R, typename T, typename Executor> | ||
|  | class awaitable_async_op_handler<R(std::exception_ptr, T), Executor> | ||
|  |   : public awaitable_thread<Executor> | ||
|  | { | ||
|  | public: | ||
|  |   struct result_type | ||
|  |   { | ||
|  |     std::exception_ptr* ex_; | ||
|  |     T* value_; | ||
|  |   }; | ||
|  | 
 | ||
|  |   awaitable_async_op_handler( | ||
|  |       awaitable_thread<Executor>* h, result_type& result) | ||
|  |     : awaitable_thread<Executor>(std::move(*h)), | ||
|  |       result_(result) | ||
|  |   { | ||
|  |   } | ||
|  | 
 | ||
|  |   void operator()(std::exception_ptr ex, T value) | ||
|  |   { | ||
|  |     result_.ex_ = &ex; | ||
|  |     result_.value_ = &value; | ||
|  |     this->entry_point()->top_of_stack_->attach_thread(this); | ||
|  |     this->entry_point()->top_of_stack_->clear_cancellation_slot(); | ||
|  |     this->pump(); | ||
|  |   } | ||
|  | 
 | ||
|  |   static T resume(result_type& result) | ||
|  |   { | ||
|  |     if (*result.ex_) | ||
|  |     { | ||
|  |       std::exception_ptr ex = std::exchange(*result.ex_, nullptr); | ||
|  |       std::rethrow_exception(ex); | ||
|  |     } | ||
|  |     return std::move(*result.value_); | ||
|  |   } | ||
|  | 
 | ||
|  | private: | ||
|  |   result_type& result_; | ||
|  | }; | ||
|  | 
 | ||
|  | template <typename R, typename... Ts, typename Executor> | ||
|  | class awaitable_async_op_handler<R(Ts...), Executor> | ||
|  |   : public awaitable_thread<Executor> | ||
|  | { | ||
|  | public: | ||
|  |   typedef std::tuple<Ts...>* result_type; | ||
|  | 
 | ||
|  |   awaitable_async_op_handler( | ||
|  |       awaitable_thread<Executor>* h, result_type& result) | ||
|  |     : awaitable_thread<Executor>(std::move(*h)), | ||
|  |       result_(result) | ||
|  |   { | ||
|  |   } | ||
|  | 
 | ||
|  |   template <typename... Args> | ||
|  |   void operator()(Args&&... args) | ||
|  |   { | ||
|  |     std::tuple<Ts...> result(std::forward<Args>(args)...); | ||
|  |     result_ = &result; | ||
|  |     this->entry_point()->top_of_stack_->attach_thread(this); | ||
|  |     this->entry_point()->top_of_stack_->clear_cancellation_slot(); | ||
|  |     this->pump(); | ||
|  |   } | ||
|  | 
 | ||
|  |   static std::tuple<Ts...> resume(result_type& result) | ||
|  |   { | ||
|  |     return std::move(*result); | ||
|  |   } | ||
|  | 
 | ||
|  | private: | ||
|  |   result_type& result_; | ||
|  | }; | ||
|  | 
 | ||
|  | template <typename R, typename... Ts, typename Executor> | ||
|  | class awaitable_async_op_handler<R(asio::error_code, Ts...), Executor> | ||
|  |   : public awaitable_thread<Executor> | ||
|  | { | ||
|  | public: | ||
|  |   struct result_type | ||
|  |   { | ||
|  |     asio::error_code* ec_; | ||
|  |     std::tuple<Ts...>* value_; | ||
|  |   }; | ||
|  | 
 | ||
|  |   awaitable_async_op_handler( | ||
|  |       awaitable_thread<Executor>* h, result_type& result) | ||
|  |     : awaitable_thread<Executor>(std::move(*h)), | ||
|  |       result_(result) | ||
|  |   { | ||
|  |   } | ||
|  | 
 | ||
|  |   template <typename... Args> | ||
|  |   void operator()(asio::error_code ec, Args&&... args) | ||
|  |   { | ||
|  |     result_.ec_ = &ec; | ||
|  |     std::tuple<Ts...> value(std::forward<Args>(args)...); | ||
|  |     result_.value_ = &value; | ||
|  |     this->entry_point()->top_of_stack_->attach_thread(this); | ||
|  |     this->entry_point()->top_of_stack_->clear_cancellation_slot(); | ||
|  |     this->pump(); | ||
|  |   } | ||
|  | 
 | ||
|  |   static std::tuple<Ts...> resume(result_type& result) | ||
|  |   { | ||
|  |     throw_error(*result.ec_); | ||
|  |     return std::move(*result.value_); | ||
|  |   } | ||
|  | 
 | ||
|  | private: | ||
|  |   result_type& result_; | ||
|  | }; | ||
|  | 
 | ||
|  | template <typename R, typename... Ts, typename Executor> | ||
|  | class awaitable_async_op_handler<R(std::exception_ptr, Ts...), Executor> | ||
|  |   : public awaitable_thread<Executor> | ||
|  | { | ||
|  | public: | ||
|  |   struct result_type | ||
|  |   { | ||
|  |     std::exception_ptr* ex_; | ||
|  |     std::tuple<Ts...>* value_; | ||
|  |   }; | ||
|  | 
 | ||
|  |   awaitable_async_op_handler( | ||
|  |       awaitable_thread<Executor>* h, result_type& result) | ||
|  |     : awaitable_thread<Executor>(std::move(*h)), | ||
|  |       result_(result) | ||
|  |   { | ||
|  |   } | ||
|  | 
 | ||
|  |   template <typename... Args> | ||
|  |   void operator()(std::exception_ptr ex, Args&&... args) | ||
|  |   { | ||
|  |     result_.ex_ = &ex; | ||
|  |     std::tuple<Ts...> value(std::forward<Args>(args)...); | ||
|  |     result_.value_ = &value; | ||
|  |     this->entry_point()->top_of_stack_->attach_thread(this); | ||
|  |     this->entry_point()->top_of_stack_->clear_cancellation_slot(); | ||
|  |     this->pump(); | ||
|  |   } | ||
|  | 
 | ||
|  |   static std::tuple<Ts...> resume(result_type& result) | ||
|  |   { | ||
|  |     if (*result.ex_) | ||
|  |     { | ||
|  |       std::exception_ptr ex = std::exchange(*result.ex_, nullptr); | ||
|  |       std::rethrow_exception(ex); | ||
|  |     } | ||
|  |     return std::move(*result.value_); | ||
|  |   } | ||
|  | 
 | ||
|  | private: | ||
|  |   result_type& result_; | ||
|  | }; | ||
|  | 
 | ||
|  | template <typename Signature, typename Op, typename Executor> | ||
|  | class awaitable_async_op | ||
|  | { | ||
|  | public: | ||
|  |   typedef awaitable_async_op_handler<Signature, Executor> handler_type; | ||
|  | 
 | ||
|  |   awaitable_async_op(Op&& o, awaitable_frame_base<Executor>* frame | ||
|  | #if defined(ASIO_ENABLE_HANDLER_TRACKING)
 | ||
|  | # if defined(ASIO_HAS_SOURCE_LOCATION)
 | ||
|  |       , const detail::source_location& location | ||
|  | # endif // defined(ASIO_HAS_SOURCE_LOCATION)
 | ||
|  | #endif // defined(ASIO_ENABLE_HANDLER_TRACKING)
 | ||
|  |     ) | ||
|  |     : op_(std::forward<Op>(o)), | ||
|  |       frame_(frame), | ||
|  |       result_() | ||
|  | #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)
 | ||
|  |   { | ||
|  |   } | ||
|  | 
 | ||
|  |   bool await_ready() const noexcept | ||
|  |   { | ||
|  |     return false; | ||
|  |   } | ||
|  | 
 | ||
|  |   void await_suspend(coroutine_handle<void>) | ||
|  |   { | ||
|  |     frame_->after_suspend( | ||
|  |         [](void* arg) | ||
|  |         { | ||
|  |           awaitable_async_op* self = static_cast<awaitable_async_op*>(arg); | ||
|  | #if defined(ASIO_ENABLE_HANDLER_TRACKING)
 | ||
|  | # if defined(ASIO_HAS_SOURCE_LOCATION)
 | ||
|  |           ASIO_HANDLER_LOCATION((self->location_.file_name(), | ||
|  |               self->location_.line(), self->location_.function_name())); | ||
|  | # endif // defined(ASIO_HAS_SOURCE_LOCATION)
 | ||
|  | #endif // defined(ASIO_ENABLE_HANDLER_TRACKING)
 | ||
|  |           std::forward<Op&&>(self->op_)( | ||
|  |               handler_type(self->frame_->detach_thread(), self->result_)); | ||
|  |         }, this); | ||
|  |   } | ||
|  | 
 | ||
|  |   auto await_resume() | ||
|  |   { | ||
|  |     return handler_type::resume(result_); | ||
|  |   } | ||
|  | 
 | ||
|  | private: | ||
|  |   Op&& op_; | ||
|  |   awaitable_frame_base<Executor>* frame_; | ||
|  |   typename handler_type::result_type result_; | ||
|  | #if defined(ASIO_ENABLE_HANDLER_TRACKING)
 | ||
|  | # if defined(ASIO_HAS_SOURCE_LOCATION)
 | ||
|  |   detail::source_location location_; | ||
|  | # endif // defined(ASIO_HAS_SOURCE_LOCATION)
 | ||
|  | #endif // defined(ASIO_ENABLE_HANDLER_TRACKING)
 | ||
|  | }; | ||
|  | 
 | ||
|  | } // namespace detail
 | ||
|  | } // namespace asio
 | ||
|  | 
 | ||
|  | #if !defined(GENERATING_DOCUMENTATION)
 | ||
|  | # if defined(ASIO_HAS_STD_COROUTINE)
 | ||
|  | 
 | ||
|  | namespace std { | ||
|  | 
 | ||
|  | template <typename T, typename Executor, typename... Args> | ||
|  | struct coroutine_traits<asio::awaitable<T, Executor>, Args...> | ||
|  | { | ||
|  |   typedef asio::detail::awaitable_frame<T, Executor> promise_type; | ||
|  | }; | ||
|  | 
 | ||
|  | } // namespace std
 | ||
|  | 
 | ||
|  | # else // defined(ASIO_HAS_STD_COROUTINE)
 | ||
|  | 
 | ||
|  | namespace std { namespace experimental { | ||
|  | 
 | ||
|  | template <typename T, typename Executor, typename... Args> | ||
|  | struct coroutine_traits<asio::awaitable<T, Executor>, Args...> | ||
|  | { | ||
|  |   typedef asio::detail::awaitable_frame<T, Executor> promise_type; | ||
|  | }; | ||
|  | 
 | ||
|  | }} // namespace std::experimental
 | ||
|  | 
 | ||
|  | # endif // defined(ASIO_HAS_STD_COROUTINE)
 | ||
|  | #endif // !defined(GENERATING_DOCUMENTATION)
 | ||
|  | 
 | ||
|  | #include "asio/detail/pop_options.hpp"
 | ||
|  | 
 | ||
|  | #endif // ASIO_IMPL_AWAITABLE_HPP
 |