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<>.
This commit is contained in:
Christopher Kohlhoff 2019-02-26 20:06:39 +11:00
parent 171a02ed0d
commit 490743a662
35 changed files with 1703 additions and 1463 deletions

View File

@ -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 \

View File

@ -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"

View File

@ -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 <experimental/coroutine>
#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 <typename> class awaitable_thread;
template <typename, typename> class awaitable_frame;
} // namespace detail
/// The return type of a coroutine or asynchronous operation.
template <typename T, typename Executor = executor>
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 <class U>
void await_suspend(
detail::coroutine_handle<detail::awaitable_frame<U, Executor>> h)
{
frame_->push_frame(&h.promise());
}
// Support for co_await keyword.
T await_resume()
{
return frame_->get();
}
#endif // !defined(GENERATING_DOCUMENTATION)
private:
template <typename> friend class detail::awaitable_thread;
template <typename, typename> 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<T, Executor>* a)
: frame_(a)
{
}
detail::awaitable_frame<T, Executor>* 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

View File

@ -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 <typename T>
struct awaitable_signature;
template <typename T, typename Executor>
struct awaitable_signature<awaitable<T, Executor>>
{
typedef void type(std::exception_ptr, T);
};
template <typename Executor>
struct awaitable_signature<awaitable<void, Executor>>
{
typedef void type(std::exception_ptr);
};
} // namespace detail
/// Spawn a new thread of execution.
/**
* The entry point function object @c f must have the signature:
*
* @code awaitable<void, E> f(); @endcode
*
* where @c E is convertible from @c Executor.
*/
template <typename Executor, typename F, typename CompletionToken>
ASIO_INITFN_RESULT_TYPE(CompletionToken,
typename detail::awaitable_signature<typename result_of<F()>::type>::type)
co_spawn(const Executor& ex, F&& f, CompletionToken&& token,
typename enable_if<
is_executor<Executor>::value
>::type* = 0);
/// Spawn a new thread of execution.
/**
* The entry point function object @c f must have the signature:
*
* @code awaitable<void, E> f(); @endcode
*
* where @c E is convertible from @c ExecutionContext::executor_type.
*/
template <typename ExecutionContext, typename F, typename CompletionToken>
ASIO_INITFN_RESULT_TYPE(CompletionToken,
typename detail::awaitable_signature<typename result_of<F()>::type>::type)
co_spawn(ExecutionContext& ctx, F&& f, CompletionToken&& token,
typename enable_if<
is_convertible<ExecutionContext&, execution_context&>::value
>::type* = 0);
} // namespace asio
#include "asio/detail/pop_options.hpp"
#include "asio/impl/co_spawn.hpp"
#endif // defined(ASIO_HAS_CO_AWAIT) || defined(GENERATING_DOCUMENTATION)
#endif // ASIO_CO_SPAWN_HPP

View File

@ -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

View File

@ -33,7 +33,7 @@ public:
enum { mem_index = 0 };
};
struct awaitee_tag
struct awaitable_frame_tag
{
enum { mem_index = 1 };
};

View File

@ -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

View File

