From 490743a662842805d795099b7f155ad4a970c320 Mon Sep 17 00:00:00 2001 From: Christopher Kohlhoff Date: Tue, 26 Feb 2019 20:06:39 +1100 Subject: [PATCH] Promote coroutines TS support classes to asio namespace. The awaitable<>, co_spawn(), this_coro, detached, and redirect_error facilities have been moved from the asio::experimental namespace to namespace asio. As part of this change, the this_coro::token() awaitable has been superseded by the asio::use_awaitable completion token. Please note that the use_awaitable and redirect_error completion tokens work only with asynchronous operations that use the new form of async_result with member function initiate(). Furthermore, when using use_awaitable, please be aware that the asynchronous operation is not initiated until co_await is applied to the awaitable<>. --- asio/include/Makefile.am | 18 +- asio/include/asio.hpp | 6 + asio/include/asio/awaitable.hpp | 123 +++ asio/include/asio/co_spawn.hpp | 88 ++ .../asio/{experimental => }/detached.hpp | 21 +- asio/include/asio/detail/thread_info_base.hpp | 2 +- asio/include/asio/experimental.hpp | 22 - asio/include/asio/experimental/co_spawn.hpp | 226 ----- .../asio/experimental/impl/co_spawn.hpp | 843 ------------------ .../asio/experimental/impl/detached.hpp | 90 -- asio/include/asio/impl/awaitable.hpp | 418 +++++++++ asio/include/asio/impl/co_spawn.hpp | 138 +++ asio/include/asio/impl/detached.hpp | 130 +++ .../impl/redirect_error.hpp | 153 +++- asio/include/asio/impl/use_awaitable.hpp | 272 ++++++ .../{experimental => }/redirect_error.hpp | 16 +- asio/include/asio/this_coro.hpp | 45 + asio/include/asio/use_awaitable.hpp | 71 ++ asio/src/doc/overview.qbk | 4 +- asio/src/doc/overview/coroutines_ts.qbk | 62 +- asio/src/doc/quickref.xml | 38 +- asio/src/examples/cpp17/Makefile.am | 1 - .../cpp17/coroutines_ts/chat_server.cpp | 30 +- .../double_buffered_echo_server.cpp | 97 -- .../cpp17/coroutines_ts/echo_server.cpp | 28 +- .../cpp17/coroutines_ts/range_based_for.cpp | 22 +- .../coroutines_ts/refactored_echo_server.cpp | 28 +- asio/src/tests/Makefile.am | 18 + asio/src/tests/unit/.gitignore | 6 + asio/src/tests/unit/awaitable.cpp | 25 + asio/src/tests/unit/co_spawn.cpp | 25 + asio/src/tests/unit/detached.cpp | 25 + asio/src/tests/unit/redirect_error.cpp | 25 + asio/src/tests/unit/this_coro.cpp | 25 + asio/src/tests/unit/use_awaitable.cpp | 25 + 35 files changed, 1703 insertions(+), 1463 deletions(-) create mode 100644 asio/include/asio/awaitable.hpp create mode 100644 asio/include/asio/co_spawn.hpp rename asio/include/asio/{experimental => }/detached.hpp (71%) delete mode 100644 asio/include/asio/experimental.hpp delete mode 100644 asio/include/asio/experimental/co_spawn.hpp delete mode 100644 asio/include/asio/experimental/impl/co_spawn.hpp delete mode 100644 asio/include/asio/experimental/impl/detached.hpp create mode 100644 asio/include/asio/impl/awaitable.hpp create mode 100644 asio/include/asio/impl/co_spawn.hpp create mode 100644 asio/include/asio/impl/detached.hpp rename asio/include/asio/{experimental => }/impl/redirect_error.hpp (58%) create mode 100644 asio/include/asio/impl/use_awaitable.hpp rename asio/include/asio/{experimental => }/redirect_error.hpp (80%) create mode 100644 asio/include/asio/this_coro.hpp create mode 100644 asio/include/asio/use_awaitable.hpp delete mode 100644 asio/src/examples/cpp17/coroutines_ts/double_buffered_echo_server.cpp create mode 100644 asio/src/tests/unit/awaitable.cpp create mode 100644 asio/src/tests/unit/co_spawn.cpp create mode 100644 asio/src/tests/unit/detached.cpp create mode 100644 asio/src/tests/unit/redirect_error.cpp create mode 100644 asio/src/tests/unit/this_coro.cpp create mode 100644 asio/src/tests/unit/use_awaitable.cpp diff --git a/asio/include/Makefile.am b/asio/include/Makefile.am index 49907032..be56dfa7 100644 --- a/asio/include/Makefile.am +++ b/asio/include/Makefile.am @@ -3,6 +3,7 @@ nobase_include_HEADERS = \ asio/associated_allocator.hpp \ asio/associated_executor.hpp \ asio/async_result.hpp \ + asio/awaitable.hpp \ asio/basic_datagram_socket.hpp \ asio/basic_deadline_timer.hpp \ asio/basic_io_object.hpp \ @@ -27,11 +28,13 @@ nobase_include_HEADERS = \ asio/buffered_write_stream.hpp \ asio/buffer.hpp \ asio/buffers_iterator.hpp \ + asio/co_spawn.hpp \ asio/completion_condition.hpp \ asio/connect.hpp \ asio/coroutine.hpp \ asio/deadline_timer.hpp \ asio/defer.hpp \ + asio/detached.hpp \ asio/detail/array_fwd.hpp \ asio/detail/array.hpp \ asio/detail/assert.hpp \ @@ -289,13 +292,6 @@ nobase_include_HEADERS = \ asio/execution_context.hpp \ asio/executor.hpp \ asio/executor_work_guard.hpp \ - asio/experimental/co_spawn.hpp \ - asio/experimental/detached.hpp \ - asio/experimental.hpp \ - asio/experimental/impl/co_spawn.hpp \ - asio/experimental/impl/detached.hpp \ - asio/experimental/impl/redirect_error.hpp \ - asio/experimental/redirect_error.hpp \ asio/generic/basic_endpoint.hpp \ asio/generic/datagram_protocol.hpp \ asio/generic/detail/endpoint.hpp \ @@ -308,10 +304,13 @@ nobase_include_HEADERS = \ asio/handler_invoke_hook.hpp \ asio/high_resolution_timer.hpp \ asio.hpp \ + asio/impl/awaitable.hpp \ asio/impl/buffered_read_stream.hpp \ asio/impl/buffered_write_stream.hpp \ + asio/impl/co_spawn.hpp \ asio/impl/connect.hpp \ asio/impl/defer.hpp \ + asio/impl/detached.hpp \ asio/impl/dispatch.hpp \ asio/impl/error_code.ipp \ asio/impl/error.ipp \ @@ -326,6 +325,7 @@ nobase_include_HEADERS = \ asio/impl/read_at.hpp \ asio/impl/read.hpp \ asio/impl/read_until.hpp \ + asio/impl/redirect_error.hpp \ asio/impl/serial_port_base.hpp \ asio/impl/serial_port_base.ipp \ asio/impl/spawn.hpp \ @@ -336,6 +336,7 @@ nobase_include_HEADERS = \ asio/impl/system_executor.hpp \ asio/impl/thread_pool.hpp \ asio/impl/thread_pool.ipp \ + asio/impl/use_awaitable.hpp \ asio/impl/use_future.hpp \ asio/impl/write_at.hpp \ asio/impl/write.hpp \ @@ -403,6 +404,7 @@ nobase_include_HEADERS = \ asio/read_at.hpp \ asio/read.hpp \ asio/read_until.hpp \ + asio/redirect_error.hpp \ asio/serial_port_base.hpp \ asio/serial_port.hpp \ asio/signal_set.hpp \ @@ -443,6 +445,7 @@ nobase_include_HEADERS = \ asio/system_error.hpp \ asio/system_executor.hpp \ asio/system_timer.hpp \ + asio/this_coro.hpp \ asio/thread.hpp \ asio/thread_pool.hpp \ asio/time_traits.hpp \ @@ -455,6 +458,7 @@ nobase_include_HEADERS = \ asio/ts/socket.hpp \ asio/ts/timer.hpp \ asio/unyield.hpp \ + asio/use_awaitable.hpp \ asio/use_future.hpp \ asio/uses_executor.hpp \ asio/version.hpp \ diff --git a/asio/include/asio.hpp b/asio/include/asio.hpp index be1e8a7d..5fe2814b 100644 --- a/asio/include/asio.hpp +++ b/asio/include/asio.hpp @@ -18,6 +18,7 @@ #include "asio/associated_allocator.hpp" #include "asio/associated_executor.hpp" #include "asio/async_result.hpp" +#include "asio/awaitable.hpp" #include "asio/basic_datagram_socket.hpp" #include "asio/basic_deadline_timer.hpp" #include "asio/basic_io_object.hpp" @@ -41,11 +42,13 @@ #include "asio/buffered_write_stream_fwd.hpp" #include "asio/buffered_write_stream.hpp" #include "asio/buffers_iterator.hpp" +#include "asio/co_spawn.hpp" #include "asio/completion_condition.hpp" #include "asio/connect.hpp" #include "asio/coroutine.hpp" #include "asio/deadline_timer.hpp" #include "asio/defer.hpp" +#include "asio/detached.hpp" #include "asio/dispatch.hpp" #include "asio/error.hpp" #include "asio/error_code.hpp" @@ -105,6 +108,7 @@ #include "asio/read.hpp" #include "asio/read_at.hpp" #include "asio/read_until.hpp" +#include "asio/redirect_error.hpp" #include "asio/serial_port.hpp" #include "asio/serial_port_base.hpp" #include "asio/signal_set.hpp" @@ -116,9 +120,11 @@ #include "asio/system_error.hpp" #include "asio/system_executor.hpp" #include "asio/system_timer.hpp" +#include "asio/this_coro.hpp" #include "asio/thread.hpp" #include "asio/thread_pool.hpp" #include "asio/time_traits.hpp" +#include "asio/use_awaitable.hpp" #include "asio/use_future.hpp" #include "asio/uses_executor.hpp" #include "asio/version.hpp" diff --git a/asio/include/asio/awaitable.hpp b/asio/include/asio/awaitable.hpp new file mode 100644 index 00000000..890fb67e --- /dev/null +++ b/asio/include/asio/awaitable.hpp @@ -0,0 +1,123 @@ +// +// awaitable.hpp +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2019 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_AWAITABLE_HPP +#define ASIO_AWAITABLE_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 +#include "asio/executor.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +using std::experimental::coroutine_handle; +using std::experimental::suspend_always; + +template class awaitable_thread; +template class awaitable_frame; + +} // namespace detail + +/// The return type of a coroutine or asynchronous operation. +template +class awaitable +{ +public: + /// The type of the awaited value. + typedef T value_type; + + /// The executor type that will be used for the coroutine. + typedef Executor executor_type; + + /// Default constructor. + constexpr awaitable() noexcept + : frame_(nullptr) + { + } + + /// Move constructor. + awaitable(awaitable&& other) noexcept + : frame_(std::exchange(other.frame_, nullptr)) + { + } + + /// Destructor + ~awaitable() + { + if (frame_) + frame_->destroy(); + } + + /// Checks if the awaitable refers to a future result. + bool valid() const noexcept + { + return !!frame_; + } + +#if !defined(GENERATING_DOCUMENTATION) + + // Support for co_await keyword. + bool await_ready() const noexcept + { + return false; + } + + // Support for co_await keyword. + template + void await_suspend( + detail::coroutine_handle> h) + { + frame_->push_frame(&h.promise()); + } + + // Support for co_await keyword. + T await_resume() + { + return frame_->get(); + } + +#endif // !defined(GENERATING_DOCUMENTATION) + +private: + template friend class detail::awaitable_thread; + template friend class detail::awaitable_frame; + + // Not copy constructible or copy assignable. + awaitable(const awaitable&) = delete; + awaitable& operator=(const awaitable&) = delete; + + // Construct the awaitable from a coroutine's frame object. + explicit awaitable(detail::awaitable_frame* a) + : frame_(a) + { + } + + detail::awaitable_frame* frame_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/impl/awaitable.hpp" + +#endif // defined(ASIO_HAS_CO_AWAIT) || defined(GENERATING_DOCUMENTATION) + +#endif // ASIO_AWAITABLE_HPP diff --git a/asio/include/asio/co_spawn.hpp b/asio/include/asio/co_spawn.hpp new file mode 100644 index 00000000..68781577 --- /dev/null +++ b/asio/include/asio/co_spawn.hpp @@ -0,0 +1,88 @@ +// +// co_spawn.hpp +// ~~~~~~~~~~~~ +// +// Copyright (c) 2003-2019 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_context.hpp" +#include "asio/is_executor.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +struct awaitable_signature; + +template +struct awaitable_signature> +{ + typedef void type(std::exception_ptr, T); +}; + +template +struct awaitable_signature> +{ + typedef void type(std::exception_ptr); +}; + +} // namespace detail + +/// Spawn a new thread of execution. +/** + * The entry point function object @c f must have the signature: + * + * @code awaitable f(); @endcode + * + * where @c E is convertible from @c Executor. + */ +template +ASIO_INITFN_RESULT_TYPE(CompletionToken, + typename detail::awaitable_signature::type>::type) +co_spawn(const Executor& ex, F&& f, CompletionToken&& token, + typename enable_if< + is_executor::value + >::type* = 0); + +/// Spawn a new thread of execution. +/** + * The entry point function object @c f must have the signature: + * + * @code awaitable f(); @endcode + * + * where @c E is convertible from @c ExecutionContext::executor_type. + */ +template +ASIO_INITFN_RESULT_TYPE(CompletionToken, + typename detail::awaitable_signature::type>::type) +co_spawn(ExecutionContext& ctx, F&& f, CompletionToken&& token, + typename enable_if< + is_convertible::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 diff --git a/asio/include/asio/experimental/detached.hpp b/asio/include/asio/detached.hpp similarity index 71% rename from asio/include/asio/experimental/detached.hpp rename to asio/include/asio/detached.hpp index da57d99a..332ed24f 100644 --- a/asio/include/asio/experimental/detached.hpp +++ b/asio/include/asio/detached.hpp @@ -1,6 +1,6 @@ // -// experimental/detached.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// detached.hpp +// ~~~~~~~~~~~~ // // Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com) // @@ -8,8 +8,8 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef ASIO_EXPERIMENTAL_DETACHED_HPP -#define ASIO_EXPERIMENTAL_DETACHED_HPP +#ifndef ASIO_DETACHED_HPP +#define ASIO_DETACHED_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once @@ -21,7 +21,6 @@ #include "asio/detail/push_options.hpp" namespace asio { -namespace experimental { /// Class used to specify that an asynchronous operation is detached. /** @@ -30,9 +29,9 @@ namespace experimental { * detached. That is, there is no completion handler waiting for the * operation's result. A detached_t object may be passed as a handler to an * asynchronous operation, typically using the special value - * @c asio::experimental::detached. For example: + * @c asio::detached. For example: - * @code my_socket.async_send(my_buffer, asio::experimental::detached); + * @code my_socket.async_send(my_buffer, asio::detached); * @endcode */ class detached_t @@ -46,8 +45,7 @@ public: /// A special value, similar to std::nothrow. /** - * See the documentation for asio::experimental::detached_t for a usage - * example. + * See the documentation for asio::detached_t for a usage example. */ #if defined(ASIO_HAS_CONSTEXPR) || defined(GENERATING_DOCUMENTATION) constexpr detached_t detached; @@ -55,11 +53,10 @@ constexpr detached_t detached; __declspec(selectany) detached_t detached; #endif -} // namespace experimental } // namespace asio #include "asio/detail/pop_options.hpp" -#include "asio/experimental/impl/detached.hpp" +#include "asio/impl/detached.hpp" -#endif // ASIO_EXPERIMENTAL_DETACHED_HPP +#endif // ASIO_DETACHED_HPP diff --git a/asio/include/asio/detail/thread_info_base.hpp b/asio/include/asio/detail/thread_info_base.hpp index 72942008..8bc809b6 100644 --- a/asio/include/asio/detail/thread_info_base.hpp +++ b/asio/include/asio/detail/thread_info_base.hpp @@ -33,7 +33,7 @@ public: enum { mem_index = 0 }; }; - struct awaitee_tag + struct awaitable_frame_tag { enum { mem_index = 1 }; }; diff --git a/asio/include/asio/experimental.hpp b/asio/include/asio/experimental.hpp deleted file mode 100644 index bbe860ac..00000000 --- a/asio/include/asio/experimental.hpp +++ /dev/null @@ -1,22 +0,0 @@ -// -// experimental.hpp -// ~~~~~~~~~~~~~~~~ -// -// Copyright (c) 2003-2019 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_EXPERIMENTAL_HPP -#define ASIO_EXPERIMENTAL_HPP - -#if defined(_MSC_VER) && (_MSC_VER >= 1200) -# pragma once -#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) - -#include "asio/experimental/co_spawn.hpp" -#include "asio/experimental/detached.hpp" -#include "asio/experimental/redirect_error.hpp" - -#endif // ASIO_EXPERIMENTAL_HPP diff --git a/asio/include/asio/experimental/co_spawn.hpp b/asio/include/asio/experimental/co_spawn.hpp deleted file mode 100644 index 3cfd722e..00000000 --- a/asio/include/asio/experimental/co_spawn.hpp +++ /dev/null @@ -1,226 +0,0 @@ -// -// experimental/co_spawn.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~~~ -// -// Copyright (c) 2003-2019 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_EXPERIMENTAL_CO_SPAWN_HPP -#define ASIO_EXPERIMENTAL_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 -#include "asio/executor.hpp" -#include "asio/strand.hpp" - -#include "asio/detail/push_options.hpp" - -namespace asio { -namespace experimental { -namespace detail { - -using std::experimental::coroutine_handle; - -template class awaiter; -template class awaitee_base; -template class awaitee; -template class await_handler_base; -template -auto co_spawn(const Executor& ex, F&& f, CompletionToken&& token); - -} // namespace detail - -namespace this_coro { - -/// Awaitable type that returns a completion token for the current coroutine. -struct token_t {}; - -/// Awaitable object that returns a completion token for the current coroutine. -constexpr inline token_t token() { return {}; } - -/// Awaitable type that returns the executor of the current coroutine. -struct executor_t {}; - -/// Awaitable object that returns the executor of the current coroutine. -constexpr inline executor_t executor() { return {}; } - -} // namespace this_coro - -/// A completion token that represents the currently executing coroutine. -/** - * The await_token class is used to represent the currently executing - * coroutine. An await_token may be passed as a handler to an asynchronous - * operation. For example: - * - * @code awaitable my_coroutine() - * { - * await_token token = co_await this_coro::token(); - * ... - * std::size_t n = co_await my_socket.async_read_some(buffer, token); - * ... - * } @endcode - * - * The initiating function (async_read_some in the above example) suspends the - * current coroutine. The coroutine is resumed when the asynchronous operation - * completes, and the result of the operation is returned. - */ -template -class await_token -{ -public: - /// The associated executor type. - typedef Executor executor_type; - - /// Copy constructor. - await_token(const await_token& other) noexcept - : awaiter_(other.awaiter_) - { - } - - /// Move constructor. - await_token(await_token&& other) noexcept - : awaiter_(std::exchange(other.awaiter_, nullptr)) - { - } - - /// Get the associated executor. - executor_type get_executor() const noexcept - { - return awaiter_->get_executor(); - } - -private: - // No assignment allowed. - await_token& operator=(const await_token&) = delete; - - template friend class detail::awaitee_base; - template friend class detail::await_handler_base; - - // Private constructor used by awaitee_base. - explicit await_token(detail::awaiter* a) - : awaiter_(a) - { - } - - detail::awaiter* awaiter_; -}; - -/// The return type of a coroutine or asynchronous operation. -template > -class awaitable -{ -public: - /// The type of the awaited value. - typedef T value_type; - - /// The executor type that will be used for the coroutine. - typedef Executor executor_type; - - /// Move constructor. - awaitable(awaitable&& other) noexcept - : awaitee_(std::exchange(other.awaitee_, nullptr)) - { - } - - /// Destructor - ~awaitable() - { - if (awaitee_) - { - detail::coroutine_handle< - detail::awaitee>::from_promise( - *awaitee_).destroy(); - } - } - -#if !defined(GENERATING_DOCUMENTATION) - - // Support for co_await keyword. - bool await_ready() const noexcept - { - return awaitee_->ready(); - } - - // Support for co_await keyword. - void await_suspend(detail::coroutine_handle> h) - { - awaitee_->attach_caller(h); - } - - // Support for co_await keyword. - template - void await_suspend(detail::coroutine_handle> h) - { - awaitee_->attach_caller(h); - } - - // Support for co_await keyword. - T await_resume() - { - return awaitee_->get(); - } - -#endif // !defined(GENERATING_DOCUMENTATION) - -private: - template friend class detail::awaitee; - template friend class detail::await_handler_base; - - // Not copy constructible or copy assignable. - awaitable(const awaitable&) = delete; - awaitable& operator=(const awaitable&) = delete; - - // Construct the awaitable from a coroutine's promise object. - explicit awaitable(detail::awaitee* a) : awaitee_(a) {} - - detail::awaitee* awaitee_; -}; - -/// Spawn a new thread of execution. -template ::value>::type> -inline auto co_spawn(const Executor& ex, F&& f, CompletionToken&& token) -{ - return detail::co_spawn(ex, std::forward(f), - std::forward(token)); -} - -/// Spawn a new thread of execution. -template ::value>::type> -inline auto co_spawn(ExecutionContext& ctx, F&& f, CompletionToken&& token) -{ - return detail::co_spawn(ctx.get_executor(), std::forward(f), - std::forward(token)); -} - -/// Spawn a new thread of execution. -template -inline auto co_spawn(const await_token& parent, - F&& f, CompletionToken&& token) -{ - return detail::co_spawn(parent.get_executor(), std::forward(f), - std::forward(token)); -} - -} // namespace experimental -} // namespace asio - -#include "asio/detail/pop_options.hpp" - -#include "asio/experimental/impl/co_spawn.hpp" - -#endif // defined(ASIO_HAS_CO_AWAIT) || defined(GENERATING_DOCUMENTATION) - -#endif // ASIO_EXPERIMENTAL_CO_SPAWN_HPP diff --git a/asio/include/asio/experimental/impl/co_spawn.hpp b/asio/include/asio/experimental/impl/co_spawn.hpp deleted file mode 100644 index 8d51b18b..00000000 --- a/asio/include/asio/experimental/impl/co_spawn.hpp +++ /dev/null @@ -1,843 +0,0 @@ -// -// experimental/impl/co_spawn.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// -// Copyright (c) 2003-2019 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_EXPERIMENTAL_IMPL_CO_SPAWN_HPP -#define ASIO_EXPERIMENTAL_IMPL_CO_SPAWN_HPP - -#if defined(_MSC_VER) && (_MSC_VER >= 1200) -# pragma once -#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) - -#include "asio/detail/config.hpp" -#include -#include -#include -#include -#include -#include -#include "asio/async_result.hpp" -#include "asio/detail/thread_context.hpp" -#include "asio/detail/thread_info_base.hpp" -#include "asio/detail/type_traits.hpp" -#include "asio/dispatch.hpp" -#include "asio/post.hpp" - -#include "asio/detail/push_options.hpp" - -namespace asio { -namespace experimental { -namespace detail { - -// Promise object for coroutine at top of thread-of-execution "stack". -template -class awaiter -{ -public: - struct deleter - { - void operator()(awaiter* a) - { - if (a) - a->release(); - } - }; - - typedef std::unique_ptr ptr; - - typedef Executor executor_type; - - ~awaiter() - { - if (has_executor_) - static_cast(static_cast(executor_))->~Executor(); - } - - void set_executor(const Executor& ex) - { - new (&executor_) Executor(ex); - has_executor_ = true; - } - - executor_type get_executor() const noexcept - { - return *static_cast(static_cast(executor_)); - } - - awaiter* get_return_object() - { - return this; - } - - auto initial_suspend() - { - return std::experimental::suspend_always(); - } - - auto final_suspend() - { - return std::experimental::suspend_always(); - } - - void return_void() - { - } - - awaiter* add_ref() - { - ++ref_count_; - return this; - } - - void release() - { - if (--ref_count_ == 0) - coroutine_handle::from_promise(*this).destroy(); - } - - void unhandled_exception() - { - pending_exception_ = std::current_exception(); - } - - void rethrow_unhandled_exception() - { - if (pending_exception_) - { - std::exception_ptr ex = std::exchange(pending_exception_, nullptr); - std::rethrow_exception(ex); - } - } - -private: - std::size_t ref_count_ = 0; - std::exception_ptr pending_exception_ = nullptr; - alignas(Executor) unsigned char executor_[sizeof(Executor)]; - bool has_executor_ = false; -}; - -// Base promise for coroutines further down the thread-of-execution "stack". -template -class awaitee_base -{ -public: -#if !defined(ASIO_DISABLE_AWAITEE_RECYCLING) - void* operator new(std::size_t size) - { - return asio::detail::thread_info_base::allocate( - asio::detail::thread_info_base::awaitee_tag(), - asio::detail::thread_context::thread_call_stack::top(), - size); - } - - void operator delete(void* pointer, std::size_t size) - { - asio::detail::thread_info_base::deallocate( - asio::detail::thread_info_base::awaitee_tag(), - asio::detail::thread_context::thread_call_stack::top(), - pointer, size); - } -#endif // !defined(ASIO_DISABLE_AWAITEE_RECYCLING) - - auto initial_suspend() - { - return std::experimental::suspend_never(); - } - - struct final_suspender - { - awaitee_base* this_; - - bool await_ready() const noexcept - { - return false; - } - - void await_suspend(coroutine_handle) - { - this_->wake_caller(); - } - - void await_resume() const noexcept - { - } - }; - - auto final_suspend() - { - return final_suspender{this}; - } - - void set_except(std::exception_ptr e) - { - pending_exception_ = e; - } - - 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); - } - } - - awaiter* top() - { - return awaiter_; - } - - coroutine_handle caller() - { - return caller_; - } - - bool ready() const - { - return ready_; - } - - void wake_caller() - { - if (caller_) - caller_.resume(); - else - ready_ = true; - } - - class awaitable_executor - { - public: - explicit awaitable_executor(awaitee_base* a) - : this_(a) - { - } - - bool await_ready() const noexcept - { - return this_->awaiter_ != nullptr; - } - - template - void await_suspend(coroutine_handle> h) noexcept - { - this_->resume_on_attach_ = h; - } - - Executor await_resume() - { - return this_->awaiter_->get_executor(); - } - - private: - awaitee_base* this_; - }; - - awaitable_executor await_transform(this_coro::executor_t) noexcept - { - return awaitable_executor(this); - } - - class awaitable_token - { - public: - explicit awaitable_token(awaitee_base* a) - : this_(a) - { - } - - bool await_ready() const noexcept - { - return this_->awaiter_ != nullptr; - } - - template - void await_suspend(coroutine_handle> h) noexcept - { - this_->resume_on_attach_ = h; - } - - await_token await_resume() - { - return await_token(this_->awaiter_); - } - - private: - awaitee_base* this_; - }; - - awaitable_token await_transform(this_coro::token_t) noexcept - { - return awaitable_token(this); - } - - template - awaitable await_transform(awaitable& t) const - { - return std::move(t); - } - - template - awaitable await_transform(awaitable&& t) const - { - return std::move(t); - } - - std::experimental::suspend_always await_transform( - std::experimental::suspend_always) const - { - return std::experimental::suspend_always(); - } - - void attach_caller(coroutine_handle> h) - { - this->caller_ = h; - this->attach_callees(&h.promise()); - } - - template - void attach_caller(coroutine_handle> h) - { - this->caller_ = h; - if (h.promise().awaiter_) - this->attach_callees(h.promise().awaiter_); - else - h.promise().unattached_callee_ = this; - } - - void attach_callees(awaiter* a) - { - for (awaitee_base* curr = this; curr != nullptr; - curr = std::exchange(curr->unattached_callee_, nullptr)) - { - curr->awaiter_ = a; - if (curr->resume_on_attach_) - return std::exchange(curr->resume_on_attach_, nullptr).resume(); - } - } - -protected: - awaiter* awaiter_ = nullptr; - coroutine_handle caller_ = nullptr; - awaitee_base* unattached_callee_ = nullptr; - std::exception_ptr pending_exception_ = nullptr; - coroutine_handle resume_on_attach_ = nullptr; - bool ready_ = false; -}; - -// Promise object for coroutines further down the thread-of-execution "stack". -template -class awaitee - : public awaitee_base -{ -public: - awaitee() - { - } - - awaitee(awaitee&& other) noexcept - : awaitee_base(std::move(other)) - { - } - - ~awaitee() - { - if (has_result_) - static_cast(static_cast(result_))->~T(); - } - - awaitable get_return_object() - { - return awaitable(this); - }; - - template - void return_value(U&& u) - { - new (&result_) T(std::forward(u)); - has_result_ = true; - } - - T get() - { - this->caller_ = nullptr; - this->rethrow_exception(); - return std::move(*static_cast(static_cast(result_))); - } - -private: - alignas(T) unsigned char result_[sizeof(T)]; - bool has_result_ = false; -}; - -// Promise object for coroutines further down the thread-of-execution "stack". -template -class awaitee - : public awaitee_base -{ -public: - awaitable get_return_object() - { - return awaitable(this); - }; - - void return_void() - { - } - - void get() - { - this->caller_ = nullptr; - this->rethrow_exception(); - } -}; - -template -class awaiter_task -{ -public: - typedef Executor executor_type; - - awaiter_task(awaiter* a) - : awaiter_(a->add_ref()) - { - } - - awaiter_task(awaiter_task&& other) noexcept - : awaiter_(std::exchange(other.awaiter_, nullptr)) - { - } - - ~awaiter_task() - { - if (awaiter_) - { - // Coroutine "stack unwinding" must be performed through the executor. - executor_type ex(awaiter_->get_executor()); - (post)(ex, - [a = std::move(awaiter_)]() mutable - { - typename awaiter::ptr(std::move(a)); - }); - } - } - - executor_type get_executor() const noexcept - { - return awaiter_->get_executor(); - } - -protected: - typename awaiter::ptr awaiter_; -}; - -template -class co_spawn_handler : public awaiter_task -{ -public: - using awaiter_task::awaiter_task; - - void operator()() - { - typename awaiter::ptr ptr(std::move(this->awaiter_)); - coroutine_handle>::from_promise(*ptr.get()).resume(); - } -}; - -template -class await_handler_base : public awaiter_task -{ -public: - typedef awaitable awaitable_type; - - await_handler_base(await_token token) - : awaiter_task(token.awaiter_), - awaitee_(nullptr) - { - } - - await_handler_base(await_handler_base&& other) noexcept - : awaiter_task(std::move(other)), - awaitee_(std::exchange(other.awaitee_, nullptr)) - { - } - - void attach_awaitee(const awaitable& a) - { - awaitee_ = a.awaitee_; - } - -protected: - awaitee* awaitee_; -}; - -template class await_handler; - -template -class await_handler - : public await_handler_base -{ -public: - using await_handler_base::await_handler_base; - - void operator()() - { - typename awaiter::ptr ptr(std::move(this->awaiter_)); - this->awaitee_->return_void(); - this->awaitee_->wake_caller(); - ptr->rethrow_unhandled_exception(); - } -}; - -template -class await_handler - : public await_handler_base -{ -public: - typedef void return_type; - - using await_handler_base::await_handler_base; - - void operator()(const asio::error_code& ec) - { - typename awaiter::ptr ptr(std::move(this->awaiter_)); - if (ec) - { - this->awaitee_->set_except( - std::make_exception_ptr(asio::system_error(ec))); - } - else - this->awaitee_->return_void(); - this->awaitee_->wake_caller(); - ptr->rethrow_unhandled_exception(); - } -}; - -template -class await_handler - : public await_handler_base -{ -public: - using await_handler_base::await_handler_base; - - void operator()(std::exception_ptr ex) - { - typename awaiter::ptr ptr(std::move(this->awaiter_)); - if (ex) - this->awaitee_->set_except(ex); - else - this->awaitee_->return_void(); - this->awaitee_->wake_caller(); - ptr->rethrow_unhandled_exception(); - } -}; - -template -class await_handler - : public await_handler_base -{ -public: - using await_handler_base::await_handler_base; - - template - void operator()(Arg&& arg) - { - typename awaiter::ptr ptr(std::move(this->awaiter_)); - this->awaitee_->return_value(std::forward(arg)); - this->awaitee_->wake_caller(); - ptr->rethrow_unhandled_exception(); - } -}; - -template -class await_handler - : public await_handler_base -{ -public: - using await_handler_base::await_handler_base; - - template - void operator()(const asio::error_code& ec, Arg&& arg) - { - typename awaiter::ptr ptr(std::move(this->awaiter_)); - if (ec) - { - this->awaitee_->set_except( - std::make_exception_ptr(asio::system_error(ec))); - } - else - this->awaitee_->return_value(std::forward(arg)); - this->awaitee_->wake_caller(); - ptr->rethrow_unhandled_exception(); - } -}; - -template -class await_handler - : public await_handler_base -{ -public: - using await_handler_base::await_handler_base; - - template - void operator()(std::exception_ptr ex, Arg&& arg) - { - typename awaiter::ptr ptr(std::move(this->awaiter_)); - if (ex) - this->awaitee_->set_except(ex); - else - this->awaitee_->return_value(std::forward(arg)); - this->awaitee_->wake_caller(); - ptr->rethrow_unhandled_exception(); - } -}; - -template -class await_handler - : public await_handler_base> -{ -public: - using await_handler_base>::await_handler_base; - - template - void operator()(Args&&... args) - { - typename awaiter::ptr ptr(std::move(this->awaiter_)); - this->awaitee_->return_value( - std::forward_as_tuple(std::forward(args)...)); - this->awaitee_->wake_caller(); - ptr->rethrow_unhandled_exception(); - } -}; - -template -class await_handler - : public await_handler_base> -{ -public: - using await_handler_base>::await_handler_base; - - template - void operator()(const asio::error_code& ec, Args&&... args) - { - typename awaiter::ptr ptr(std::move(this->awaiter_)); - if (ec) - { - this->awaitee_->set_except( - std::make_exception_ptr(asio::system_error(ec))); - } - else - { - this->awaitee_->return_value( - std::forward_as_tuple(std::forward(args)...)); - } - this->awaitee_->wake_caller(); - ptr->rethrow_unhandled_exception(); - } -}; - -template -class await_handler - : public await_handler_base> -{ -public: - using await_handler_base>::await_handler_base; - - template - void operator()(std::exception_ptr ex, Args&&... args) - { - typename awaiter::ptr ptr(std::move(this->awaiter_)); - if (ex) - this->awaitee_->set_except(ex); - else - { - this->awaitee_->return_value( - std::forward_as_tuple(std::forward(args)...)); - } - this->awaitee_->wake_caller(); - ptr->rethrow_unhandled_exception(); - } -}; - -template -struct awaitable_signature; - -template -struct awaitable_signature> -{ - typedef void type(std::exception_ptr, T); -}; - -template -struct awaitable_signature> -{ - typedef void type(std::exception_ptr); -}; - -template -awaiter* co_spawn_entry_point(awaitable*, - executor_work_guard work_guard, F f, Handler handler) -{ - bool done = false; - - try - { - T t = co_await f(); - - done = true; - - (dispatch)(work_guard.get_executor(), - [handler = std::move(handler), t = std::move(t)]() mutable - { - handler(std::exception_ptr(), std::move(t)); - }); - } - catch (...) - { - if (done) - throw; - - (dispatch)(work_guard.get_executor(), - [handler = std::move(handler), e = std::current_exception()]() mutable - { - handler(e, T()); - }); - } -} - -template -awaiter* co_spawn_entry_point(awaitable*, - executor_work_guard work_guard, F f, Handler handler) -{ - std::exception_ptr e = nullptr; - - try - { - co_await f(); - } - catch (...) - { - e = std::current_exception(); - } - - (dispatch)(work_guard.get_executor(), - [handler = std::move(handler), e]() mutable - { - handler(e); - }); -} - -template -auto co_spawn(const Executor& ex, F&& f, CompletionToken&& token) -{ - typedef typename result_of::type awaitable_type; - typedef typename awaitable_type::executor_type executor_type; - typedef typename awaitable_signature::type signature_type; - - async_completion completion(token); - - executor_type ex2(ex); - auto work_guard = make_work_guard(completion.completion_handler, ex2); - - auto* a = (co_spawn_entry_point)( - static_cast(nullptr), std::move(work_guard), - std::forward(f), std::move(completion.completion_handler)); - - a->set_executor(ex2); - (post)(co_spawn_handler(a)); - - return completion.result.get(); -} - -#if defined(_MSC_VER) -# pragma warning(push) -# pragma warning(disable:4033) -#endif // defined(_MSC_VER) - -#if defined(_MSC_VER) -template T dummy_return() -{ - return std::move(*static_cast(nullptr)); -} - -template <> -inline void dummy_return() -{ -} -#endif // defined(_MSC_VER) - -template -inline Awaitable make_dummy_awaitable() -{ - for (;;) co_await std::experimental::suspend_always(); -#if defined(_MSC_VER) - co_return dummy_return(); -#endif // defined(_MSC_VER) -} - -#if defined(_MSC_VER) -# pragma warning(pop) -#endif // defined(_MSC_VER) - -} // namespace detail -} // namespace experimental - -template -class async_result, R(Args...)> -{ -public: - typedef experimental::detail::await_handler< - Executor, typename decay::type...> completion_handler_type; - - typedef typename experimental::detail::await_handler< - Executor, Args...>::awaitable_type return_type; - - async_result(completion_handler_type& h) - : awaitable_(experimental::detail::make_dummy_awaitable()) - { - h.attach_awaitee(awaitable_); - } - - return_type get() - { - return std::move(awaitable_); - } - -private: - return_type awaitable_; -}; - -} // namespace asio - -namespace std { namespace experimental { - -template -struct coroutine_traits< - asio::experimental::detail::awaiter*, Args...> -{ - typedef asio::experimental::detail::awaiter promise_type; -}; - -template -struct coroutine_traits< - asio::experimental::awaitable, Args...> -{ - typedef asio::experimental::detail::awaitee promise_type; -}; - -}} // namespace std::experimental - -#include "asio/detail/pop_options.hpp" - -#endif // ASIO_EXPERIMENTAL_IMPL_CO_SPAWN_HPP diff --git a/asio/include/asio/experimental/impl/detached.hpp b/asio/include/asio/experimental/impl/detached.hpp deleted file mode 100644 index 2f575674..00000000 --- a/asio/include/asio/experimental/impl/detached.hpp +++ /dev/null @@ -1,90 +0,0 @@ -// -// experimental/impl/detached.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// -// Copyright (c) 2003-2019 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_EXPERIMENTAL_IMPL_DETACHED_HPP -#define ASIO_EXPERIMENTAL_IMPL_DETACHED_HPP - -#if defined(_MSC_VER) && (_MSC_VER >= 1200) -# pragma once -#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) - -#include "asio/detail/config.hpp" -#include "asio/async_result.hpp" -#include "asio/detail/variadic_templates.hpp" -#include "asio/system_error.hpp" - -#include "asio/detail/push_options.hpp" - -namespace asio { -namespace experimental { -namespace detail { - - // Class to adapt a detached_t as a completion handler. - class detached_handler - { - public: - detached_handler(detached_t) - { - } - -#if defined(ASIO_HAS_VARIADIC_TEMPLATES) - - template - void operator()(Args...) - { - } - -#else // defined(ASIO_HAS_VARIADIC_TEMPLATES) - - void operator()() - { - } - -#define ASIO_PRIVATE_DETACHED_DEF(n) \ - template \ - void operator()(ASIO_VARIADIC_BYVAL_PARAMS(n)) \ - { \ - } \ - /**/ - ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_DETACHED_DEF) -#undef ASIO_PRIVATE_DETACHED_DEF - -#endif // defined(ASIO_HAS_VARIADIC_TEMPLATES) - }; - -} // namespace detail -} // namespace experimental - -#if !defined(GENERATING_DOCUMENTATION) - -template -struct async_result -{ - typedef asio::experimental::detail::detached_handler - completion_handler_type; - - typedef void return_type; - - explicit async_result(completion_handler_type&) - { - } - - void get() - { - } -}; - -#endif // !defined(GENERATING_DOCUMENTATION) - -} // namespace asio - -#include "asio/detail/pop_options.hpp" - -#endif // ASIO_EXPERIMENTAL_IMPL_DETACHED_HPP diff --git a/asio/include/asio/impl/awaitable.hpp b/asio/include/asio/impl/awaitable.hpp new file mode 100644 index 00000000..c3731d40 --- /dev/null +++ b/asio/include/asio/impl/awaitable.hpp @@ -0,0 +1,418 @@ +// +// impl/awaitable.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2019 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 +#include +#include +#include +#include "asio/detail/thread_context.hpp" +#include "asio/detail/thread_info_base.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/post.hpp" +#include "asio/system_error.hpp" +#include "asio/this_coro.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +// 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 +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::thread_call_stack::top(), + 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::thread_call_stack::top(), + 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) noexcept + { + 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); + } + } + + template + auto await_transform(awaitable a) const + { + return a; + } + + // 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) noexcept + { + } + + auto await_resume() const noexcept + { + return this_->attached_thread_->get_executor(); + } + }; + + return result{this}; + } + + // 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 + auto await_transform(Function f, + typename enable_if< + is_convertible< + typename result_of::type, + awaitable_thread* + >::value + >::type* = 0) + { + struct result + { + Function function_; + awaitable_frame_base* this_; + + bool await_ready() const noexcept + { + return false; + } + + void await_suspend(coroutine_handle) noexcept + { + function_(this_); + } + + void await_resume() const noexcept + { + } + }; + + return result{std::move(f), this}; + } + + void attach_thread(awaitable_thread* handler) noexcept + { + attached_thread_ = handler; + } + + awaitable_thread* detach_thread() noexcept + { + return std::exchange(attached_thread_, nullptr); + } + + void push_frame(awaitable_frame_base* caller) noexcept + { + caller_ = caller; + attached_thread_ = caller_->attached_thread_; + attached_thread_->top_of_stack_ = this; + caller_->attached_thread_ = nullptr; + } + + void pop_frame() noexcept + { + if (caller_) + caller_->attached_thread_ = attached_thread_; + attached_thread_->top_of_stack_ = caller_; + attached_thread_ = nullptr; + caller_ = nullptr; + } + + void resume() + { + coro_.resume(); + } + + void destroy() + { + coro_.destroy(); + } + +protected: + coroutine_handle coro_ = nullptr; + awaitable_thread* attached_thread_ = nullptr; + awaitable_frame_base* caller_ = nullptr; + std::exception_ptr pending_exception_ = nullptr; +}; + +template +class awaitable_frame + : public awaitable_frame_base +{ +public: + awaitable_frame() noexcept + { + } + + awaitable_frame(awaitable_frame&& other) noexcept + : awaitable_frame_base(std::move(other)) + { + } + + ~awaitable_frame() + { + if (has_result_) + static_cast(static_cast(result_))->~T(); + } + + awaitable get_return_object() noexcept + { + this->coro_ = coroutine_handle::from_promise(*this); + return awaitable(this); + }; + + template + void return_value(U&& u) + { + new (&result_) T(std::forward(u)); + has_result_ = true; + } + + template + void return_values(Us&&... us) + { + this->return_value(std::forward_as_tuple(std::forward(us)...)); + } + + T get() + { + this->caller_ = nullptr; + this->rethrow_exception(); + return std::move(*static_cast(static_cast(result_))); + } + +private: + alignas(T) unsigned char result_[sizeof(T)]; + bool has_result_ = false; +}; + +template +class awaitable_frame + : public awaitable_frame_base +{ +public: + awaitable get_return_object() + { + this->coro_ = coroutine_handle::from_promise(*this); + return awaitable(this); + }; + + void return_void() + { + } + + void get() + { + this->caller_ = nullptr; + this->rethrow_exception(); + } +}; + +template +class awaitable_thread +{ +public: + typedef Executor executor_type; + + // Construct from the entry point of a new thread of execution. + awaitable_thread(awaitable p, const Executor& ex) + : bottom_of_stack_(std::move(p)), + top_of_stack_(bottom_of_stack_.frame_), + executor_(ex) + { + } + + // Transfer ownership from another awaitable_thread. + awaitable_thread(awaitable_thread&& other) noexcept + : bottom_of_stack_(std::move(other.bottom_of_stack_)), + top_of_stack_(std::exchange(other.top_of_stack_, nullptr)), + executor_(std::move(other.executor_)) + { + } + + // 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. + (post)(executor_, + [a = std::move(bottom_of_stack_)]() mutable + { + awaitable(std::move(a)); + }); + } + } + + executor_type get_executor() const noexcept + { + return executor_; + } + + // Launch a new thread of execution. + void launch() + { + top_of_stack_->attach_thread(this); + pump(); + } + +protected: + template 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 top_of_stack_->resume(); while (top_of_stack_); + if (bottom_of_stack_.valid()) + { + awaitable a(std::move(bottom_of_stack_)); + a.frame_->rethrow_exception(); + } + } + + awaitable bottom_of_stack_; + awaitable_frame_base* top_of_stack_; + executor_type executor_; +}; + +} // namespace detail +} // namespace asio + +namespace std { namespace experimental { + +template +struct coroutine_traits, Args...> +{ + typedef asio::detail::awaitable_frame promise_type; +}; + +}} // namespace std::experimental + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IMPL_AWAITABLE_HPP diff --git a/asio/include/asio/impl/co_spawn.hpp b/asio/include/asio/impl/co_spawn.hpp new file mode 100644 index 00000000..40c5122d --- /dev/null +++ b/asio/include/asio/impl/co_spawn.hpp @@ -0,0 +1,138 @@ +// +// impl/co_spawn.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2019 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_CO_SPAWN_HPP +#define ASIO_IMPL_CO_SPAWN_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/awaitable.hpp" +#include "asio/dispatch.hpp" +#include "asio/post.hpp" +#include "asio/use_awaitable.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +awaitable co_spawn_entry_point( + awaitable*, Executor ex, F f, Handler handler) +{ + auto spawn_work = make_work_guard(ex); + auto handler_work = make_work_guard(handler, ex); + + (void) co_await (post)(spawn_work.get_executor(), + use_awaitable_t{}); + + bool done = false; + try + { + T t = co_await f(); + + done = true; + + (dispatch)(handler_work.get_executor(), + [handler = std::move(handler), t = std::move(t)]() mutable + { + handler(std::exception_ptr(), std::move(t)); + }); + } + catch (...) + { + if (done) + throw; + + (dispatch)(handler_work.get_executor(), + [handler = std::move(handler), e = std::current_exception()]() mutable + { + handler(e, T()); + }); + } +} + +template +awaitable co_spawn_entry_point( + awaitable*, Executor ex, F f, Handler handler) +{ + auto spawn_work = make_work_guard(ex); + auto handler_work = make_work_guard(handler, ex); + + (void) co_await (post)(spawn_work.get_executor(), + use_awaitable_t{}); + + std::exception_ptr e = nullptr; + try + { + co_await f(); + } + catch (...) + { + e = std::current_exception(); + } + + (dispatch)(handler_work.get_executor(), + [handler = std::move(handler), e]() mutable + { + handler(e); + }); +} + +struct initiate_co_spawn +{ + template + void operator()(Handler&& handler, const Executor& ex, F&& f) const + { + typedef typename result_of::type awaitable_type; + typedef typename awaitable_type::executor_type executor_type; + + executor_type ex2(ex); + auto a = (co_spawn_entry_point)(static_cast(nullptr), + ex2, std::forward(f), std::forward(handler)); + awaitable_handler(std::move(a), ex2).launch(); + } +}; + +} // namespace detail + +template +inline ASIO_INITFN_RESULT_TYPE(CompletionToken, + typename detail::awaitable_signature::type>::type) +co_spawn(const Executor& ex, F&& f, CompletionToken&& token, + typename enable_if< + is_executor::value + >::type*) +{ + return async_initiate::type>>( + detail::initiate_co_spawn(), token, ex, std::forward(f)); +} + +template +inline ASIO_INITFN_RESULT_TYPE(CompletionToken, + typename detail::awaitable_signature::type>::type) +co_spawn(ExecutionContext& ctx, F&& f, CompletionToken&& token, + typename enable_if< + is_convertible::value + >::type*) +{ + return (co_spawn)(ctx.get_executor(), std::forward(f), + std::forward(token)); +} + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IMPL_CO_SPAWN_HPP diff --git a/asio/include/asio/impl/detached.hpp b/asio/include/asio/impl/detached.hpp new file mode 100644 index 00000000..8817ba95 --- /dev/null +++ b/asio/include/asio/impl/detached.hpp @@ -0,0 +1,130 @@ +// +// impl/detached.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2019 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_DETACHED_HPP +#define ASIO_IMPL_DETACHED_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/async_result.hpp" +#include "asio/detail/variadic_templates.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + + // Class to adapt a detached_t as a completion handler. + class detached_handler + { + public: + typedef void result_type; + + detached_handler(detached_t) + { + } + +#if defined(ASIO_HAS_VARIADIC_TEMPLATES) + + template + void operator()(Args...) + { + } + +#else // defined(ASIO_HAS_VARIADIC_TEMPLATES) + + void operator()() + { + } + +#define ASIO_PRIVATE_DETACHED_DEF(n) \ + template \ + void operator()(ASIO_VARIADIC_TARGS(n)) \ + { \ + } \ + /**/ + ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_DETACHED_DEF) +#undef ASIO_PRIVATE_DETACHED_DEF + +#endif // defined(ASIO_HAS_VARIADIC_TEMPLATES) + }; + +} // namespace detail + +#if !defined(GENERATING_DOCUMENTATION) + +template +struct async_result +{ + typedef asio::detail::detached_handler completion_handler_type; + + typedef void return_type; + + explicit async_result(completion_handler_type&) + { + } + + void get() + { + } + +#if defined(ASIO_HAS_VARIADIC_TEMPLATES) + + template + static return_type initiate( + ASIO_MOVE_ARG(Initiation) initiation, + ASIO_MOVE_ARG(RawCompletionToken), + ASIO_MOVE_ARG(Args)... args) + { + ASIO_MOVE_CAST(Initiation)(initiation)( + detail::detached_handler(detached_t()), + ASIO_MOVE_CAST(Args)(args)...); + } + +#else // defined(ASIO_HAS_VARIADIC_TEMPLATES) + + template + static return_type initiate( + ASIO_MOVE_ARG(Initiation) initiation, + ASIO_MOVE_ARG(RawCompletionToken)) + { + ASIO_MOVE_CAST(Initiation)(initiation)( + detail::detached_handler(detached_t())); + } + +#define ASIO_PRIVATE_INITIATE_DEF(n) \ + template \ + static return_type initiate( \ + ASIO_MOVE_ARG(Initiation) initiation, \ + ASIO_MOVE_ARG(RawCompletionToken), \ + ASIO_VARIADIC_MOVE_PARAMS(n)) \ + { \ + ASIO_MOVE_CAST(Initiation)(initiation)( \ + detail::detached_handler(detached_t()), \ + ASIO_VARIADIC_MOVE_ARGS(n)); \ + } \ + /**/ + ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_INITIATE_DEF) +#undef ASIO_PRIVATE_INITIATE_DEF + +#endif // defined(ASIO_HAS_VARIADIC_TEMPLATES) +}; + +#endif // !defined(GENERATING_DOCUMENTATION) + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IMPL_DETACHED_HPP diff --git a/asio/include/asio/experimental/impl/redirect_error.hpp b/asio/include/asio/impl/redirect_error.hpp similarity index 58% rename from asio/include/asio/experimental/impl/redirect_error.hpp rename to asio/include/asio/impl/redirect_error.hpp index 57f8fea2..ca1e5e52 100644 --- a/asio/include/asio/experimental/impl/redirect_error.hpp +++ b/asio/include/asio/impl/redirect_error.hpp @@ -1,6 +1,6 @@ -// -// experimental/impl/redirect_error.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +// impl/redirect_error.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com) // @@ -8,8 +8,8 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef ASIO_EXPERIMENTAL_IMPL_REDIRECT_ERROR_HPP -#define ASIO_EXPERIMENTAL_IMPL_REDIRECT_ERROR_HPP +#ifndef ASIO_IMPL_REDIRECT_ERROR_HPP +#define ASIO_IMPL_REDIRECT_ERROR_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once @@ -29,7 +29,6 @@ #include "asio/detail/push_options.hpp" namespace asio { -namespace experimental { namespace detail { // Class to adapt a redirect_error_t as a completion handler. @@ -37,6 +36,8 @@ template class redirect_error_handler { public: + typedef void result_type; + template redirect_error_handler(redirect_error_t e) : ec_(e.ec_), @@ -44,6 +45,14 @@ public: { } + template + redirect_error_handler(asio::error_code& ec, + ASIO_MOVE_ARG(RedirectedHandler) h) + : ec_(ec), + handler_(ASIO_MOVE_CAST(RedirectedHandler)(h)) + { + } + void operator()() { handler_(); @@ -210,36 +219,131 @@ struct redirect_error_signature #endif // defined(ASIO_HAS_VARIADIC_TEMPLATES) } // namespace detail -} // namespace experimental #if !defined(GENERATING_DOCUMENTATION) template -struct async_result, Signature> - : async_result::type> +struct async_result, Signature> { - typedef experimental::detail::redirect_error_handler< - typename async_result::type> - ::completion_handler_type> completion_handler_type; + typedef typename async_result::type> + ::return_type return_type; - explicit async_result(completion_handler_type& h) - : async_result::type>(h.handler_) + template + struct init_wrapper { + template + init_wrapper(asio::error_code& ec, ASIO_MOVE_ARG(Init) init) + : ec_(ec), + initiation_(ASIO_MOVE_CAST(Init)(init)) + { + } + +#if defined(ASIO_HAS_VARIADIC_TEMPLATES) + + template + void operator()( + ASIO_MOVE_ARG(Handler) handler, + ASIO_MOVE_ARG(Args)... args) + { + ASIO_MOVE_CAST(Initiation)(initiation_)( + detail::redirect_error_handler< + typename decay::type>( + ec_, ASIO_MOVE_CAST(Handler)(handler)), + ASIO_MOVE_CAST(Args)(args)...); + } + +#else // defined(ASIO_HAS_VARIADIC_TEMPLATES) + + template + void operator()( + ASIO_MOVE_ARG(Handler) handler) + { + ASIO_MOVE_CAST(Initiation)(initiation_)( + detail::redirect_error_handler< + typename decay::type>( + ec_, ASIO_MOVE_CAST(Handler)(handler))); + } + +#define ASIO_PRIVATE_INIT_WRAPPER_DEF(n) \ + template \ + void operator()( \ + ASIO_MOVE_ARG(Handler) handler, \ + ASIO_VARIADIC_MOVE_PARAMS(n)) \ + { \ + ASIO_MOVE_CAST(Initiation)(initiation_)( \ + detail::redirect_error_handler< \ + typename decay::type>( \ + ec_, ASIO_MOVE_CAST(Handler)(handler)), \ + ASIO_VARIADIC_MOVE_ARGS(n)); \ + } \ + /**/ + ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_INIT_WRAPPER_DEF) +#undef ASIO_PRIVATE_INIT_WRAPPER_DEF + +#endif // defined(ASIO_HAS_VARIADIC_TEMPLATES) + + asio::error_code& ec_; + Initiation initiation_; + }; + +#if defined(ASIO_HAS_VARIADIC_TEMPLATES) + + template + static return_type initiate( + ASIO_MOVE_ARG(Initiation) initiation, + ASIO_MOVE_ARG(RawCompletionToken) token, + ASIO_MOVE_ARG(Args)... args) + { + return async_initiate::type>( + init_wrapper::type>( + token.ec_, ASIO_MOVE_CAST(Initiation)(initiation)), + token.token_, ASIO_MOVE_CAST(Args)(args)...); } + +#else // defined(ASIO_HAS_VARIADIC_TEMPLATES) + + template + static return_type initiate( + ASIO_MOVE_ARG(Initiation) initiation, + ASIO_MOVE_ARG(RawCompletionToken) token) + { + return async_initiate::type>( + init_wrapper::type>( + token.ec_, ASIO_MOVE_CAST(Initiation)(initiation)), + token.token_); + } + +#define ASIO_PRIVATE_INITIATE_DEF(n) \ + template \ + static return_type initiate( \ + ASIO_MOVE_ARG(Initiation) initiation, \ + ASIO_MOVE_ARG(RawCompletionToken) token, \ + ASIO_VARIADIC_MOVE_PARAMS(n)) \ + { \ + return async_initiate::type>( \ + init_wrapper::type>( \ + token.ec_, ASIO_MOVE_CAST(Initiation)(initiation)), \ + token.token_, ASIO_VARIADIC_MOVE_ARGS(n)); \ + } \ + /**/ + ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_INITIATE_DEF) +#undef ASIO_PRIVATE_INITIATE_DEF + +#endif // defined(ASIO_HAS_VARIADIC_TEMPLATES) }; template -struct associated_executor< - experimental::detail::redirect_error_handler, Executor> +struct associated_executor, Executor> { typedef typename associated_executor::type type; static type get( - const experimental::detail::redirect_error_handler& h, + const detail::redirect_error_handler& h, const Executor& ex = Executor()) ASIO_NOEXCEPT { return associated_executor::get(h.handler_, ex); @@ -247,13 +351,12 @@ struct associated_executor< }; template -struct associated_allocator< - experimental::detail::redirect_error_handler, Allocator> +struct associated_allocator, Allocator> { typedef typename associated_allocator::type type; static type get( - const experimental::detail::redirect_error_handler& h, + const detail::redirect_error_handler& h, const Allocator& a = Allocator()) ASIO_NOEXCEPT { return associated_allocator::get(h.handler_, a); @@ -266,4 +369,4 @@ struct associated_allocator< #include "asio/detail/pop_options.hpp" -#endif // ASIO_EXPERIMENTAL_IMPL_REDIRECT_ERROR_HPP +#endif // ASIO_IMPL_REDIRECT_ERROR_HPP diff --git a/asio/include/asio/impl/use_awaitable.hpp b/asio/include/asio/impl/use_awaitable.hpp new file mode 100644 index 00000000..78c82748 --- /dev/null +++ b/asio/include/asio/impl/use_awaitable.hpp @@ -0,0 +1,272 @@ +// +// impl/use_awaitable.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2019 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_USE_AWAITABLE_HPP +#define ASIO_IMPL_USE_AWAITABLE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/async_result.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class awaitable_handler_base + : public awaitable_thread +{ +public: + typedef void result_type; + typedef awaitable awaitable_type; + + // Construct from the entry point of a new thread of execution. + awaitable_handler_base(awaitable a, const Executor& ex) + : awaitable_thread(std::move(a), ex) + { + } + + // Transfer ownership from another awaitable_thread. + explicit awaitable_handler_base(awaitable_thread* h) + : awaitable_thread(std::move(*h)) + { + } + +protected: + awaitable_frame* frame() noexcept + { + return static_cast*>(this->top_of_stack_); + } +}; + +template +class awaitable_handler; + +template +class awaitable_handler + : public awaitable_handler_base +{ +public: + using awaitable_handler_base::awaitable_handler_base; + + void operator()() + { + this->frame()->attach_thread(this); + this->frame()->return_void(); + this->frame()->pop_frame(); + this->pump(); + } +}; + +template +class awaitable_handler + : public awaitable_handler_base +{ +public: + using awaitable_handler_base::awaitable_handler_base; + + void operator()(const asio::error_code& ec) + { + this->frame()->attach_thread(this); + if (ec) + this->frame()->set_error(ec); + else + this->frame()->return_void(); + this->frame()->pop_frame(); + this->pump(); + } +}; + +template +class awaitable_handler + : public awaitable_handler_base +{ +public: + using awaitable_handler_base::awaitable_handler_base; + + void operator()(std::exception_ptr ex) + { + this->frame()->attach_thread(this); + if (ex) + this->frame()->set_except(ex); + else + this->frame()->return_void(); + this->frame()->pop_frame(); + this->pump(); + } +}; + +template +class awaitable_handler + : public awaitable_handler_base +{ +public: + using awaitable_handler_base::awaitable_handler_base; + + template + void operator()(Arg&& arg) + { + this->frame()->attach_thread(this); + this->frame()->return_value(std::forward(arg)); + this->frame()->pop_frame(); + this->pump(); + } +}; + +template +class awaitable_handler + : public awaitable_handler_base +{ +public: + using awaitable_handler_base::awaitable_handler_base; + + template + void operator()(const asio::error_code& ec, Arg&& arg) + { + this->frame()->attach_thread(this); + if (ec) + this->frame()->set_error(ec); + else + this->frame()->return_value(std::forward(arg)); + this->frame()->pop_frame(); + this->pump(); + } +}; + +template +class awaitable_handler + : public awaitable_handler_base +{ +public: + using awaitable_handler_base::awaitable_handler_base; + + template + void operator()(std::exception_ptr ex, Arg&& arg) + { + this->frame()->attach_thread(this); + if (ex) + this->frame()->set_except(ex); + else + this->frame()->return_value(std::forward(arg)); + this->frame()->pop_frame(); + this->pump(); + } +}; + +template +class awaitable_handler + : public awaitable_handler_base> +{ +public: + using awaitable_handler_base>::awaitable_handler_base; + + template + void operator()(Args&&... args) + { + this->frame()->attach_thread(this); + this->frame()->return_values(std::forward(args)...); + this->frame()->pop_frame(); + this->pump(); + } +}; + +template +class awaitable_handler + : public awaitable_handler_base> +{ +public: + using awaitable_handler_base>::awaitable_handler_base; + + template + void operator()(const asio::error_code& ec, Args&&... args) + { + this->frame()->attach_thread(this); + if (ec) + this->frame()->set_error(ec); + else + this->frame()->return_values(std::forward(args)...); + this->frame()->pop_frame(); + this->pump(); + } +}; + +template +class awaitable_handler + : public awaitable_handler_base> +{ +public: + using awaitable_handler_base>::awaitable_handler_base; + + template + void operator()(std::exception_ptr ex, Args&&... args) + { + this->frame()->attach_thread(this); + if (ex) + this->frame()->set_except(ex); + else + this->frame()->return_values(std::forward(args)...); + this->frame()->pop_frame(); + this->pump(); + } +}; + +} // namespace detail + +template +class async_result, R(Args...)> +{ +public: + typedef typename detail::awaitable_handler< + Executor, typename decay::type...> handler_type; + typedef typename handler_type::awaitable_type return_type; + +#if defined(_MSC_VER) + template + static T dummy_return() + { + return std::move(*static_cast(nullptr)); + } + + template <> + static void dummy_return() + { + } +#endif // defined(_MSC_VER) + + template + static return_type initiate(Initiation initiation, + use_awaitable_t, InitArgs... args) + { + co_await [&](auto* frame) + { + handler_type handler(frame->detach_thread()); + std::move(initiation)(std::move(handler), std::move(args)...); + return static_cast(nullptr); + }; + + for (;;) {} // Never reached. +#if defined(_MSC_VER) + co_return dummy_return(); +#endif // defined(_MSC_VER) + } +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IMPL_USE_AWAITABLE_HPP diff --git a/asio/include/asio/experimental/redirect_error.hpp b/asio/include/asio/redirect_error.hpp similarity index 80% rename from asio/include/asio/experimental/redirect_error.hpp rename to asio/include/asio/redirect_error.hpp index b701ad33..b98249e5 100644 --- a/asio/include/asio/experimental/redirect_error.hpp +++ b/asio/include/asio/redirect_error.hpp @@ -1,6 +1,6 @@ // -// experimental/redirect_error.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// redirect_error.hpp +// ~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com) // @@ -8,8 +8,8 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef ASIO_EXPERIMENTAL_REDIRECT_ERROR_HPP -#define ASIO_EXPERIMENTAL_REDIRECT_ERROR_HPP +#ifndef ASIO_REDIRECT_ERROR_HPP +#define ASIO_REDIRECT_ERROR_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once @@ -22,7 +22,6 @@ #include "asio/detail/push_options.hpp" namespace asio { -namespace experimental { /// Completion token type used to specify that an error produced by an /// asynchronous operation is captured to an error_code variable. @@ -51,17 +50,16 @@ public: /// Create a completion token to capture error_code values to a variable. template inline redirect_error_t::type> redirect_error( - CompletionToken&& completion_token, asio::error_code& ec) + ASIO_MOVE_ARG(CompletionToken) completion_token, asio::error_code& ec) { return redirect_error_t::type>( ASIO_MOVE_CAST(CompletionToken)(completion_token), ec); } -} // namespace experimental } // namespace asio #include "asio/detail/pop_options.hpp" -#include "asio/experimental/impl/redirect_error.hpp" +#include "asio/impl/redirect_error.hpp" -#endif // ASIO_EXPERIMENTAL_REDIRECT_ERROR_HPP +#endif // ASIO_REDIRECT_ERROR_HPP diff --git a/asio/include/asio/this_coro.hpp b/asio/include/asio/this_coro.hpp new file mode 100644 index 00000000..02374e10 --- /dev/null +++ b/asio/include/asio/this_coro.hpp @@ -0,0 +1,45 @@ +// +// this_coro.hpp +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2019 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_THIS_CORO_HPP +#define ASIO_THIS_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/detail/push_options.hpp" + +namespace asio { +namespace this_coro { + +/// Awaitable type that returns the executor of the current coroutine. +struct executor_t +{ + ASIO_CONSTEXPR executor_t() + { + } +}; + +/// Awaitable object that returns the executor of the current coroutine. +#if defined(ASIO_HAS_CONSTEXPR) || defined(GENERATING_DOCUMENTATION) +constexpr executor_t executor; +#elif defined(ASIO_MSVC) +__declspec(selectany) executor_t executor; +#endif + +} // namespace this_coro +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_THIS_CORO_HPP diff --git a/asio/include/asio/use_awaitable.hpp b/asio/include/asio/use_awaitable.hpp new file mode 100644 index 00000000..8d8a049f --- /dev/null +++ b/asio/include/asio/use_awaitable.hpp @@ -0,0 +1,71 @@ +// +// use_awaitable.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2019 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_USE_AWAITABLE_HPP +#define ASIO_USE_AWAITABLE_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/detail/push_options.hpp" + +namespace asio { + +/// A completion token that represents the currently executing coroutine. +/** + * The @c use_awaitable_t class, with its value @c use_awaitable, is used to + * represent the currently executing coroutine. This completion token may be + * passed as a handler to an asynchronous operation. For example: + * + * @code awaitable my_coroutine() + * { + * std::size_t n = co_await my_socket.async_read_some(buffer, use_awaitable); + * ... + * } @endcode + * + * When used with co_await, the initiating function (@c async_read_some in the + * above example) suspends the current coroutine. The coroutine is resumed when + * the asynchronous operation completes, and the result of the operation is + * returned. + */ +template +struct use_awaitable_t +{ + ASIO_CONSTEXPR use_awaitable_t() + { + } +}; + +/// A completion token object that represents the currently executing coroutine. +/** + * See the documentation for asio::use_awaitable_t for a usage example. + */ +#if defined(ASIO_HAS_CONSTEXPR) || defined(GENERATING_DOCUMENTATION) +constexpr use_awaitable_t<> use_awaitable; +#elif defined(ASIO_MSVC) +__declspec(selectany) use_awaitable_t<> use_awaitable; +#endif + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/impl/use_awaitable.hpp" + +#endif // defined(ASIO_HAS_CO_AWAIT) || defined(GENERATING_DOCUMENTATION) + +#endif // ASIO_USE_AWAITABLE_HPP diff --git a/asio/src/doc/overview.qbk b/asio/src/doc/overview.qbk index c4e86025..9f3e7ad5 100644 --- a/asio/src/doc/overview.qbk +++ b/asio/src/doc/overview.qbk @@ -22,7 +22,7 @@ * [link asio.overview.core.concurrency_hint Concurrency Hints] * [link asio.overview.core.coroutine Stackless Coroutines] * [link asio.overview.core.spawn Stackful Coroutines] - * [link asio.overview.core.coroutines_ts Coroutines TS Support (experimental)] + * [link asio.overview.core.coroutines_ts Coroutines TS Support] * [link asio.overview.networking Networking] * [link asio.overview.networking.protocols TCP, UDP and ICMP] * [link asio.overview.networking.other_protocols Support for Other Protocols] @@ -58,7 +58,7 @@ * [link asio.overview.core.concurrency_hint Concurrency Hints] * [link asio.overview.core.coroutine Stackless Coroutines] * [link asio.overview.core.spawn Stackful Coroutines] -* [link asio.overview.core.coroutines_ts Coroutines TS Support (experimental)] +* [link asio.overview.core.coroutines_ts Coroutines TS Support] [include overview/basics.qbk] [include overview/async.qbk] diff --git a/asio/src/doc/overview/coroutines_ts.qbk b/asio/src/doc/overview/coroutines_ts.qbk index 64ceae77..da687fad 100644 --- a/asio/src/doc/overview/coroutines_ts.qbk +++ b/asio/src/doc/overview/coroutines_ts.qbk @@ -5,37 +5,33 @@ / file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) /] -[section:coroutines_ts Coroutines TS Support (experimental)] +[section:coroutines_ts Coroutines TS Support] -(Note: "Experimental" means that this interface is provided to gather feedback -and may change in subsequent Asio releases.) +Support for the Coroutines TS is provided via the [link +asio.reference.awaitable `awaitable`] class template, the [link +asio.reference.use_awaitable_t `use_awaitable`] completion token, and the [link +asio.reference.co_spawn `co_spawn()`] function. These facilities allow programs +to implement asynchronous logic in a synchronous manner, in conjunction with +the `co_await` keyword, as shown in the following example: -Experimental support for the Coroutines TS is provided via the [link -asio.reference.experimental__co_spawn `experimental::co_spawn()`] -function. This `co_spawn()` function enables programs to implement asynchronous -logic in a synchronous manner, in conjunction with the `co_await` keyword, as -shown in the following example: - - asio::experimental::co_spawn(executor, + asio::co_spawn(executor, [socket = std::move(socket)]() mutable { return echo(std::move(socket)); }, - asio::experimental::detached); + asio::detached); // ... - asio::experimental::awaitable echo(tcp::socket socket) + asio::awaitable echo(tcp::socket socket) { - auto token = co_await asio::experimental::this_coro::token(); - try { char data[1024]; for (;;) { - std::size_t n = co_await socket.async_read_some(asio::buffer(data), token); - co_await async_write(socket, asio::buffer(data, n), token); + std::size_t n = co_await socket.async_read_some(asio::buffer(data), asio::use_awaitable); + co_await async_write(socket, asio::buffer(data, n), asio::use_awaitable); } } catch (std::exception& e) @@ -51,7 +47,7 @@ coroutines; they should all run on the same `strand` so that no explicit synchronisation is required. The second argument is a nullary function object that returns a [link -asio.reference.experimental__awaitable `asio::awaitable`], +asio.reference.awaitable `asio::awaitable`], where `R` is the type of return value produced by the coroutine. In the above example, the coroutine returns `void`. @@ -59,21 +55,15 @@ The third argument is a completion token, and this is used by `co_spawn()` to produce a completion handler with signature `void(std::exception_ptr, R)`. This completion handler is invoked with the result of the coroutine once it has finished. In the above example we pass a completion token type, [link -asio.reference.experimental__detached -`asio::experimental::detached`], which is used to explicitly ignore the -result of an asynchronous operation. +asio.reference.detached `asio::detached`], which is used to explicitly ignore +the result of an asynchronous operation. In this example the body of the coroutine is implemented in the `echo` -function. This function first obtains a completion token that represents the -current coroutine: +function. When the `use_awaitable` completion token is passed to an +asynchronous operation, the operation's initiating function returns an +`awaitable` that may be used with the `co_await` keyword: - auto token = co_await asio::experimental::this_coro::token(); - -When this completion token is passed to an asynchronous operation, the -operation's initiating function returns an `awaitable` that may be used with -the `co_await` keyword: - - std::size_t n = co_await socket.async_read_some(asio::buffer(data), token); + std::size_t n = co_await socket.async_read_some(asio::buffer(data), asio::use_awaitable); Where an asynchronous operation's handler signature has the form: @@ -93,13 +83,13 @@ passed back to the coroutine as a `system_error` exception. [heading See Also] -[link asio.reference.experimental__co_spawn experimental::co_spawn], -[link asio.reference.experimental__detached experimental::detached], -[link asio.reference.experimental__redirect_error experimental::redirect_error], -[link asio.reference.experimental__awaitable experimental::awaitable], -[link asio.reference.experimental__await_token experimental::await_token], -[link asio.reference.experimental__this_coro__executor experimental::this_coro::executor], -[link asio.reference.experimental__this_coro__token experimental::this_coro::token], +[link asio.reference.co_spawn co_spawn], +[link asio.reference.detached detached], +[link asio.reference.redirect_error redirect_error], +[link asio.reference.awaitable awaitable], +[link asio.reference.use_awaitable_t use_awaitable_t], +[link asio.reference.use_awaitable use_awaitable], +[link asio.reference.this_coro__executor this_coro::executor], [link asio.examples.cpp17_examples.coroutines_ts_support Coroutines TS examples], [link asio.overview.core.spawn Stackful Coroutines], [link asio.overview.core.coroutine Stackless Coroutines]. diff --git a/asio/src/doc/quickref.xml b/asio/src/doc/quickref.xml index 1d1b9498..02d783ac 100644 --- a/asio/src/doc/quickref.xml +++ b/asio/src/doc/quickref.xml @@ -27,6 +27,7 @@ Classes coroutine + detached_t error_code execution_context execution_context::id @@ -42,6 +43,7 @@ service_already_exists system_error system_executor + this_coro::executor_t thread thread_pool thread_pool::executor_type @@ -57,6 +59,7 @@ asio_handler_invoke asio_handler_is_continuation bind_executor + co_spawn dispatch defer get_associated_allocator @@ -72,16 +75,21 @@ Class Templates async_completion + awaitable basic_io_object basic_yield_context executor_binder executor_work_guard + redirect_error_t strand + use_awaitable_t use_future_t Special Values + detached executor_arg + this_coro::executor use_future Boost.Bind Placeholders @@ -455,11 +463,10 @@ - + - @@ -468,9 +475,6 @@ Windows-specific - - Experimental - @@ -511,30 +515,6 @@ windows::stream_handle - - Classes - - experimental::detached_t - experimental::this_coro::executor_t - experimental::this_coro::token_t - - Class Templates - - experimental::awaitable - experimental::await_token - experimental::redirect_error_t - - Free Functions - - experimental::co_spawn - experimental::this_coro::executor - experimental::this_coro::token - - Special Values - - experimental::detached - - diff --git a/asio/src/examples/cpp17/Makefile.am b/asio/src/examples/cpp17/Makefile.am index 2e090989..e87b52d4 100644 --- a/asio/src/examples/cpp17/Makefile.am +++ b/asio/src/examples/cpp17/Makefile.am @@ -1,6 +1,5 @@ EXTRA_DIST = \ coroutines_ts/chat_server.cpp \ - coroutines_ts/double_buffered_echo_server.cpp \ coroutines_ts/echo_server.cpp \ coroutines_ts/range_based_for.cpp \ coroutines_ts/refactored_echo_server.cpp diff --git a/asio/src/examples/cpp17/coroutines_ts/chat_server.cpp b/asio/src/examples/cpp17/coroutines_ts/chat_server.cpp index ea829961..b6c918b2 100644 --- a/asio/src/examples/cpp17/coroutines_ts/chat_server.cpp +++ b/asio/src/examples/cpp17/coroutines_ts/chat_server.cpp @@ -16,20 +16,24 @@ #include #include #include -#include +#include +#include +#include #include #include #include +#include #include #include +#include #include using asio::ip::tcp; -using asio::experimental::awaitable; -using asio::experimental::co_spawn; -using asio::experimental::detached; -using asio::experimental::redirect_error; -namespace this_coro = asio::experimental::this_coro; +using asio::awaitable; +using asio::co_spawn; +using asio::detached; +using asio::redirect_error; +using asio::use_awaitable; //---------------------------------------------------------------------- @@ -112,14 +116,12 @@ public: private: awaitable reader() { - auto token = co_await this_coro::token(); - try { for (std::string read_msg;;) { std::size_t n = co_await asio::async_read_until(socket_, - asio::dynamic_buffer(read_msg, 1024), "\n", token); + asio::dynamic_buffer(read_msg, 1024), "\n", use_awaitable); room_.deliver(read_msg.substr(0, n)); read_msg.erase(0, n); @@ -133,8 +135,6 @@ private: awaitable writer() { - auto token = co_await this_coro::token(); - try { while (socket_.is_open()) @@ -142,12 +142,12 @@ private: if (write_msgs_.empty()) { asio::error_code ec; - co_await timer_.async_wait(redirect_error(token, ec)); + co_await timer_.async_wait(redirect_error(use_awaitable, ec)); } else { co_await asio::async_write(socket_, - asio::buffer(write_msgs_.front()), token); + asio::buffer(write_msgs_.front()), use_awaitable); write_msgs_.pop_front(); } } @@ -175,14 +175,12 @@ private: awaitable listener(tcp::acceptor acceptor) { - auto token = co_await this_coro::token(); - chat_room room; for (;;) { std::make_shared( - co_await acceptor.async_accept(token), + co_await acceptor.async_accept(use_awaitable), room )->start(); } diff --git a/asio/src/examples/cpp17/coroutines_ts/double_buffered_echo_server.cpp b/asio/src/examples/cpp17/coroutines_ts/double_buffered_echo_server.cpp deleted file mode 100644 index acbc49d5..00000000 --- a/asio/src/examples/cpp17/coroutines_ts/double_buffered_echo_server.cpp +++ /dev/null @@ -1,97 +0,0 @@ -// -// double_buffered_echo_server.cpp -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// -// Copyright (c) 2003-2019 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) -// - -#include -#include -#include -#include -#include -#include -#include - -using asio::ip::tcp; -using asio::experimental::co_spawn; -using asio::experimental::detached; -namespace this_coro = asio::experimental::this_coro; - -template - using awaitable = asio::experimental::awaitable< - T, asio::io_context::executor_type>; - -awaitable echo(tcp::socket s) -{ - auto token = co_await this_coro::token(); - - try - { - char data1[1024]; - char data2[1024]; - - char* p1 = data1; - char* p2 = data2; - - // Perform initial read into first buffer. - size_t n = co_await s.async_read_some(asio::buffer(p1, 1024), token); - - for (;;) - { - // Swap received data to other buffer and initiate write operation. - std::swap(p1, p2); - auto write_result = asio::async_write(s, asio::buffer(p2, n), token); - - // Perform next read while write operation is in progress. - n = co_await s.async_read_some(asio::buffer(p1, 1024), token); - - // Wait for write operation to complete before proceeding. - co_await write_result; - } - } - catch (std::exception& e) - { - std::printf("echo Exception: %s\n", e.what()); - } -} - -awaitable listener() -{ - auto executor = co_await this_coro::executor(); - auto token = co_await this_coro::token(); - - tcp::acceptor acceptor(executor, {tcp::v4(), 55555}); - for (;;) - { - tcp::socket socket = co_await acceptor.async_accept(token); - co_spawn(executor, - [socket = std::move(socket)]() mutable - { - return echo(std::move(socket)); - }, - detached); - } -} - -int main() -{ - try - { - asio::io_context io_context(1); - - asio::signal_set signals(io_context, SIGINT, SIGTERM); - signals.async_wait([&](auto, auto){ io_context.stop(); }); - - co_spawn(io_context, listener, detached); - - io_context.run(); - } - catch (std::exception& e) - { - std::printf("Exception: %s\n", e.what()); - } -} diff --git a/asio/src/examples/cpp17/coroutines_ts/echo_server.cpp b/asio/src/examples/cpp17/coroutines_ts/echo_server.cpp index a7b361ec..290f21b1 100644 --- a/asio/src/examples/cpp17/coroutines_ts/echo_server.cpp +++ b/asio/src/examples/cpp17/coroutines_ts/echo_server.cpp @@ -8,8 +8,8 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#include -#include +#include +#include #include #include #include @@ -17,25 +17,21 @@ #include using asio::ip::tcp; -using asio::experimental::co_spawn; -using asio::experimental::detached; -namespace this_coro = asio::experimental::this_coro; - -template - using awaitable = asio::experimental::awaitable< - T, asio::io_context::executor_type>; +using asio::awaitable; +using asio::co_spawn; +using asio::detached; +using asio::use_awaitable; +namespace this_coro = asio::this_coro; awaitable echo(tcp::socket socket) { - auto token = co_await this_coro::token(); - try { char data[1024]; for (;;) { - std::size_t n = co_await socket.async_read_some(asio::buffer(data), token); - co_await async_write(socket, asio::buffer(data, n), token); + std::size_t n = co_await socket.async_read_some(asio::buffer(data), use_awaitable); + co_await async_write(socket, asio::buffer(data, n), use_awaitable); } } catch (std::exception& e) @@ -46,13 +42,11 @@ awaitable echo(tcp::socket socket) awaitable listener() { - auto executor = co_await this_coro::executor(); - auto token = co_await this_coro::token(); - + auto executor = co_await this_coro::executor; tcp::acceptor acceptor(executor, {tcp::v4(), 55555}); for (;;) { - tcp::socket socket = co_await acceptor.async_accept(token); + tcp::socket socket = co_await acceptor.async_accept(use_awaitable); co_spawn(executor, [socket = std::move(socket)]() mutable { diff --git a/asio/src/examples/cpp17/coroutines_ts/range_based_for.cpp b/asio/src/examples/cpp17/coroutines_ts/range_based_for.cpp index 57521c4f..5708c6e1 100644 --- a/asio/src/examples/cpp17/coroutines_ts/range_based_for.cpp +++ b/asio/src/examples/cpp17/coroutines_ts/range_based_for.cpp @@ -8,8 +8,8 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#include -#include +#include +#include #include #include #include @@ -17,10 +17,10 @@ #include using asio::ip::tcp; -using asio::experimental::awaitable; -using asio::experimental::co_spawn; -using asio::experimental::detached; -namespace this_coro = asio::experimental::this_coro; +using asio::awaitable; +using asio::co_spawn; +using asio::detached; +using asio::use_awaitable; class connection_iter { @@ -39,8 +39,7 @@ public: awaitable operator++() { - auto token = co_await this_coro::token(); - socket_ = co_await acceptor_->async_accept(token); + socket_ = co_await acceptor_->async_accept(use_awaitable); } bool operator==(const connection_iter&) const noexcept @@ -63,8 +62,7 @@ public: awaitable begin() { - auto token = co_await this_coro::token(); - tcp::socket s = co_await acceptor_.async_accept(token); + tcp::socket s = co_await acceptor_.async_accept(use_awaitable); co_return connection_iter(acceptor_, std::move(s)); } @@ -77,11 +75,9 @@ public: awaitable listener(tcp::acceptor acceptor) { - auto token = co_await this_coro::token(); - for co_await (tcp::socket s : connections(acceptor)) { - co_await asio::async_write(s, asio::buffer("hello\r\n", 7), token); + co_await asio::async_write(s, asio::buffer("hello\r\n", 7), use_awaitable); } } diff --git a/asio/src/examples/cpp17/coroutines_ts/refactored_echo_server.cpp b/asio/src/examples/cpp17/coroutines_ts/refactored_echo_server.cpp index 4144dd6b..aff1b236 100644 --- a/asio/src/examples/cpp17/coroutines_ts/refactored_echo_server.cpp +++ b/asio/src/examples/cpp17/coroutines_ts/refactored_echo_server.cpp @@ -8,8 +8,8 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#include -#include +#include +#include #include #include #include @@ -17,21 +17,17 @@ #include using asio::ip::tcp; -using asio::experimental::co_spawn; -using asio::experimental::detached; -namespace this_coro = asio::experimental::this_coro; - -template - using awaitable = asio::experimental::awaitable< - T, asio::io_context::executor_type>; +using asio::awaitable; +using asio::co_spawn; +using asio::detached; +using asio::use_awaitable; +namespace this_coro = asio::this_coro; awaitable echo_once(tcp::socket& socket) { - auto token = co_await this_coro::token(); - char data[128]; - std::size_t n = co_await socket.async_read_some(asio::buffer(data), token); - co_await async_write(socket, asio::buffer(data, n), token); + std::size_t n = co_await socket.async_read_some(asio::buffer(data), use_awaitable); + co_await async_write(socket, asio::buffer(data, n), use_awaitable); } awaitable echo(tcp::socket socket) @@ -55,13 +51,11 @@ awaitable echo(tcp::socket socket) awaitable listener() { - auto executor = co_await this_coro::executor(); - auto token = co_await this_coro::token(); - + auto executor = co_await this_coro::executor; tcp::acceptor acceptor(executor, {tcp::v4(), 55555}); for (;;) { - tcp::socket socket = co_await acceptor.async_accept(token); + tcp::socket socket = co_await acceptor.async_accept(use_awaitable); co_spawn(executor, [socket = std::move(socket)]() mutable { diff --git a/asio/src/tests/Makefile.am b/asio/src/tests/Makefile.am index e649cf0d..7674f51a 100644 --- a/asio/src/tests/Makefile.am +++ b/asio/src/tests/Makefile.am @@ -13,6 +13,7 @@ check_PROGRAMS = \ unit/associated_allocator \ unit/associated_executor \ unit/async_result \ + unit/awaitable \ unit/basic_datagram_socket \ unit/basic_deadline_timer \ unit/basic_raw_socket \ @@ -30,11 +31,13 @@ check_PROGRAMS = \ unit/buffered_write_stream \ unit/buffer \ unit/buffers_iterator \ + unit/co_spawn \ unit/completion_condition \ unit/connect \ unit/coroutine \ unit/deadline_timer \ unit/defer \ + unit/detached \ unit/dispatch \ unit/error \ unit/execution_context \ @@ -86,6 +89,7 @@ check_PROGRAMS = \ unit/read \ unit/read_at \ unit/read_until \ + unit/redirect_error \ unit/serial_port \ unit/serial_port_base \ unit/signal_set \ @@ -96,6 +100,7 @@ check_PROGRAMS = \ unit/system_context \ unit/system_executor \ unit/system_timer \ + unit/this_coro \ unit/thread \ unit/time_traits \ unit/ts/buffer \ @@ -106,6 +111,7 @@ check_PROGRAMS = \ unit/ts/netfwd \ unit/ts/socket \ unit/ts/timer \ + unit/use_awaitable \ unit/use_future \ unit/uses_executor \ unit/wait_traits \ @@ -145,6 +151,7 @@ TESTS = \ unit/associated_allocator \ unit/associated_executor \ unit/async_result \ + unit/awaitable \ unit/basic_datagram_socket \ unit/basic_deadline_timer \ unit/basic_raw_socket \ @@ -162,10 +169,12 @@ TESTS = \ unit/buffered_write_stream \ unit/buffer \ unit/buffers_iterator \ + unit/co_spawn \ unit/completion_condition \ unit/connect \ unit/deadline_timer \ unit/defer \ + unit/detached \ unit/dispatch \ unit/error \ unit/execution_context \ @@ -212,6 +221,7 @@ TESTS = \ unit/read \ unit/read_at \ unit/read_until \ + unit/redirect_error \ unit/serial_port \ unit/serial_port_base \ unit/signal_set \ @@ -222,6 +232,7 @@ TESTS = \ unit/system_context \ unit/system_executor \ unit/system_timer \ + unit/this_coro \ unit/thread \ unit/time_traits \ unit/ts/buffer \ @@ -232,6 +243,7 @@ TESTS = \ unit/ts/netfwd \ unit/ts/socket \ unit/ts/timer \ + unit/use_awaitable \ unit/use_future \ unit/uses_executor \ unit/wait_traits \ @@ -275,6 +287,7 @@ endif unit_associated_allocator_SOURCES = unit/associated_allocator.cpp unit_associated_executor_SOURCES = unit/associated_executor.cpp unit_async_result_SOURCES = unit/async_result.cpp +unit_awaitable_SOURCES = unit/awaitable.cpp unit_basic_datagram_socket_SOURCES = unit/basic_datagram_socket.cpp unit_basic_deadline_timer_SOURCES = unit/basic_deadline_timer.cpp unit_basic_raw_socket_SOURCES = unit/basic_raw_socket.cpp @@ -292,11 +305,13 @@ unit_buffers_iterator_SOURCES = unit/buffers_iterator.cpp unit_buffered_read_stream_SOURCES = unit/buffered_read_stream.cpp unit_buffered_stream_SOURCES = unit/buffered_stream.cpp unit_buffered_write_stream_SOURCES = unit/buffered_write_stream.cpp +unit_co_spawn_SOURCES = unit/co_spawn.cpp unit_completion_condition_SOURCES = unit/completion_condition.cpp unit_connect_SOURCES = unit/connect.cpp unit_coroutine_SOURCES = unit/coroutine.cpp unit_deadline_timer_SOURCES = unit/deadline_timer.cpp unit_defer_SOURCES = unit/defer.cpp +unit_detached_SOURCES = unit/detached.cpp unit_dispatch_SOURCES = unit/dispatch.cpp unit_error_SOURCES = unit/error.cpp unit_execution_context_SOURCES = unit/execution_context.cpp @@ -348,6 +363,7 @@ unit_post_SOURCES = unit/post.cpp unit_read_SOURCES = unit/read.cpp unit_read_at_SOURCES = unit/read_at.cpp unit_read_until_SOURCES = unit/read_until.cpp +unit_redirect_error_SOURCES = unit/redirect_error.cpp unit_serial_port_SOURCES = unit/serial_port.cpp unit_serial_port_base_SOURCES = unit/serial_port_base.cpp unit_signal_set_SOURCES = unit/signal_set.cpp @@ -358,6 +374,7 @@ unit_streambuf_SOURCES = unit/streambuf.cpp unit_system_context_SOURCES = unit/system_context.cpp unit_system_executor_SOURCES = unit/system_executor.cpp unit_system_timer_SOURCES = unit/system_timer.cpp +unit_this_coro_SOURCES = unit/this_coro.cpp unit_thread_SOURCES = unit/thread.cpp unit_time_traits_SOURCES = unit/time_traits.cpp unit_ts_buffer_SOURCES = unit/ts/buffer.cpp @@ -368,6 +385,7 @@ unit_ts_net_SOURCES = unit/ts/net.cpp unit_ts_netfwd_SOURCES = unit/ts/netfwd.cpp unit_ts_socket_SOURCES = unit/ts/socket.cpp unit_ts_timer_SOURCES = unit/ts/timer.cpp +unit_use_awaitable_SOURCES = unit/use_awaitable.cpp unit_use_future_SOURCES = unit/use_future.cpp unit_uses_executor_SOURCES = unit/uses_executor.cpp unit_wait_traits_SOURCES = unit/wait_traits.cpp diff --git a/asio/src/tests/unit/.gitignore b/asio/src/tests/unit/.gitignore index 6bd9751d..3374c573 100644 --- a/asio/src/tests/unit/.gitignore +++ b/asio/src/tests/unit/.gitignore @@ -10,6 +10,7 @@ associated_allocator associated_executor async_result +awaitable basic_datagram_socket basic_deadline_timer basic_raw_socket @@ -27,11 +28,13 @@ buffered_read_stream buffered_stream buffered_write_stream buffers_iterator +co_spawn completion_condition connect coroutine deadline_timer defer +detached dispatch error error_handler @@ -49,6 +52,7 @@ post read read_at read_until +redirect_error serial_port serial_port_base signal_set @@ -59,8 +63,10 @@ streambuf system_context system_executor system_timer +this_coro thread time_traits +use_awaitable use_future uses_executor wait_traits diff --git a/asio/src/tests/unit/awaitable.cpp b/asio/src/tests/unit/awaitable.cpp new file mode 100644 index 00000000..ea6d02a5 --- /dev/null +++ b/asio/src/tests/unit/awaitable.cpp @@ -0,0 +1,25 @@ +// +// awaitable.cpp +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2019 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) +// + +// Disable autolinking for unit tests. +#if !defined(BOOST_ALL_NO_LIB) +#define BOOST_ALL_NO_LIB 1 +#endif // !defined(BOOST_ALL_NO_LIB) + +// Test that header file is self-contained. +#include "asio/awaitable.hpp" + +#include "unit_test.hpp" + +ASIO_TEST_SUITE +( + "awaitable", + ASIO_TEST_CASE(null_test) +) diff --git a/asio/src/tests/unit/co_spawn.cpp b/asio/src/tests/unit/co_spawn.cpp new file mode 100644 index 00000000..90f3a2ce --- /dev/null +++ b/asio/src/tests/unit/co_spawn.cpp @@ -0,0 +1,25 @@ +// +// co_spawn.cpp +// ~~~~~~~~~~~~ +// +// Copyright (c) 2003-2019 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) +// + +// Disable autolinking for unit tests. +#if !defined(BOOST_ALL_NO_LIB) +#define BOOST_ALL_NO_LIB 1 +#endif // !defined(BOOST_ALL_NO_LIB) + +// Test that header file is self-contained. +#include "asio/co_spawn.hpp" + +#include "unit_test.hpp" + +ASIO_TEST_SUITE +( + "co_spawn", + ASIO_TEST_CASE(null_test) +) diff --git a/asio/src/tests/unit/detached.cpp b/asio/src/tests/unit/detached.cpp new file mode 100644 index 00000000..9ba45317 --- /dev/null +++ b/asio/src/tests/unit/detached.cpp @@ -0,0 +1,25 @@ +// +// detached.cpp +// ~~~~~~~~~~~~ +// +// Copyright (c) 2003-2019 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) +// + +// Disable autolinking for unit tests. +#if !defined(BOOST_ALL_NO_LIB) +#define BOOST_ALL_NO_LIB 1 +#endif // !defined(BOOST_ALL_NO_LIB) + +// Test that header file is self-contained. +#include "asio/detached.hpp" + +#include "unit_test.hpp" + +ASIO_TEST_SUITE +( + "detached", + ASIO_TEST_CASE(null_test) +) diff --git a/asio/src/tests/unit/redirect_error.cpp b/asio/src/tests/unit/redirect_error.cpp new file mode 100644 index 00000000..0e494971 --- /dev/null +++ b/asio/src/tests/unit/redirect_error.cpp @@ -0,0 +1,25 @@ +// +// redirect_error.cpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2019 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) +// + +// Disable autolinking for unit tests. +#if !defined(BOOST_ALL_NO_LIB) +#define BOOST_ALL_NO_LIB 1 +#endif // !defined(BOOST_ALL_NO_LIB) + +// Test that header file is self-contained. +#include "asio/redirect_error.hpp" + +#include "unit_test.hpp" + +ASIO_TEST_SUITE +( + "redirect_error", + ASIO_TEST_CASE(null_test) +) diff --git a/asio/src/tests/unit/this_coro.cpp b/asio/src/tests/unit/this_coro.cpp new file mode 100644 index 00000000..3ef18777 --- /dev/null +++ b/asio/src/tests/unit/this_coro.cpp @@ -0,0 +1,25 @@ +// +// this_coro.cpp +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2019 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) +// + +// Disable autolinking for unit tests. +#if !defined(BOOST_ALL_NO_LIB) +#define BOOST_ALL_NO_LIB 1 +#endif // !defined(BOOST_ALL_NO_LIB) + +// Test that header file is self-contained. +#include "asio/this_coro.hpp" + +#include "unit_test.hpp" + +ASIO_TEST_SUITE +( + "this_coro", + ASIO_TEST_CASE(null_test) +) diff --git a/asio/src/tests/unit/use_awaitable.cpp b/asio/src/tests/unit/use_awaitable.cpp new file mode 100644 index 00000000..afa7f764 --- /dev/null +++ b/asio/src/tests/unit/use_awaitable.cpp @@ -0,0 +1,25 @@ +// +// use_awaitable.cpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2019 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) +// + +// Disable autolinking for unit tests. +#if !defined(BOOST_ALL_NO_LIB) +#define BOOST_ALL_NO_LIB 1 +#endif // !defined(BOOST_ALL_NO_LIB) + +// Test that header file is self-contained. +#include "asio/use_awaitable.hpp" + +#include "unit_test.hpp" + +ASIO_TEST_SUITE +( + "use_awaitable", + ASIO_TEST_CASE(null_test) +)