524 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
		
		
			
		
	
	
			524 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
|  | //
 | ||
|  | // co_spawn.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_CO_SPAWN_HPP
 | ||
|  | #define ASIO_CO_SPAWN_HPP
 | ||
|  | 
 | ||
|  | #if defined(_MSC_VER) && (_MSC_VER >= 1200)
 | ||
|  | # pragma once
 | ||
|  | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
 | ||
|  | 
 | ||
|  | #include "asio/detail/config.hpp"
 | ||
|  | 
 | ||
|  | #if defined(ASIO_HAS_CO_AWAIT) || defined(GENERATING_DOCUMENTATION)
 | ||
|  | 
 | ||
|  | #include "asio/awaitable.hpp"
 | ||
|  | #include "asio/execution/executor.hpp"
 | ||
|  | #include "asio/execution_context.hpp"
 | ||
|  | #include "asio/is_executor.hpp"
 | ||
|  | 
 | ||
|  | #include "asio/detail/push_options.hpp"
 | ||
|  | 
 | ||
|  | namespace asio { | ||
|  | namespace detail { | ||
|  | 
 | ||
|  | template <typename T> | ||
|  | struct awaitable_signature; | ||
|  | 
 | ||
|  | template <typename T, typename Executor> | ||
|  | struct awaitable_signature<awaitable<T, Executor>> | ||
|  | { | ||
|  |   typedef void type(std::exception_ptr, T); | ||
|  | }; | ||
|  | 
 | ||
|  | template <typename Executor> | ||
|  | struct awaitable_signature<awaitable<void, Executor>> | ||
|  | { | ||
|  |   typedef void type(std::exception_ptr); | ||
|  | }; | ||
|  | 
 | ||
|  | } // namespace detail
 | ||
|  | 
 | ||
|  | /// Spawn a new coroutined-based thread of execution.
 | ||
|  | /**
 | ||
|  |  * @param ex The executor that will be used to schedule the new thread of | ||
|  |  * execution. | ||
|  |  * | ||
|  |  * @param a The asio::awaitable object that is the result of calling the | ||
|  |  * coroutine's entry point function. | ||
|  |  * | ||
|  |  * @param token The @ref completion_token that will handle the notification that | ||
|  |  * the thread of execution has completed. The function signature of the | ||
|  |  * completion handler must be: | ||
|  |  * @code void handler(std::exception_ptr, T); @endcode | ||
|  |  * | ||
|  |  * @par Completion Signature | ||
|  |  * @code void(std::exception_ptr, T) @endcode | ||
|  |  * | ||
|  |  * @par Example | ||
|  |  * @code | ||
|  |  * asio::awaitable<std::size_t> echo(tcp::socket socket) | ||
|  |  * { | ||
|  |  *   std::size_t bytes_transferred = 0; | ||
|  |  * | ||
|  |  *   try | ||
|  |  *   { | ||
|  |  *     char data[1024]; | ||
|  |  *     for (;;) | ||
|  |  *     { | ||
|  |  *       std::size_t n = co_await socket.async_read_some( | ||
|  |  *           asio::buffer(data), asio::use_awaitable); | ||
|  |  * | ||
|  |  *       co_await asio::async_write(socket, | ||
|  |  *           asio::buffer(data, n), asio::use_awaitable); | ||
|  |  * | ||
|  |  *       bytes_transferred += n; | ||
|  |  *     } | ||
|  |  *   } | ||
|  |  *   catch (const std::exception&) | ||
|  |  *   { | ||
|  |  *   } | ||
|  |  * | ||
|  |  *   co_return bytes_transferred; | ||
|  |  * } | ||
|  |  * | ||
|  |  * // ...
 | ||
|  |  * | ||
|  |  * asio::co_spawn(my_executor, | ||
|  |  *   echo(std::move(my_tcp_socket)), | ||
|  |  *   [](std::exception_ptr e, std::size_t n) | ||
|  |  *   { | ||
|  |  *     std::cout << "transferred " << n << "\n"; | ||
|  |  *   }); | ||
|  |  * @endcode | ||
|  |  * | ||
|  |  * @par Per-Operation Cancellation | ||
|  |  * The new thread of execution is created with a cancellation state that | ||
|  |  * supports @c cancellation_type::terminal values only. To change the | ||
|  |  * cancellation state, call asio::this_coro::reset_cancellation_state. | ||
|  |  */ | ||
|  | template <typename Executor, typename T, typename AwaitableExecutor, | ||
|  |     ASIO_COMPLETION_TOKEN_FOR( | ||
|  |       void(std::exception_ptr, T)) CompletionToken | ||
|  |         ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(Executor)> | ||
|  | inline ASIO_INITFN_AUTO_RESULT_TYPE( | ||
|  |     CompletionToken, void(std::exception_ptr, T)) | ||
|  | co_spawn(const Executor& ex, awaitable<T, AwaitableExecutor> a, | ||
|  |     CompletionToken&& token | ||
|  |       ASIO_DEFAULT_COMPLETION_TOKEN(Executor), | ||
|  |     typename constraint< | ||
|  |       (is_executor<Executor>::value || execution::is_executor<Executor>::value) | ||
|  |         && is_convertible<Executor, AwaitableExecutor>::value | ||
|  |     >::type = 0); | ||
|  | 
 | ||
|  | /// Spawn a new coroutined-based thread of execution.
 | ||
|  | /**
 | ||
|  |  * @param ex The executor that will be used to schedule the new thread of | ||
|  |  * execution. | ||
|  |  * | ||
|  |  * @param a The asio::awaitable object that is the result of calling the | ||
|  |  * coroutine's entry point function. | ||
|  |  * | ||
|  |  * @param token The @ref completion_token that will handle the notification that | ||
|  |  * the thread of execution has completed. The function signature of the | ||
|  |  * completion handler must be: | ||
|  |  * @code void handler(std::exception_ptr); @endcode | ||
|  |  * | ||
|  |  * @par Completion Signature | ||
|  |  * @code void(std::exception_ptr) @endcode | ||
|  |  * | ||
|  |  * @par Example | ||
|  |  * @code | ||
|  |  * asio::awaitable<void> echo(tcp::socket socket) | ||
|  |  * { | ||
|  |  *   try | ||
|  |  *   { | ||
|  |  *     char data[1024]; | ||
|  |  *     for (;;) | ||
|  |  *     { | ||
|  |  *       std::size_t n = co_await socket.async_read_some( | ||
|  |  *           asio::buffer(data), asio::use_awaitable); | ||
|  |  * | ||
|  |  *       co_await asio::async_write(socket, | ||
|  |  *           asio::buffer(data, n), asio::use_awaitable); | ||
|  |  *     } | ||
|  |  *   } | ||
|  |  *   catch (const std::exception& e) | ||
|  |  *   { | ||
|  |  *     std::cerr << "Exception: " << e.what() << "\n"; | ||
|  |  *   } | ||
|  |  * } | ||
|  |  * | ||
|  |  * // ...
 | ||
|  |  * | ||
|  |  * asio::co_spawn(my_executor, | ||
|  |  *   echo(std::move(my_tcp_socket)), | ||
|  |  *   asio::detached); | ||
|  |  * @endcode | ||
|  |  * | ||
|  |  * @par Per-Operation Cancellation | ||
|  |  * The new thread of execution is created with a cancellation state that | ||
|  |  * supports @c cancellation_type::terminal values only. To change the | ||
|  |  * cancellation state, call asio::this_coro::reset_cancellation_state. | ||
|  |  */ | ||
|  | template <typename Executor, typename AwaitableExecutor, | ||
|  |     ASIO_COMPLETION_TOKEN_FOR( | ||
|  |       void(std::exception_ptr)) CompletionToken | ||
|  |         ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(Executor)> | ||
|  | inline ASIO_INITFN_AUTO_RESULT_TYPE( | ||
|  |     CompletionToken, void(std::exception_ptr)) | ||
|  | co_spawn(const Executor& ex, awaitable<void, AwaitableExecutor> a, | ||
|  |     CompletionToken&& token | ||
|  |       ASIO_DEFAULT_COMPLETION_TOKEN(Executor), | ||
|  |     typename constraint< | ||
|  |       (is_executor<Executor>::value || execution::is_executor<Executor>::value) | ||
|  |         && is_convertible<Executor, AwaitableExecutor>::value | ||
|  |     >::type = 0); | ||
|  | 
 | ||
|  | /// Spawn a new coroutined-based thread of execution.
 | ||
|  | /**
 | ||
|  |  * @param ctx An execution context that will provide the executor to be used to | ||
|  |  * schedule the new thread of execution. | ||
|  |  * | ||
|  |  * @param a The asio::awaitable object that is the result of calling the | ||
|  |  * coroutine's entry point function. | ||
|  |  * | ||
|  |  * @param token The @ref completion_token that will handle the notification that | ||
|  |  * the thread of execution has completed. The function signature of the | ||
|  |  * completion handler must be: | ||
|  |  * @code void handler(std::exception_ptr); @endcode | ||
|  |  * | ||
|  |  * @par Completion Signature | ||
|  |  * @code void(std::exception_ptr, T) @endcode | ||
|  |  * | ||
|  |  * @par Example | ||
|  |  * @code | ||
|  |  * asio::awaitable<std::size_t> echo(tcp::socket socket) | ||
|  |  * { | ||
|  |  *   std::size_t bytes_transferred = 0; | ||
|  |  * | ||
|  |  *   try | ||
|  |  *   { | ||
|  |  *     char data[1024]; | ||
|  |  *     for (;;) | ||
|  |  *     { | ||
|  |  *       std::size_t n = co_await socket.async_read_some( | ||
|  |  *           asio::buffer(data), asio::use_awaitable); | ||
|  |  * | ||
|  |  *       co_await asio::async_write(socket, | ||
|  |  *           asio::buffer(data, n), asio::use_awaitable); | ||
|  |  * | ||
|  |  *       bytes_transferred += n; | ||
|  |  *     } | ||
|  |  *   } | ||
|  |  *   catch (const std::exception&) | ||
|  |  *   { | ||
|  |  *   } | ||
|  |  * | ||
|  |  *   co_return bytes_transferred; | ||
|  |  * } | ||
|  |  * | ||
|  |  * // ...
 | ||
|  |  * | ||
|  |  * asio::co_spawn(my_io_context, | ||
|  |  *   echo(std::move(my_tcp_socket)), | ||
|  |  *   [](std::exception_ptr e, std::size_t n) | ||
|  |  *   { | ||
|  |  *     std::cout << "transferred " << n << "\n"; | ||
|  |  *   }); | ||
|  |  * @endcode | ||
|  |  * | ||
|  |  * @par Per-Operation Cancellation | ||
|  |  * The new thread of execution is created with a cancellation state that | ||
|  |  * supports @c cancellation_type::terminal values only. To change the | ||
|  |  * cancellation state, call asio::this_coro::reset_cancellation_state. | ||
|  |  */ | ||
|  | template <typename ExecutionContext, typename T, typename AwaitableExecutor, | ||
|  |     ASIO_COMPLETION_TOKEN_FOR( | ||
|  |       void(std::exception_ptr, T)) CompletionToken | ||
|  |         ASIO_DEFAULT_COMPLETION_TOKEN_TYPE( | ||
|  |           typename ExecutionContext::executor_type)> | ||
|  | inline ASIO_INITFN_AUTO_RESULT_TYPE( | ||
|  |     CompletionToken, void(std::exception_ptr, T)) | ||
|  | co_spawn(ExecutionContext& ctx, awaitable<T, AwaitableExecutor> a, | ||
|  |     CompletionToken&& token | ||
|  |       ASIO_DEFAULT_COMPLETION_TOKEN( | ||
|  |         typename ExecutionContext::executor_type), | ||
|  |     typename constraint< | ||
|  |       is_convertible<ExecutionContext&, execution_context&>::value | ||
|  |         && is_convertible<typename ExecutionContext::executor_type, | ||
|  |           AwaitableExecutor>::value | ||
|  |     >::type = 0); | ||
|  | 
 | ||
|  | /// Spawn a new coroutined-based thread of execution.
 | ||
|  | /**
 | ||
|  |  * @param ctx An execution context that will provide the executor to be used to | ||
|  |  * schedule the new thread of execution. | ||
|  |  * | ||
|  |  * @param a The asio::awaitable object that is the result of calling the | ||
|  |  * coroutine's entry point function. | ||
|  |  * | ||
|  |  * @param token The @ref completion_token that will handle the notification that | ||
|  |  * the thread of execution has completed. The function signature of the | ||
|  |  * completion handler must be: | ||
|  |  * @code void handler(std::exception_ptr); @endcode | ||
|  |  * | ||
|  |  * @par Completion Signature | ||
|  |  * @code void(std::exception_ptr) @endcode | ||
|  |  * | ||
|  |  * @par Example | ||
|  |  * @code | ||
|  |  * asio::awaitable<void> echo(tcp::socket socket) | ||
|  |  * { | ||
|  |  *   try | ||
|  |  *   { | ||
|  |  *     char data[1024]; | ||
|  |  *     for (;;) | ||
|  |  *     { | ||
|  |  *       std::size_t n = co_await socket.async_read_some( | ||
|  |  *           asio::buffer(data), asio::use_awaitable); | ||
|  |  * | ||
|  |  *       co_await asio::async_write(socket, | ||
|  |  *           asio::buffer(data, n), asio::use_awaitable); | ||
|  |  *     } | ||
|  |  *   } | ||
|  |  *   catch (const std::exception& e) | ||
|  |  *   { | ||
|  |  *     std::cerr << "Exception: " << e.what() << "\n"; | ||
|  |  *   } | ||
|  |  * } | ||
|  |  * | ||
|  |  * // ...
 | ||
|  |  * | ||
|  |  * asio::co_spawn(my_io_context, | ||
|  |  *   echo(std::move(my_tcp_socket)), | ||
|  |  *   asio::detached); | ||
|  |  * @endcode | ||
|  |  * | ||
|  |  * @par Per-Operation Cancellation | ||
|  |  * The new thread of execution is created with a cancellation state that | ||
|  |  * supports @c cancellation_type::terminal values only. To change the | ||
|  |  * cancellation state, call asio::this_coro::reset_cancellation_state. | ||
|  |  */ | ||
|  | template <typename ExecutionContext, typename AwaitableExecutor, | ||
|  |     ASIO_COMPLETION_TOKEN_FOR( | ||
|  |       void(std::exception_ptr)) CompletionToken | ||
|  |         ASIO_DEFAULT_COMPLETION_TOKEN_TYPE( | ||
|  |           typename ExecutionContext::executor_type)> | ||
|  | inline ASIO_INITFN_AUTO_RESULT_TYPE( | ||
|  |     CompletionToken, void(std::exception_ptr)) | ||
|  | co_spawn(ExecutionContext& ctx, awaitable<void, AwaitableExecutor> a, | ||
|  |     CompletionToken&& token | ||
|  |       ASIO_DEFAULT_COMPLETION_TOKEN( | ||
|  |         typename ExecutionContext::executor_type), | ||
|  |     typename constraint< | ||
|  |       is_convertible<ExecutionContext&, execution_context&>::value | ||
|  |         && is_convertible<typename ExecutionContext::executor_type, | ||
|  |           AwaitableExecutor>::value | ||
|  |     >::type = 0); | ||
|  | 
 | ||
|  | /// Spawn a new coroutined-based thread of execution.
 | ||
|  | /**
 | ||
|  |  * @param ex The executor that will be used to schedule the new thread of | ||
|  |  * execution. | ||
|  |  * | ||
|  |  * @param f A nullary function object with a return type of the form | ||
|  |  * @c asio::awaitable<R,E> that will be used as the coroutine's entry | ||
|  |  * point. | ||
|  |  * | ||
|  |  * @param token The @ref completion_token that will handle the notification | ||
|  |  * that the thread of execution has completed. If @c R is @c void, the function | ||
|  |  * signature of the completion handler must be: | ||
|  |  * | ||
|  |  * @code void handler(std::exception_ptr); @endcode | ||
|  |  * Otherwise, the function signature of the completion handler must be: | ||
|  |  * @code void handler(std::exception_ptr, R); @endcode | ||
|  |  * | ||
|  |  * @par Completion Signature | ||
|  |  * @code void(std::exception_ptr, R) @endcode | ||
|  |  * where @c R is the first template argument to the @c awaitable returned by the | ||
|  |  * supplied function object @c F: | ||
|  |  * @code asio::awaitable<R, AwaitableExecutor> F() @endcode | ||
|  |  * | ||
|  |  * @par Example | ||
|  |  * @code | ||
|  |  * asio::awaitable<std::size_t> echo(tcp::socket socket) | ||
|  |  * { | ||
|  |  *   std::size_t bytes_transferred = 0; | ||
|  |  * | ||
|  |  *   try | ||
|  |  *   { | ||
|  |  *     char data[1024]; | ||
|  |  *     for (;;) | ||
|  |  *     { | ||
|  |  *       std::size_t n = co_await socket.async_read_some( | ||
|  |  *           asio::buffer(data), asio::use_awaitable); | ||
|  |  * | ||
|  |  *       co_await asio::async_write(socket, | ||
|  |  *           asio::buffer(data, n), asio::use_awaitable); | ||
|  |  * | ||
|  |  *       bytes_transferred += n; | ||
|  |  *     } | ||
|  |  *   } | ||
|  |  *   catch (const std::exception&) | ||
|  |  *   { | ||
|  |  *   } | ||
|  |  * | ||
|  |  *   co_return bytes_transferred; | ||
|  |  * } | ||
|  |  * | ||
|  |  * // ...
 | ||
|  |  * | ||
|  |  * asio::co_spawn(my_executor, | ||
|  |  *   [socket = std::move(my_tcp_socket)]() mutable | ||
|  |  *     -> asio::awaitable<void> | ||
|  |  *   { | ||
|  |  *     try | ||
|  |  *     { | ||
|  |  *       char data[1024]; | ||
|  |  *       for (;;) | ||
|  |  *       { | ||
|  |  *         std::size_t n = co_await socket.async_read_some( | ||
|  |  *             asio::buffer(data), asio::use_awaitable); | ||
|  |  * | ||
|  |  *         co_await asio::async_write(socket, | ||
|  |  *             asio::buffer(data, n), asio::use_awaitable); | ||
|  |  *       } | ||
|  |  *     } | ||
|  |  *     catch (const std::exception& e) | ||
|  |  *     { | ||
|  |  *       std::cerr << "Exception: " << e.what() << "\n"; | ||
|  |  *     } | ||
|  |  *   }, asio::detached); | ||
|  |  * @endcode | ||
|  |  * | ||
|  |  * @par Per-Operation Cancellation | ||
|  |  * The new thread of execution is created with a cancellation state that | ||
|  |  * supports @c cancellation_type::terminal values only. To change the | ||
|  |  * cancellation state, call asio::this_coro::reset_cancellation_state. | ||
|  |  */ | ||
|  | template <typename Executor, typename F, | ||
|  |     ASIO_COMPLETION_TOKEN_FOR(typename detail::awaitable_signature< | ||
|  |       typename result_of<F()>::type>::type) CompletionToken | ||
|  |         ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(Executor)> | ||
|  | ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, | ||
|  |     typename detail::awaitable_signature<typename result_of<F()>::type>::type) | ||
|  | co_spawn(const Executor& ex, F&& f, | ||
|  |     CompletionToken&& token | ||
|  |       ASIO_DEFAULT_COMPLETION_TOKEN(Executor), | ||
|  |     typename constraint< | ||
|  |       is_executor<Executor>::value || execution::is_executor<Executor>::value | ||
|  |     >::type = 0); | ||
|  | 
 | ||