@ -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 <experimental/coroutine>
#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 <typename> class awaiter;
template <typename> class awaitee_base;
template <typename, typename> class awaitee;
template <typename, typename> class await_handler_base;
template <typename Executor, typename F, typename CompletionToken>
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<void> 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 <typename Executor>
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 <typename> friend class detail::awaitee_base;
template <typename, typename> friend class detail::await_handler_base;
// Private constructor used by awaitee_base.
explicit await_token(detail::awaiter<Executor>* a)
: awaiter_(a)
{
}
detail::awaiter<Executor>* awaiter_;
};
/// The return type of a coroutine or asynchronous operation.
template <typename T, typename Executor = strand<executor>>
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<T, Executor>>::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<detail::awaiter<Executor>> h)
{
awaitee_->attach_caller(h);
}
// Support for co_await keyword.
template <class U>
void await_suspend(detail::coroutine_handle<detail::awaitee<U, Executor>> h)
{
awaitee_->attach_caller(h);
}
// Support for co_await keyword.
T await_resume()
{
return awaitee_->get();
}
#endif // !defined(GENERATING_DOCUMENTATION)
private:
template <typename, typename> friend class detail::awaitee;
template <typename, typename> 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<T, Executor>* a) : awaitee_(a) {}
detail::awaitee<T, Executor>* awaitee_;
};
/// Spawn a new thread of execution.
template <typename Executor, typename F, typename CompletionToken,
typename = typename enable_if<is_executor<Executor>::value>::type>
inline auto co_spawn(const Executor& ex, F&& f, CompletionToken&& token)
{
return detail::co_spawn(ex, std::forward<F>(f),
std::forward<CompletionToken>(token));
}
/// Spawn a new thread of execution.
template <typename ExecutionContext, typename F, typename CompletionToken,
typename = typename enable_if<
is_convertible<ExecutionContext&, execution_context&>::value>::type>
inline auto co_spawn(ExecutionContext& ctx, F&& f, CompletionToken&& token)
{
return detail::co_spawn(ctx.get_executor(), std::forward<F>(f),
std::forward<CompletionToken>(token));
}
/// Spawn a new thread of execution.
template <typename Executor, typename F, typename CompletionToken>
inline auto co_spawn(const await_token<Executor>& parent,
F&& f, CompletionToken&& token)
{
return detail::co_spawn(parent.get_executor(), std::forward<F>(f),
std::forward<CompletionToken>(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

View File

@ -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 <exception>
#include <functional>
#include <memory>
#include <new>
#include <tuple>
#include <utility>
#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 <typename Executor>
class awaiter
{
public:
struct deleter
{
void operator()(awaiter* a)
{
if (a)
a->release();
}
};
typedef std::unique_ptr<awaiter, deleter> ptr;
typedef Executor executor_type;
~awaiter()
{
if (has_executor_)
static_cast<Executor*>(static_cast<void*>(executor_))->~Executor();
}
void set_executor(const Executor& ex)
{
new (&executor_) Executor(ex);
has_executor_ = true;
}
executor_type get_executor() const noexcept
{
return *static_cast<const Executor*>(static_cast<const void*>(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<awaiter>::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 <typename Executor>
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<void>)
{
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<Executor>* top()
{
return awaiter_;
}
coroutine_handle<void> 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 <typename U, typename Ex>
void await_suspend(coroutine_handle<detail::awaitee<U, Ex>> 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 <typename U, typename Ex>
void await_suspend(coroutine_handle<detail::awaitee<U, Ex>> h) noexcept
{
this_->resume_on_attach_ = h;
}
await_token<Executor> await_resume()
{
return await_token<Executor>(this_->awaiter_);
}
private:
awaitee_base* this_;
};
awaitable_token await_transform(this_coro::token_t) noexcept
{
return awaitable_token(this);
}
template <typename T>
awaitable<T, Executor> await_transform(awaitable<T, Executor>& t) const
{
return std::move(t);
}
template <typename T>
awaitable<T, Executor> await_transform(awaitable<T, Executor>&& 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<awaiter<Executor>> h)
{
this->caller_ = h;
this->attach_callees(&h.promise());
}
template <typename U>
void attach_caller(coroutine_handle<awaitee<U, Executor>> h)
{
this->caller_ = h;
if (h.promise().awaiter_)
this->attach_callees(h.promise().awaiter_);
else
h.promise().unattached_callee_ = this;
}
void attach_callees(awaiter<Executor>* 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<Executor>* awaiter_ = nullptr;
coroutine_handle<void> caller_ = nullptr;
awaitee_base<Executor>* unattached_callee_ = nullptr;
std::exception_ptr pending_exception_ = nullptr;
coroutine_handle<void> resume_on_attach_ = nullptr;
bool ready_ = false;
};
// Promise object for coroutines further down the thread-of-execution "stack".
template <typename T, typename Executor>
class awaitee
: public awaitee_base<Executor>
{
public:
awaitee()
{
}
awaitee(awaitee&& other) noexcept
: awaitee_base<Executor>(std::move(other))
{
}
~awaitee()
{
if (has_result_)
static_cast<T*>(static_cast<void*>(result_))->~T();
}
awaitable<T, Executor> get_return_object()
{
return awaitable<T, Executor>(this);
};
template <typename U>
void return_value(U&& u)
{
new (&result_) T(std::forward<U>(u));
has_result_ = true;
}
T get()
{
this->caller_ = nullptr;
this->rethrow_exception();
return std::move(*static_cast<T*>(static_cast<void*>(result_)));
}
private:
alignas(T) unsigned char result_[sizeof(T)];
bool has_result_ = false;
};
// Promise object for coroutines further down the thread-of-execution "stack".
template <typename Executor>
class awaitee<void, Executor>
: public awaitee_base<Executor>
{
public:
awaitable<void, Executor> get_return_object()
{
return awaitable<void, Executor>(this);
};
void return_void()
{
}
void get()
{
this->caller_ = nullptr;
this->rethrow_exception();
}
};
template <typename Executor>
class awaiter_task
{
public:
typedef Executor executor_type;
awaiter_task(awaiter<Executor>* 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<Executor>::ptr(std::move(a));
});
}
}
executor_type get_executor() const noexcept
{
return awaiter_->get_executor();
}
protected:
typename awaiter<Executor>::ptr awaiter_;
};
template <typename Executor>
class co_spawn_handler : public awaiter_task<Executor>
{
public:
using awaiter_task<Executor>::awaiter_task;
void operator()()
{
typename awaiter<Executor>::ptr ptr(std::move(this->awaiter_));
coroutine_handle<awaiter<Executor>>::from_promise(*ptr.get()).resume();
}
};
template <typename Executor, typename T>
class await_handler_base : public awaiter_task<Executor>
{
public:
typedef awaitable<T, Executor> awaitable_type;
await_handler_base(await_token<Executor> token)
: awaiter_task<Executor>(token.awaiter_),
awaitee_(nullptr)
{
}
await_handler_base(await_handler_base&& other) noexcept
: awaiter_task<Executor>(std::move(other)),
awaitee_(std::exchange(other.awaitee_, nullptr))
{
}
void attach_awaitee(const awaitable<T, Executor>& a)
{
awaitee_ = a.awaitee_;
}
protected:
awaitee<T, Executor>* awaitee_;
};
template <typename, typename...> class await_handler;
template <typename Executor>
class await_handler<Executor, void>
: public await_handler_base<Executor, void>
{
public:
using await_handler_base<Executor, void>::await_handler_base;
void operator()()
{
typename awaiter<Executor>::ptr ptr(std::move(this->awaiter_));
this->awaitee_->return_void();
this->awaitee_->wake_caller();
ptr->rethrow_unhandled_exception();
}
};
template <typename Executor>
class await_handler<Executor, asio::error_code>
: public await_handler_base<Executor, void>
{
public:
typedef void return_type;
using await_handler_base<Executor, void>::await_handler_base;
void operator()(const asio::error_code& ec)
{
typename awaiter<Executor>::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 <typename Executor>
class await_handler<Executor, std::exception_ptr>
: public await_handler_base<Executor, void>
{
public:
using await_handler_base<Executor, void>::await_handler_base;
void operator()(std::exception_ptr ex)
{
typename awaiter<Executor>::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 <typename Executor, typename T>
class await_handler<Executor, T>
: public await_handler_base<Executor, T>
{
public:
using await_handler_base<Executor, T>::await_handler_base;
template <typename Arg>
void operator()(Arg&& arg)
{
typename awaiter<Executor>::ptr ptr(std::move(this->awaiter_));
this->awaitee_->return_value(std::forward<Arg>(arg));
this->awaitee_->wake_caller();
ptr->rethrow_unhandled_exception();
}
};
template <typename Executor, typename T>
class await_handler<Executor, asio::error_code, T>
: public await_handler_base<Executor, T>
{
public:
using await_handler_base<Executor, T>::await_handler_base;
template <typename Arg>
void operator()(const asio::error_code& ec, Arg&& arg)
{
typename awaiter<Executor>::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>(arg));
this->awaitee_->wake_caller();
ptr->rethrow_unhandled_exception();
}
};
template <typename Executor, typename T>
class await_handler<Executor, std::exception_ptr, T>
: public await_handler_base<Executor, T>
{
public:
using await_handler_base<Executor, T>::await_handler_base;
template <typename Arg>
void operator()(std::exception_ptr ex, Arg&& arg)
{
typename awaiter<Executor>::ptr ptr(std::move(this->awaiter_));
if (ex)
this->awaitee_->set_except(ex);
else
this->awaitee_->return_value(std::forward<Arg>(arg));
this->awaitee_->wake_caller();
ptr->rethrow_unhandled_exception();
}
};
template <typename Executor, typename... Ts>
class await_handler
: public await_handler_base<Executor, std::tuple<Ts...>>
{
public:
using await_handler_base<Executor, std::tuple<Ts...>>::await_handler_base;
template <typename... Args>
void operator()(Args&&... args)
{
typename awaiter<Executor>::ptr ptr(std::move(this->awaiter_));
this->awaitee_->return_value(
std::forward_as_tuple(std::forward<Args>(args)...));
this->awaitee_->wake_caller();
ptr->rethrow_unhandled_exception();
}
};
template <typename Executor, typename... Ts>
class await_handler<Executor, asio::error_code, Ts...>
: public await_handler_base<Executor, std::tuple<Ts...>>
{
public:
using await_handler_base<Executor, std::tuple<Ts...>>::await_handler_base;
template <typename... Args>
void operator()(const asio::error_code& ec, Args&&... args)
{
typename awaiter<Executor>::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>(args)...));
}
this->awaitee_->wake_caller();
ptr->rethrow_unhandled_exception();
}
};
template <typename Executor, typename... Ts>
class await_handler<Executor, std::exception_ptr, Ts...>
: public await_handler_base<Executor, std::tuple<Ts...>>
{
public:
using await_handler_base<Executor, std::tuple<Ts...>>::await_handler_base;
template <typename... Args>
void operator()(std::exception_ptr ex, Args&&... args)
{
typename awaiter<Executor>::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>(args)...));
}
this->awaitee_->wake_caller();
ptr->rethrow_unhandled_exception();
}
};
template <typename T>
struct awaitable_signature;
template <typename T, typename Executor>
struct awaitable_signature<awaitable<T, Executor>>
{
typedef void type(std::exception_ptr, T);
};
template <typename Executor>
struct awaitable_signature<awaitable<void, Executor>>
{
typedef void type(std::exception_ptr);
};
template <typename T, typename Executor, typename F, typename Handler>
awaiter<Executor>* co_spawn_entry_point(awaitable<T, Executor>*,
executor_work_guard<Executor> 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 <typename Executor, typename F, typename Handler>
awaiter<Executor>* co_spawn_entry_point(awaitable<void, Executor>*,
executor_work_guard<Executor> 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 <typename Executor, typename F, typename CompletionToken>
auto co_spawn(const Executor& ex, F&& f, CompletionToken&& token)
{
typedef typename result_of<F()>::type awaitable_type;
typedef typename awaitable_type::executor_type executor_type;
typedef typename awaitable_signature<awaitable_type>::type signature_type;
async_completion<CompletionToken, signature_type> completion(token);
executor_type ex2(ex);
auto work_guard = make_work_guard(completion.completion_handler, ex2);
auto* a = (co_spawn_entry_point)(
static_cast<awaitable_type*>(nullptr), std::move(work_guard),
std::forward<F>(f), std::move(completion.completion_handler));
a->set_executor(ex2);
(post)(co_spawn_handler<executor_type>(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 <typename T> T dummy_return()
{
return std::move(*static_cast<T*>(nullptr));
}
template <>
inline void dummy_return()
{
}
#endif // defined(_MSC_VER)
template <typename Awaitable>
inline Awaitable make_dummy_awaitable()
{
for (;;) co_await std::experimental::suspend_always();
#if defined(_MSC_VER)
co_return dummy_return<typename Awaitable::value_type>();
#endif // defined(_MSC_VER)
}
#if defined(_MSC_VER)
# pragma warning(pop)
#endif // defined(_MSC_VER)
} // namespace detail
} // namespace experimental
template <typename Executor, typename R, typename... Args>
class async_result<experimental::await_token<Executor>, R(Args...)>
{
public:
typedef experimental::detail::await_handler<
Executor, typename decay<Args>::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<return_type>())
{
h.attach_awaitee(awaitable_);
}
return_type get()
{
return std::move(awaitable_);
}
private:
return_type awaitable_;
};
} // namespace asio
namespace std { namespace experimental {
template <typename Executor, typename... Args>
struct coroutine_traits<
asio::experimental::detail::awaiter<Executor>*, Args...>
{
typedef asio::experimental::detail::awaiter<Executor> promise_type;
};
template <typename T, typename Executor, typename... Args>
struct coroutine_traits<
asio::experimental::awaitable<T, Executor>, Args...>
{
typedef asio::experimental::detail::awaitee<T, Executor> promise_type;
};
}} // namespace std::experimental
#include "asio/detail/pop_options.hpp"
#endif // ASIO_EXPERIMENTAL_IMPL_CO_SPAWN_HPP

View File

@ -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 <typename... Args>
void operator()(Args...)
{
}
#else // defined(ASIO_HAS_VARIADIC_TEMPLATES)
void operator()()
{
}
#define ASIO_PRIVATE_DETACHED_DEF(n) \
template <ASIO_VARIADIC_TPARAMS(n)> \
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 <typename Signature>
struct async_result<experimental::detached_t, Signature>
{
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

View File

@ -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 <exception>
#include <new>
#include <tuple>
#include <utility>
#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 <typename Executor>
class awaitable_frame_base
{
public:
#if !defined(ASIO_DISABLE_AWAITABLE_FRAME_RECYCLING)
void* operator new(std::size_t size)
{
return asio::detail::thread_info_base::allocate(
asio::detail::thread_info_base::awaitable_frame_tag(),
asio::detail::thread_context::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<void>) 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 <typename T>
auto await_transform(awaitable<T, Executor> 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<void>) 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 <typename Function>
auto await_transform(Function f,
typename enable_if<
is_convertible<
typename result_of<Function(awaitable_frame_base*)>::type,
awaitable_thread<Executor>*
>::value
>::type* = 0)
{
struct result
{
Function function_;
awaitable_frame_base* this_;
bool await_ready() const noexcept
{
return false;
}
void await_suspend(coroutine_handle<void>) noexcept
{
function_(this_);
}
void await_resume() const noexcept
{
}
};
return result{std::move(f), this};
}
void attach_thread(awaitable_thread<Executor>* handler) noexcept
{
attached_thread_ = handler;
}
awaitable_thread<Executor>* detach_thread() noexcept
{
return std::exchange(attached_thread_, nullptr);
}
void push_frame(awaitable_frame_base<Executor>* 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<void> coro_ = nullptr;
awaitable_thread<Executor>* attached_thread_ = nullptr;
awaitable_frame_base<Executor>* caller_ = nullptr;
std::exception_ptr pending_exception_ = nullptr;
};
template <typename T, typename Executor>
class awaitable_frame
: public awaitable_frame_base<Executor>
{
public:
awaitable_frame() noexcept
{
}
awaitable_frame(awaitable_frame&& other) noexcept
: awaitable_frame_base<Executor>(std::move(other))
{
}
~awaitable_frame()
{
if (has_result_)
static_cast<T*>(static_cast<void*>(result_))->~T();
}
awaitable<T, Executor> get_return_object() noexcept
{
this->coro_ = coroutine_handle<awaitable_frame>::from_promise(*this);
return awaitable<T, Executor>(this);
};
template <typename U>
void return_value(U&& u)
{
new (&result_) T(std::forward<U>(u));
has_result_ = true;
}
template <typename... Us>
void return_values(Us&&... us)
{
this->return_value(std::forward_as_tuple(std::forward<Us>(us)...));
}
T get()
{
this->caller_ = nullptr;
this->rethrow_exception();
return std::move(*static_cast<T*>(static_cast<void*>(result_)));
}
private:
alignas(T) unsigned char result_[sizeof(T)];
bool has_result_ = false;
};
template <typename Executor>
class awaitable_frame<void, Executor>
: public awaitable_frame_base<Executor>
{
public:
awaitable<void, Executor> get_return_object()
{
this->coro_ = coroutine_handle<awaitable_frame>::from_promise(*this);
return awaitable<void, Executor>(this);
};
void return_void()
{
}
void get()
{
this->caller_ = nullptr;
this->rethrow_exception();
}
};
template <typename Executor>
class awaitable_thread
{
public:
typedef Executor executor_type;
// Construct from the entry point of a new thread of execution.
awaitable_thread(awaitable<void, Executor> 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<void, Executor>(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 <typename> friend class awaitable_frame_base;
// Repeatedly resume the top stack frame until the stack is empty or until it
// has been transferred to another resumable_thread object.
void pump()
{
do top_of_stack_->resume(); while (top_of_stack_);
if (bottom_of_stack_.valid())
{
awaitable<void, Executor> a(std::move(bottom_of_stack_));
a.frame_->rethrow_exception();
}
}
awaitable<void, Executor> bottom_of_stack_;
awaitable_frame_base<Executor>* top_of_stack_;
executor_type executor_;
};
} // namespace detail
} // namespace asio
namespace std { namespace experimental {
template <typename T, typename Executor, typename... Args>
struct coroutine_traits<asio::awaitable<T, Executor>, Args...>
{
typedef asio::detail::awaitable_frame<T, Executor> promise_type;
};
}} // namespace std::experimental
#include "asio/detail/pop_options.hpp"
#endif // ASIO_IMPL_AWAITABLE_HPP

View File

@ -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 <typename T, typename Executor, typename F, typename Handler>
awaitable<void, Executor> co_spawn_entry_point(
awaitable<T, Executor>*, 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<Executor>{});
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 <typename Executor, typename F, typename Handler>
awaitable<void, Executor> co_spawn_entry_point(
awaitable<void, Executor>*, 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<Executor>{});
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 <typename Handler, typename Executor, typename F>
void operator()(Handler&& handler, const Executor& ex, F&& f) const
{
typedef typename result_of<F()>::type awaitable_type;
typedef typename awaitable_type::executor_type executor_type;
executor_type ex2(ex);
auto a = (co_spawn_entry_point)(static_cast<awaitable_type*>(nullptr),
ex2, std::forward<F>(f), std::forward<Handler>(handler));
awaitable_handler<executor_type, void>(std::move(a), ex2).launch();
}
};
} // namespace detail
template <typename Executor, typename F, typename CompletionToken>
inline ASIO_INITFN_RESULT_TYPE(CompletionToken,
typename detail::awaitable_signature<typename result_of<F()>::type>::type)
co_spawn(const Executor& ex, F&& f, CompletionToken&& token,
typename enable_if<
is_executor<Executor>::value
>::type*)
{
return async_initiate<CompletionToken,
typename detail::awaitable_signature<typename result_of<F()>::type>>(
detail::initiate_co_spawn(), token, ex, std::forward<F>(f));
}
template <typename ExecutionContext, typename F, typename CompletionToken>
inline ASIO_INITFN_RESULT_TYPE(CompletionToken,
typename detail::awaitable_signature<typename result_of<F()>::type>::type)
co_spawn(ExecutionContext& ctx, F&& f, CompletionToken&& token,
typename enable_if<
is_convertible<ExecutionContext&, execution_context&>::value
>::type*)
{
return (co_spawn)(ctx.get_executor(), std::forward<F>(f),
std::forward<CompletionToken>(token));
}
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_IMPL_CO_SPAWN_HPP

View File

@ -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 <typename... Args>
void operator()(Args...)
{
}
#else // defined(ASIO_HAS_VARIADIC_TEMPLATES)
void operator()()
{
}
#define ASIO_PRIVATE_DETACHED_DEF(n) \
template <ASIO_VARIADIC_TPARAMS(n)> \
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 <typename Signature>
struct async_result<detached_t, Signature>
{
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 <typename Initiation, typename RawCompletionToken, typename... Args>
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 <typename Initiation, typename RawCompletionToken>
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 <typename Initiation, typename RawCompletionToken, \
ASIO_VARIADIC_TPARAMS(n)> \
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

View File

@ -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 <typename Handler>
class redirect_error_handler
{
public:
typedef void result_type;
template <typename CompletionToken>
redirect_error_handler(redirect_error_t<CompletionToken> e)
: ec_(e.ec_),
@ -44,6 +45,14 @@ public:
{
}
template <typename RedirectedHandler>
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<R(const asio::error_code&)>
#endif // defined(ASIO_HAS_VARIADIC_TEMPLATES)
} // namespace detail
} // namespace experimental
#if !defined(GENERATING_DOCUMENTATION)
template <typename CompletionToken, typename Signature>
struct async_result<experimental::redirect_error_t<CompletionToken>, Signature>
: async_result<CompletionToken,
typename experimental::detail::redirect_error_signature<Signature>::type>
struct async_result<redirect_error_t<CompletionToken>, Signature>
{
typedef experimental::detail::redirect_error_handler<
typename async_result<CompletionToken,
typename experimental::detail::redirect_error_signature<Signature>::type>
::completion_handler_type> completion_handler_type;
typedef typename async_result<CompletionToken,
typename detail::redirect_error_signature<Signature>::type>
::return_type return_type;
explicit async_result(completion_handler_type& h)
: async_result<CompletionToken,
typename experimental::detail::redirect_error_signature<
Signature>::type>(h.handler_)
template <typename Initiation>
struct init_wrapper
{
template <typename Init>
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 <typename Handler, typename... Args>
void operator()(
ASIO_MOVE_ARG(Handler) handler,
ASIO_MOVE_ARG(Args)... args)
{
ASIO_MOVE_CAST(Initiation)(initiation_)(
detail::redirect_error_handler<
typename decay<Handler>::type>(
ec_, ASIO_MOVE_CAST(Handler)(handler)),
ASIO_MOVE_CAST(Args)(args)...);
}
#else // defined(ASIO_HAS_VARIADIC_TEMPLATES)
template <typename Handler>
void operator()(
ASIO_MOVE_ARG(Handler) handler)
{
ASIO_MOVE_CAST(Initiation)(initiation_)(
detail::redirect_error_handler<
typename decay<Handler>::type>(
ec_, ASIO_MOVE_CAST(Handler)(handler)));
}
#define ASIO_PRIVATE_INIT_WRAPPER_DEF(n) \
template <typename Handler, ASIO_VARIADIC_TPARAMS(n)> \
void operator()( \
ASIO_MOVE_ARG(Handler) handler, \
ASIO_VARIADIC_MOVE_PARAMS(n)) \
{ \
ASIO_MOVE_CAST(Initiation)(initiation_)( \
detail::redirect_error_handler< \
typename decay<Handler>::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 <typename Initiation, typename RawCompletionToken, typename... Args>
static return_type initiate(
ASIO_MOVE_ARG(Initiation) initiation,
ASIO_MOVE_ARG(RawCompletionToken) token,
ASIO_MOVE_ARG(Args)... args)
{
return async_initiate<CompletionToken,
typename detail::redirect_error_signature<Signature>::type>(
init_wrapper<typename decay<Initiation>::type>(
token.ec_, ASIO_MOVE_CAST(Initiation)(initiation)),
token.token_, ASIO_MOVE_CAST(Args)(args)...);
}
#else // defined(ASIO_HAS_VARIADIC_TEMPLATES)
template <typename Initiation, typename RawCompletionToken>
static return_type initiate(
ASIO_MOVE_ARG(Initiation) initiation,
ASIO_MOVE_ARG(RawCompletionToken) token)
{
return async_initiate<CompletionToken,
typename detail::redirect_error_signature<Signature>::type>(
init_wrapper<typename decay<Initiation>::type>(
token.ec_, ASIO_MOVE_CAST(Initiation)(initiation)),
token.token_);
}
#define ASIO_PRIVATE_INITIATE_DEF(n) \
template <typename Initiation, typename RawCompletionToken, \
ASIO_VARIADIC_TPARAMS(n)> \
static return_type initiate( \
ASIO_MOVE_ARG(Initiation) initiation, \
ASIO_MOVE_ARG(RawCompletionToken) token, \
ASIO_VARIADIC_MOVE_PARAMS(n)) \
{ \
return async_initiate<CompletionToken, \
typename detail::redirect_error_signature<Signature>::type>( \
init_wrapper<typename decay<Initiation>::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 <typename Handler, typename Executor>
struct associated_executor<
experimental::detail::redirect_error_handler<Handler>, Executor>
struct associated_executor<detail::redirect_error_handler<Handler>, Executor>
{
typedef typename associated_executor<Handler, Executor>::type type;
static type get(
const experimental::detail::redirect_error_handler<Handler>& h,
const detail::redirect_error_handler<Handler>& h,
const Executor& ex = Executor()) ASIO_NOEXCEPT
{
return associated_executor<Handler, Executor>::get(h.handler_, ex);
@ -247,13 +351,12 @@ struct associated_executor<
};
template <typename Handler, typename Allocator>
struct associated_allocator<
experimental::detail::redirect_error_handler<Handler>, Allocator>
struct associated_allocator<detail::redirect_error_handler<Handler>, Allocator>
{
typedef typename associated_allocator<Handler, Allocator>::type type;
static type get(
const experimental::detail::redirect_error_handler<Handler>& h,
const detail::redirect_error_handler<Handler>& h,
const Allocator& a = Allocator()) ASIO_NOEXCEPT
{
return associated_allocator<Handler, 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

View File

@ -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 <typename Executor, typename T>
class awaitable_handler_base
: public awaitable_thread<Executor>
{
public:
typedef void result_type;
typedef awaitable<T, Executor> awaitable_type;
// Construct from the entry point of a new thread of execution.
awaitable_handler_base(awaitable<void, Executor> a, const Executor& ex)
: awaitable_thread<Executor>(std::move(a), ex)
{
}
// Transfer ownership from another awaitable_thread.
explicit awaitable_handler_base(awaitable_thread<Executor>* h)
: awaitable_thread<Executor>(std::move(*h))
{
}
protected:
awaitable_frame<T, Executor>* frame() noexcept
{
return static_cast<awaitable_frame<T, Executor>*>(this->top_of_stack_);
}
};
template <typename, typename...>
class awaitable_handler;
template <typename Executor>
class awaitable_handler<Executor, void>
: public awaitable_handler_base<Executor, void>
{
public:
using awaitable_handler_base<Executor, void>::awaitable_handler_base;
void operator()()
{
this->frame()->attach_thread(this);
this->frame()->return_void();
this->frame()->pop_frame();
this->pump();
}
};
template <typename Executor>
class awaitable_handler<Executor, asio::error_code>
: public awaitable_handler_base<Executor, void>
{
public:
using awaitable_handler_base<Executor, void>::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 <typename Executor>
class awaitable_handler<Executor, std::exception_ptr>
: public awaitable_handler_base<Executor, void>
{
public:
using awaitable_handler_base<Executor, void>::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 <typename Executor, typename T>
class awaitable_handler<Executor, T>
: public awaitable_handler_base<Executor, T>
{
public:
using awaitable_handler_base<Executor, T>::awaitable_handler_base;
template <typename Arg>
void operator()(Arg&& arg)
{
this->frame()->attach_thread(this);
this->frame()->return_value(std::forward<Arg>(arg));
this->frame()->pop_frame();
this->pump();
}
};
template <typename Executor, typename T>
class awaitable_handler<Executor, asio::error_code, T>
: public awaitable_handler_base<Executor, T>
{
public:
using awaitable_handler_base<Executor, T>::awaitable_handler_base;
template <typename Arg>
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>(arg));
this->frame()->pop_frame();
this->pump();
}
};
template <typename Executor, typename T>
class awaitable_handler<Executor, std::exception_ptr, T>
: public awaitable_handler_base<Executor, T>
{
public:
using awaitable_handler_base<Executor, T>::awaitable_handler_base;
template <typename Arg>
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>(arg));
this->frame()->pop_frame();
this->pump();
}
};
template <typename Executor, typename... Ts>
class awaitable_handler
: public awaitable_handler_base<Executor, std::tuple<Ts...>>
{
public:
using awaitable_handler_base<Executor,
std::tuple<Ts...>>::awaitable_handler_base;
template <typename... Args>
void operator()(Args&&... args)
{
this->frame()->attach_thread(this);
this->frame()->return_values(std::forward<Args>(args)...);
this->frame()->pop_frame();
this->pump();
}
};
template <typename Executor, typename... Ts>
class awaitable_handler<Executor, asio::error_code, Ts...>
: public awaitable_handler_base<Executor, std::tuple<Ts...>>
{
public:
using awaitable_handler_base<Executor,
std::tuple<Ts...>>::awaitable_handler_base;
template <typename... Args>
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>(args)...);
this->frame()->pop_frame();
this->pump();
}
};
template <typename Executor, typename... Ts>
class awaitable_handler<Executor, std::exception_ptr, Ts...>
: public awaitable_handler_base<Executor, std::tuple<Ts...>>
{
public:
using awaitable_handler_base<Executor,
std::tuple<Ts...>>::awaitable_handler_base;
template <typename... Args>
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>(args)...);
this->frame()->pop_frame();
this->pump();
}
};
} // namespace detail
template <typename Executor, typename R, typename... Args>
class async_result<use_awaitable_t<Executor>, R(Args...)>
{
public:
typedef typename detail::awaitable_handler<
Executor, typename decay<Args>::type...> handler_type;
typedef typename handler_type::awaitable_type return_type;
#if defined(_MSC_VER)
template <typename T>
static T dummy_return()
{
return std::move(*static_cast<T*>(nullptr));
}
template <>
static void dummy_return()
{
}
#endif // defined(_MSC_VER)
template <typename Initiation, typename... InitArgs>
static return_type initiate(Initiation initiation,
use_awaitable_t<Executor>, InitArgs... args)
{
co_await [&](auto* frame)
{
handler_type handler(frame->detach_thread());
std::move(initiation)(std::move(handler), std::move(args)...);
return static_cast<handler_type*>(nullptr);
};
for (;;) {} // Never reached.
#if defined(_MSC_VER)
co_return dummy_return<typename return_type::value_type>();
#endif // defined(_MSC_VER)
}
};
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_IMPL_USE_AWAITABLE_HPP

View File

@ -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 <typename CompletionToken>
inline redirect_error_t<typename decay<CompletionToken>::type> redirect_error(
CompletionToken&& completion_token, asio::error_code& ec)
ASIO_MOVE_ARG(CompletionToken) completion_token, asio::error_code& ec)
{
return redirect_error_t<typename decay<CompletionToken>::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

View File

@ -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

View File

@ -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<void> 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 <typename Executor = executor>
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

View File

@ -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]

View File

@ -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<void> echo(tcp::socket socket)
asio::awaitable<void> 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<R>`],
asio.reference.awaitable `asio::awaitable<R>`],
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].

View File

@ -27,6 +27,7 @@
<bridgehead renderas="sect3">Classes</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="asio.reference.coroutine">coroutine</link></member>
<member><link linkend="asio.reference.detached_t">detached_t</link></member>
<member><link linkend="asio.reference.error_code">error_code</link></member>
<member><link linkend="asio.reference.execution_context">execution_context</link></member>
<member><link linkend="asio.reference.execution_context__id">execution_context::id</link></member>
@ -42,6 +43,7 @@
<member><link linkend="asio.reference.service_already_exists">service_already_exists</link></member>
<member><link linkend="asio.reference.system_error">system_error</link></member>
<member><link linkend="asio.reference.system_executor">system_executor</link></member>
<member><link linkend="asio.reference.this_coro__executor_t">this_coro::executor_t</link></member>
<member><link linkend="asio.reference.thread">thread</link></member>
<member><link linkend="asio.reference.thread_pool">thread_pool</link></member>
<member><link linkend="asio.reference.thread_pool__executor_type">thread_pool::executor_type</link></member>
@ -57,6 +59,7 @@
<member><link linkend="asio.reference.asio_handler_invoke">asio_handler_invoke</link></member>
<member><link linkend="asio.reference.asio_handler_is_continuation">asio_handler_is_continuation</link></member>
<member><link linkend="asio.reference.bind_executor">bind_executor</link></member>
<member><link linkend="asio.reference.co_spawn">co_spawn</link></member>
<member><link linkend="asio.reference.dispatch">dispatch</link></member>
<member><link linkend="asio.reference.defer">defer</link></member>
<member><link linkend="asio.reference.get_associated_allocator">get_associated_allocator</link></member>
@ -72,16 +75,21 @@
<bridgehead renderas="sect3">Class Templates</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="asio.reference.async_completion">async_completion</link></member>
<member><link linkend="asio.reference.awaitable">awaitable</link></member>
<member><link linkend="asio.reference.basic_io_object">basic_io_object</link></member>
<member><link linkend="asio.reference.basic_yield_context">basic_yield_context</link></member>
<member><link linkend="asio.reference.executor_binder">executor_binder</link></member>
<member><link linkend="asio.reference.executor_work_guard">executor_work_guard</link></member>
<member><link linkend="asio.reference.redirect_error_t">redirect_error_t</link></member>
<member><link linkend="asio.reference.strand">strand</link></member>
<member><link linkend="asio.reference.use_awaitable_t">use_awaitable_t</link></member>
<member><link linkend="asio.reference.use_future_t">use_future_t</link></member>
</simplelist>
<bridgehead renderas="sect3">Special Values</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="asio.reference.detached">detached</link></member>
<member><link linkend="asio.reference.executor_arg">executor_arg</link></member>
<member><link linkend="asio.reference.this_coro__executor">this_coro::executor</link></member>
<member><link linkend="asio.reference.use_future">use_future</link></member>
</simplelist>
<bridgehead renderas="sect3">Boost.Bind Placeholders</bridgehead>
@ -455,11 +463,10 @@
</row>
</tbody>
</tgroup>
<tgroup cols="4">
<tgroup cols="3">
<colspec colname="a"/>
<colspec colname="b"/>
<colspec colname="c"/>
<colspec colname="d"/>
<thead>
<row>
<entry valign="center" namest="a" nameend="b">
@ -468,9 +475,6 @@
<entry valign="center" namest="c" nameend="c">
<bridgehead renderas="sect2">Windows-specific</bridgehead>
</entry>
<entry valign="center" namest="d" nameend="d">
<bridgehead renderas="sect2">Experimental</bridgehead>
</entry>
</row>
</thead>
<tbody>
@ -511,30 +515,6 @@
<member><link linkend="asio.reference.windows__stream_handle">windows::stream_handle</link></member>
</simplelist>
</entry>
<entry valign="top">
<bridgehead renderas="sect3">Classes</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="asio.reference.experimental__detached_t">experimental::detached_t</link></member>
<member><link linkend="asio.reference.experimental__this_coro__executor_t">experimental::this_coro::executor_t</link></member>
<member><link linkend="asio.reference.experimental__this_coro__token_t">experimental::this_coro::token_t</link></member>
</simplelist>
<bridgehead renderas="sect3">Class Templates</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="asio.reference.experimental__awaitable">experimental::awaitable</link></member>
<member><link linkend="asio.reference.experimental__await_token">experimental::await_token</link></member>
<member><link linkend="asio.reference.experimental__redirect_error_t">experimental::redirect_error_t</link></member>
</simplelist>
<bridgehead renderas="sect3">Free Functions</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="asio.reference.experimental__co_spawn">experimental::co_spawn</link></member>
<member><link linkend="asio.reference.experimental__this_coro__executor">experimental::this_coro::executor</link></member>
<member><link linkend="asio.reference.experimental__this_coro__token">experimental::this_coro::token</link></member>
</simplelist>
<bridgehead renderas="sect3">Special Values</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="asio.reference.experimental__detached">experimental::detached</link></member>
</simplelist>
</entry>
</row>
</tbody>
</tgroup>

View File

@ -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

View File

@ -16,20 +16,24 @@
#include <set>
#include <string>
#include <utility>
#include <asio/experimental.hpp>
#include <asio/awaitable.hpp>
#include <asio/detached.hpp>
#include <asio/co_spawn.hpp>
#include <asio/io_context.hpp>
#include <asio/ip/tcp.hpp>
#include <asio/read_until.hpp>
#include <asio/redirect_error.hpp>
#include <asio/signal_set.hpp>
#include <asio/steady_timer.hpp>
#include <asio/use_awaitable.hpp>
#include <asio/write.hpp>
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<void> 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<void> 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<void> listener(tcp::acceptor acceptor)
{
auto token = co_await this_coro::token();
chat_room room;
for (;;)
{
std::make_shared<chat_session>(
co_await acceptor.async_accept(token),
co_await acceptor.async_accept(use_awaitable),
room
)->start();
}

View File

@ -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 <asio/experimental/co_spawn.hpp>
#include <asio/experimental/detached.hpp>
#include <asio/io_context.hpp>
#include <asio/ip/tcp.hpp>
#include <asio/signal_set.hpp>
#include <asio/write.hpp>
#include <cstdio>
using asio::ip::tcp;
using asio::experimental::co_spawn;
using asio::experimental::detached;
namespace this_coro = asio::experimental::this_coro;
template <typename T>
using awaitable = asio::experimental::awaitable<
T, asio::io_context::executor_type>;
awaitable<void> 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<void> 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());
}
}

View File

@ -8,8 +8,8 @@
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include <asio/experimental/co_spawn.hpp>
#include <asio/experimental/detached.hpp>
#include <asio/co_spawn.hpp>
#include <asio/detached.hpp>
#include <asio/io_context.hpp>
#include <asio/ip/tcp.hpp>
#include <asio/signal_set.hpp>
@ -17,25 +17,21 @@
#include <cstdio>
using asio::ip::tcp;
using asio::experimental::co_spawn;
using asio::experimental::detached;
namespace this_coro = asio::experimental::this_coro;
template <typename T>
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<void> 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<void> echo(tcp::socket socket)
awaitable<void> 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
{

View File

@ -8,8 +8,8 @@
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include <asio/experimental/co_spawn.hpp>
#include <asio/experimental/detached.hpp>
#include <asio/co_spawn.hpp>
#include <asio/detached.hpp>
#include <asio/io_context.hpp>
#include <asio/ip/tcp.hpp>
#include <asio/signal_set.hpp>
@ -17,10 +17,10 @@
#include <cstdio>
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<void> 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<connection_iter> 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<void> 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);
}
}

View File

@ -8,8 +8,8 @@
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include <asio/experimental/co_spawn.hpp>
#include <asio/experimental/detached.hpp>
#include <asio/co_spawn.hpp>
#include <asio/detached.hpp>
#include <asio/io_context.hpp>
#include <asio/ip/tcp.hpp>
#include <asio/signal_set.hpp>
@ -17,21 +17,17 @@
#include <cstdio>
using asio::ip::tcp;
using asio::experimental::co_spawn;
using asio::experimental::detached;
namespace this_coro = asio::experimental::this_coro;
template <typename T>
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<void> 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<void> echo(tcp::socket socket)
@ -55,13 +51,11 @@ awaitable<void> echo(tcp::socket socket)
awaitable<void> 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
{

View File

@ -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

View File

@ -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

View File

@ -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)
)

View File

@ -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)
)

View File

@ -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)
)

View File

@ -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)
)

View File

@ -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)
)

View File

@ -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)
)