|  | /// Spawn a new coroutined-based thread of execution.
 | ||
|  | /**
 | ||
|  |  * @param ctx An execution context that will provide the executor to be used to | ||
|  |  * schedule the new thread of execution. | ||
|  |  * | ||
|  |  * @param f A nullary function object with a return type of the form | ||
|  |  * @c asio::awaitable<R,E> that will be used as the coroutine's entry | ||
|  |  * point. | ||
|  |  * | ||
|  |  * @param token The @ref completion_token that will handle the notification | ||
|  |  * that the thread of execution has completed. If @c R is @c void, the function | ||
|  |  * signature of the completion handler must be: | ||
|  |  * | ||
|  |  * @code void handler(std::exception_ptr); @endcode | ||
|  |  * Otherwise, the function signature of the completion handler must be: | ||
|  |  * @code void handler(std::exception_ptr, R); @endcode | ||
|  |  * | ||
|  |  * @par Completion Signature | ||
|  |  * @code void(std::exception_ptr, R) @endcode | ||
|  |  * where @c R is the first template argument to the @c awaitable returned by the | ||
|  |  * supplied function object @c F: | ||
|  |  * @code asio::awaitable<R, AwaitableExecutor> F() @endcode | ||
|  |  * | ||
|  |  * @par Example | ||
|  |  * @code | ||
|  |  * asio::awaitable<std::size_t> echo(tcp::socket socket) | ||
|  |  * { | ||
|  |  *   std::size_t bytes_transferred = 0; | ||
|  |  * | ||
|  |  *   try | ||
|  |  *   { | ||
|  |  *     char data[1024]; | ||
|  |  *     for (;;) | ||
|  |  *     { | ||
|  |  *       std::size_t n = co_await socket.async_read_some( | ||
|  |  *           asio::buffer(data), asio::use_awaitable); | ||
|  |  * | ||
|  |  *       co_await asio::async_write(socket, | ||
|  |  *           asio::buffer(data, n), asio::use_awaitable); | ||
|  |  * | ||
|  |  *       bytes_transferred += n; | ||
|  |  *     } | ||
|  |  *   } | ||
|  |  *   catch (const std::exception&) | ||
|  |  *   { | ||
|  |  *   } | ||
|  |  * | ||
|  |  *   co_return bytes_transferred; | ||
|  |  * } | ||
|  |  * | ||
|  |  * // ...
 | ||
|  |  * | ||
|  |  * asio::co_spawn(my_io_context, | ||
|  |  *   [socket = std::move(my_tcp_socket)]() mutable | ||
|  |  *     -> asio::awaitable<void> | ||
|  |  *   { | ||
|  |  *     try | ||
|  |  *     { | ||
|  |  *       char data[1024]; | ||
|  |  *       for (;;) | ||
|  |  *       { | ||
|  |  *         std::size_t n = co_await socket.async_read_some( | ||
|  |  *             asio::buffer(data), asio::use_awaitable); | ||
|  |  * | ||
|  |  *         co_await asio::async_write(socket, | ||
|  |  *             asio::buffer(data, n), asio::use_awaitable); | ||
|  |  *       } | ||
|  |  *     } | ||
|  |  *     catch (const std::exception& e) | ||
|  |  *     { | ||
|  |  *       std::cerr << "Exception: " << e.what() << "\n"; | ||
|  |  *     } | ||
|  |  *   }, asio::detached); | ||
|  |  * @endcode | ||
|  |  * | ||
|  |  * @par Per-Operation Cancellation | ||
|  |  * The new thread of execution is created with a cancellation state that | ||
|  |  * supports @c cancellation_type::terminal values only. To change the | ||
|  |  * cancellation state, call asio::this_coro::reset_cancellation_state. | ||
|  |  */ | ||
|  | template <typename ExecutionContext, typename F, | ||
|  |     ASIO_COMPLETION_TOKEN_FOR(typename detail::awaitable_signature< | ||
|  |       typename result_of<F()>::type>::type) CompletionToken | ||
|  |         ASIO_DEFAULT_COMPLETION_TOKEN_TYPE( | ||
|  |           typename ExecutionContext::executor_type)> | ||
|  | ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, | ||
|  |     typename detail::awaitable_signature<typename result_of<F()>::type>::type) | ||
|  | co_spawn(ExecutionContext& ctx, F&& f, | ||
|  |     CompletionToken&& token | ||
|  |       ASIO_DEFAULT_COMPLETION_TOKEN( | ||
|  |         typename ExecutionContext::executor_type), | ||
|  |     typename constraint< | ||
|  |       is_convertible<ExecutionContext&, execution_context&>::value | ||
|  |     >::type = 0); | ||
|  | 
 | ||
|  | } // namespace asio
 | ||
|  | 
 | ||
|  | #include "asio/detail/pop_options.hpp"
 | ||
|  | 
 | ||
|  | #include "asio/impl/co_spawn.hpp"
 | ||
|  | 
 | ||
|  | #endif // defined(ASIO_HAS_CO_AWAIT) || defined(GENERATING_DOCUMENTATION)
 | ||
|  | 
 | ||
|  | #endif // ASIO_CO_SPAWN_HPP
 |