Initial Commit

This commit is contained in:
2025-11-06 15:51:56 +01:00
commit 0f7191ad54
648 changed files with 170981 additions and 0 deletions

View File

@@ -0,0 +1,132 @@
//
// experimental/as_single.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2025 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_AS_SINGLE_HPP
#define ASIO_EXPERIMENTAL_AS_SINGLE_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/type_traits.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
namespace experimental {
/// A @ref completion_token adapter used to specify that the completion handler
/// arguments should be combined into a single argument.
/**
* The as_single_t class is used to indicate that any arguments to the
* completion handler should be combined and passed as a single argument.
* If there is already one argument, that argument is passed as-is. If
* there is more than argument, the arguments are first moved into a
* @c std::tuple and that tuple is then passed to the completion handler.
*/
template <typename CompletionToken>
class as_single_t
{
public:
/// Tag type used to prevent the "default" constructor from being used for
/// conversions.
struct default_constructor_tag {};
/// Default constructor.
/**
* This constructor is only valid if the underlying completion token is
* default constructible and move constructible. The underlying completion
* token is itself defaulted as an argument to allow it to capture a source
* location.
*/
constexpr as_single_t(
default_constructor_tag = default_constructor_tag(),
CompletionToken token = CompletionToken())
: token_(static_cast<CompletionToken&&>(token))
{
}
/// Constructor.
template <typename T>
constexpr explicit as_single_t(
T&& completion_token)
: token_(static_cast<T&&>(completion_token))
{
}
/// Adapts an executor to add the @c as_single_t completion token as the
/// default.
template <typename InnerExecutor>
struct executor_with_default : InnerExecutor
{
/// Specify @c as_single_t as the default completion token type.
typedef as_single_t default_completion_token_type;
/// Construct the adapted executor from the inner executor type.
executor_with_default(const InnerExecutor& ex) noexcept
: InnerExecutor(ex)
{
}
/// Convert the specified executor to the inner executor type, then use
/// that to construct the adapted executor.
template <typename OtherExecutor>
executor_with_default(const OtherExecutor& ex,
constraint_t<
is_convertible<OtherExecutor, InnerExecutor>::value
> = 0) noexcept
: InnerExecutor(ex)
{
}
};
/// Type alias to adapt an I/O object to use @c as_single_t as its
/// default completion token type.
template <typename T>
using as_default_on_t = typename T::template rebind_executor<
executor_with_default<typename T::executor_type>>::other;
/// Function helper to adapt an I/O object to use @c as_single_t as its
/// default completion token type.
template <typename T>
static typename decay_t<T>::template rebind_executor<
executor_with_default<typename decay_t<T>::executor_type>
>::other
as_default_on(T&& object)
{
return typename decay_t<T>::template rebind_executor<
executor_with_default<typename decay_t<T>::executor_type>
>::other(static_cast<T&&>(object));
}
//private:
CompletionToken token_;
};
/// Adapt a @ref completion_token to specify that the completion handler
/// arguments should be combined into a single argument.
template <typename CompletionToken>
ASIO_NODISCARD inline
constexpr as_single_t<decay_t<CompletionToken>>
as_single(CompletionToken&& completion_token)
{
return as_single_t<decay_t<CompletionToken>>(
static_cast<CompletionToken&&>(completion_token));
}
} // namespace experimental
} // namespace asio
#include "asio/detail/pop_options.hpp"
#include "asio/experimental/impl/as_single.hpp"
#endif // ASIO_EXPERIMENTAL_AS_SINGLE_HPP

View File

@@ -0,0 +1,536 @@
//
// experimental/awaitable_operators.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2025 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_AWAITABLE_OPERATORS_HPP
#define ASIO_EXPERIMENTAL_AWAITABLE_OPERATORS_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/config.hpp"
#include <optional>
#include <stdexcept>
#include <tuple>
#include <variant>
#include "asio/awaitable.hpp"
#include "asio/co_spawn.hpp"
#include "asio/deferred.hpp"
#include "asio/detail/type_traits.hpp"
#include "asio/experimental/parallel_group.hpp"
#include "asio/multiple_exceptions.hpp"
#include "asio/this_coro.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
namespace experimental {
namespace awaitable_operators {
namespace detail {
template <typename T, typename Executor>
awaitable<T, Executor> awaitable_wrap(awaitable<T, Executor> a,
constraint_t<is_constructible<T>::value>* = 0)
{
return a;
}
template <typename T, typename Executor>
awaitable<std::optional<T>, Executor> awaitable_wrap(awaitable<T, Executor> a,
constraint_t<!is_constructible<T>::value>* = 0)
{
co_return std::optional<T>(co_await std::move(a));
}
template <typename T>
T& awaitable_unwrap(conditional_t<true, T, void>& r,
constraint_t<is_constructible<T>::value>* = 0)
{
return r;
}
template <typename T>
T& awaitable_unwrap(std::optional<conditional_t<true, T, void>>& r,
constraint_t<!is_constructible<T>::value>* = 0)
{
return *r;
}
} // namespace detail
/// Wait for both operations to succeed.
/**
* If one operations fails, the other is cancelled as the AND-condition can no
* longer be satisfied.
*/
template <typename Executor>
awaitable<void, Executor> operator&&(
awaitable<void, Executor> t, awaitable<void, Executor> u)
{
auto ex = co_await this_coro::executor;
auto [order, ex0, ex1] =
co_await make_parallel_group(
co_spawn(ex, std::move(t), deferred),
co_spawn(ex, std::move(u), deferred)
).async_wait(
wait_for_one_error(),
deferred
);
if (ex0 && ex1)
throw multiple_exceptions(ex0);
if (ex0)
std::rethrow_exception(ex0);
if (ex1)
std::rethrow_exception(ex1);
co_return;
}
/// Wait for both operations to succeed.
/**
* If one operations fails, the other is cancelled as the AND-condition can no
* longer be satisfied.
*/
template <typename U, typename Executor>
awaitable<U, Executor> operator&&(
awaitable<void, Executor> t, awaitable<U, Executor> u)
{
auto ex = co_await this_coro::executor;
auto [order, ex0, ex1, r1] =
co_await make_parallel_group(
co_spawn(ex, std::move(t), deferred),
co_spawn(ex, detail::awaitable_wrap(std::move(u)), deferred)
).async_wait(
wait_for_one_error(),
deferred
);
if (ex0 && ex1)
throw multiple_exceptions(ex0);
if (ex0)
std::rethrow_exception(ex0);
if (ex1)
std::rethrow_exception(ex1);
co_return std::move(detail::awaitable_unwrap<U>(r1));
}
/// Wait for both operations to succeed.
/**
* If one operations fails, the other is cancelled as the AND-condition can no
* longer be satisfied.
*/
template <typename T, typename Executor>
awaitable<T, Executor> operator&&(
awaitable<T, Executor> t, awaitable<void, Executor> u)
{
auto ex = co_await this_coro::executor;
auto [order, ex0, r0, ex1] =
co_await make_parallel_group(
co_spawn(ex, detail::awaitable_wrap(std::move(t)), deferred),
co_spawn(ex, std::move(u), deferred)
).async_wait(
wait_for_one_error(),
deferred
);
if (ex0 && ex1)
throw multiple_exceptions(ex0);
if (ex0)
std::rethrow_exception(ex0);
if (ex1)
std::rethrow_exception(ex1);
co_return std::move(detail::awaitable_unwrap<T>(r0));
}
/// Wait for both operations to succeed.
/**
* If one operations fails, the other is cancelled as the AND-condition can no
* longer be satisfied.
*/
template <typename T, typename U, typename Executor>
awaitable<std::tuple<T, U>, Executor> operator&&(
awaitable<T, Executor> t, awaitable<U, Executor> u)
{
auto ex = co_await this_coro::executor;
auto [order, ex0, r0, ex1, r1] =
co_await make_parallel_group(
co_spawn(ex, detail::awaitable_wrap(std::move(t)), deferred),
co_spawn(ex, detail::awaitable_wrap(std::move(u)), deferred)
).async_wait(
wait_for_one_error(),
deferred
);
if (ex0 && ex1)
throw multiple_exceptions(ex0);
if (ex0)
std::rethrow_exception(ex0);
if (ex1)
std::rethrow_exception(ex1);
co_return std::make_tuple(
std::move(detail::awaitable_unwrap<T>(r0)),
std::move(detail::awaitable_unwrap<U>(r1)));
}
/// Wait for both operations to succeed.
/**
* If one operations fails, the other is cancelled as the AND-condition can no
* longer be satisfied.
*/
template <typename... T, typename Executor>
awaitable<std::tuple<T..., std::monostate>, Executor> operator&&(
awaitable<std::tuple<T...>, Executor> t, awaitable<void, Executor> u)
{
auto ex = co_await this_coro::executor;
auto [order, ex0, r0, ex1, r1] =
co_await make_parallel_group(
co_spawn(ex, detail::awaitable_wrap(std::move(t)), deferred),
co_spawn(ex, std::move(u), deferred)
).async_wait(
wait_for_one_error(),
deferred
);
if (ex0 && ex1)
throw multiple_exceptions(ex0);
if (ex0)
std::rethrow_exception(ex0);
if (ex1)
std::rethrow_exception(ex1);
co_return std::move(detail::awaitable_unwrap<std::tuple<T...>>(r0));
}
/// Wait for both operations to succeed.
/**
* If one operations fails, the other is cancelled as the AND-condition can no
* longer be satisfied.
*/
template <typename... T, typename U, typename Executor>
awaitable<std::tuple<T..., U>, Executor> operator&&(
awaitable<std::tuple<T...>, Executor> t, awaitable<U, Executor> u)
{
auto ex = co_await this_coro::executor;
auto [order, ex0, r0, ex1, r1] =
co_await make_parallel_group(
co_spawn(ex, detail::awaitable_wrap(std::move(t)), deferred),
co_spawn(ex, detail::awaitable_wrap(std::move(u)), deferred)
).async_wait(
wait_for_one_error(),
deferred
);
if (ex0 && ex1)
throw multiple_exceptions(ex0);
if (ex0)
std::rethrow_exception(ex0);
if (ex1)
std::rethrow_exception(ex1);
co_return std::tuple_cat(
std::move(detail::awaitable_unwrap<std::tuple<T...>>(r0)),
std::make_tuple(std::move(detail::awaitable_unwrap<U>(r1))));
}
/// Wait for one operation to succeed.
/**
* If one operations succeeds, the other is cancelled as the OR-condition is
* already satisfied.
*/
template <typename Executor>
awaitable<std::variant<std::monostate, std::monostate>, Executor> operator||(
awaitable<void, Executor> t, awaitable<void, Executor> u)
{
auto ex = co_await this_coro::executor;
auto [order, ex0, ex1] =
co_await make_parallel_group(
co_spawn(ex, std::move(t), deferred),
co_spawn(ex, std::move(u), deferred)
).async_wait(
wait_for_one_success(),
deferred
);
if (order[0] == 0)
{
if (!ex0)
co_return std::variant<std::monostate, std::monostate>{
std::in_place_index<0>};
if (!ex1)
co_return std::variant<std::monostate, std::monostate>{
std::in_place_index<1>};
throw multiple_exceptions(ex0);
}
else
{
if (!ex1)
co_return std::variant<std::monostate, std::monostate>{
std::in_place_index<1>};
if (!ex0)
co_return std::variant<std::monostate, std::monostate>{
std::in_place_index<0>};
throw multiple_exceptions(ex1);
}
}
/// Wait for one operation to succeed.
/**
* If one operations succeeds, the other is cancelled as the OR-condition is
* already satisfied.
*/
template <typename U, typename Executor>
awaitable<std::variant<std::monostate, U>, Executor> operator||(
awaitable<void, Executor> t, awaitable<U, Executor> u)
{
auto ex = co_await this_coro::executor;
auto [order, ex0, ex1, r1] =
co_await make_parallel_group(
co_spawn(ex, std::move(t), deferred),
co_spawn(ex, detail::awaitable_wrap(std::move(u)), deferred)
).async_wait(
wait_for_one_success(),
deferred
);
if (order[0] == 0)
{
if (!ex0)
co_return std::variant<std::monostate, U>{
std::in_place_index<0>};
if (!ex1)
co_return std::variant<std::monostate, U>{
std::in_place_index<1>,
std::move(detail::awaitable_unwrap<U>(r1))};
throw multiple_exceptions(ex0);
}
else
{
if (!ex1)
co_return std::variant<std::monostate, U>{
std::in_place_index<1>,
std::move(detail::awaitable_unwrap<U>(r1))};
if (!ex0)
co_return std::variant<std::monostate, U>{
std::in_place_index<0>};
throw multiple_exceptions(ex1);
}
}
/// Wait for one operation to succeed.
/**
* If one operations succeeds, the other is cancelled as the OR-condition is
* already satisfied.
*/
template <typename T, typename Executor>
awaitable<std::variant<T, std::monostate>, Executor> operator||(
awaitable<T, Executor> t, awaitable<void, Executor> u)
{
auto ex = co_await this_coro::executor;
auto [order, ex0, r0, ex1] =
co_await make_parallel_group(
co_spawn(ex, detail::awaitable_wrap(std::move(t)), deferred),
co_spawn(ex, std::move(u), deferred)
).async_wait(
wait_for_one_success(),
deferred
);
if (order[0] == 0)
{
if (!ex0)
co_return std::variant<T, std::monostate>{
std::in_place_index<0>,
std::move(detail::awaitable_unwrap<T>(r0))};
if (!ex1)
co_return std::variant<T, std::monostate>{
std::in_place_index<1>};
throw multiple_exceptions(ex0);
}
else
{
if (!ex1)
co_return std::variant<T, std::monostate>{
std::in_place_index<1>};
if (!ex0)
co_return std::variant<T, std::monostate>{
std::in_place_index<0>,
std::move(detail::awaitable_unwrap<T>(r0))};
throw multiple_exceptions(ex1);
}
}
/// Wait for one operation to succeed.
/**
* If one operations succeeds, the other is cancelled as the OR-condition is
* already satisfied.
*/
template <typename T, typename U, typename Executor>
awaitable<std::variant<T, U>, Executor> operator||(
awaitable<T, Executor> t, awaitable<U, Executor> u)
{
auto ex = co_await this_coro::executor;
auto [order, ex0, r0, ex1, r1] =
co_await make_parallel_group(
co_spawn(ex, detail::awaitable_wrap(std::move(t)), deferred),
co_spawn(ex, detail::awaitable_wrap(std::move(u)), deferred)
).async_wait(
wait_for_one_success(),
deferred
);
if (order[0] == 0)
{
if (!ex0)
co_return std::variant<T, U>{
std::in_place_index<0>,
std::move(detail::awaitable_unwrap<T>(r0))};
if (!ex1)
co_return std::variant<T, U>{
std::in_place_index<1>,
std::move(detail::awaitable_unwrap<U>(r1))};
throw multiple_exceptions(ex0);
}
else
{
if (!ex1)
co_return std::variant<T, U>{
std::in_place_index<1>,
std::move(detail::awaitable_unwrap<U>(r1))};
if (!ex0)
co_return std::variant<T, U>{
std::in_place_index<0>,
std::move(detail::awaitable_unwrap<T>(r0))};
throw multiple_exceptions(ex1);
}
}
namespace detail {
template <typename... T>
struct widen_variant
{
template <std::size_t I, typename SourceVariant>
static std::variant<T...> call(SourceVariant& source)
{
if (source.index() == I)
return std::variant<T...>{
std::in_place_index<I>, std::move(std::get<I>(source))};
else if constexpr (I + 1 < std::variant_size_v<SourceVariant>)
return call<I + 1>(source);
else
throw std::logic_error("empty variant");
}
};
} // namespace detail
/// Wait for one operation to succeed.
/**
* If one operations succeeds, the other is cancelled as the OR-condition is
* already satisfied.
*/
template <typename... T, typename Executor>
awaitable<std::variant<T..., std::monostate>, Executor> operator||(
awaitable<std::variant<T...>, Executor> t, awaitable<void, Executor> u)
{
auto ex = co_await this_coro::executor;
auto [order, ex0, r0, ex1] =
co_await make_parallel_group(
co_spawn(ex, detail::awaitable_wrap(std::move(t)), deferred),
co_spawn(ex, std::move(u), deferred)
).async_wait(
wait_for_one_success(),
deferred
);
using widen = detail::widen_variant<T..., std::monostate>;
if (order[0] == 0)
{
if (!ex0)
co_return widen::template call<0>(
detail::awaitable_unwrap<std::variant<T...>>(r0));
if (!ex1)
co_return std::variant<T..., std::monostate>{
std::in_place_index<sizeof...(T)>};
throw multiple_exceptions(ex0);
}
else
{
if (!ex1)
co_return std::variant<T..., std::monostate>{
std::in_place_index<sizeof...(T)>};
if (!ex0)
co_return widen::template call<0>(
detail::awaitable_unwrap<std::variant<T...>>(r0));
throw multiple_exceptions(ex1);
}
}
/// Wait for one operation to succeed.
/**
* If one operations succeeds, the other is cancelled as the OR-condition is
* already satisfied.
*/
template <typename... T, typename U, typename Executor>
awaitable<std::variant<T..., U>, Executor> operator||(
awaitable<std::variant<T...>, Executor> t, awaitable<U, Executor> u)
{
auto ex = co_await this_coro::executor;
auto [order, ex0, r0, ex1, r1] =
co_await make_parallel_group(
co_spawn(ex, detail::awaitable_wrap(std::move(t)), deferred),
co_spawn(ex, detail::awaitable_wrap(std::move(u)), deferred)
).async_wait(
wait_for_one_success(),
deferred
);
using widen = detail::widen_variant<T..., U>;
if (order[0] == 0)
{
if (!ex0)
co_return widen::template call<0>(
detail::awaitable_unwrap<std::variant<T...>>(r0));
if (!ex1)
co_return std::variant<T..., U>{
std::in_place_index<sizeof...(T)>,
std::move(detail::awaitable_unwrap<U>(r1))};
throw multiple_exceptions(ex0);
}
else
{
if (!ex1)
co_return std::variant<T..., U>{
std::in_place_index<sizeof...(T)>,
std::move(detail::awaitable_unwrap<U>(r1))};
if (!ex0)
co_return widen::template call<0>(
detail::awaitable_unwrap<std::variant<T...>>(r0));
throw multiple_exceptions(ex1);
}
}
} // namespace awaitable_operators
} // namespace experimental
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_EXPERIMENTAL_AWAITABLE_OPERATORS_HPP

View File

@@ -0,0 +1,514 @@
//
// experimental/basic_channel.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2025 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_BASIC_CHANNEL_HPP
#define ASIO_EXPERIMENTAL_BASIC_CHANNEL_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/non_const_lvalue.hpp"
#include "asio/detail/null_mutex.hpp"
#include "asio/execution/executor.hpp"
#include "asio/execution_context.hpp"
#include "asio/experimental/detail/channel_send_functions.hpp"
#include "asio/experimental/detail/channel_service.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
namespace experimental {
namespace detail {
} // namespace detail
/// A channel for messages.
/**
* The basic_channel class template is used for sending messages between
* different parts of the same application. A <em>message</em> is defined as a
* collection of arguments to be passed to a completion handler, and the set of
* messages supported by a channel is specified by its @c Traits and
* <tt>Signatures...</tt> template parameters. Messages may be sent and received
* using asynchronous or non-blocking synchronous operations.
*
* Unless customising the traits, applications will typically use the @c
* experimental::channel alias template. For example:
* @code void send_loop(int i, steady_timer& timer,
* channel<void(error_code, int)>& ch)
* {
* if (i < 10)
* {
* timer.expires_after(chrono::seconds(1));
* timer.async_wait(
* [i, &timer, &ch](error_code error)
* {
* if (!error)
* {
* ch.async_send(error_code(), i,
* [i, &timer, &ch](error_code error)
* {
* if (!error)
* {
* send_loop(i + 1, timer, ch);
* }
* });
* }
* });
* }
* else
* {
* ch.close();
* }
* }
*
* void receive_loop(channel<void(error_code, int)>& ch)
* {
* ch.async_receive(
* [&ch](error_code error, int i)
* {
* if (!error)
* {
* std::cout << "Received " << i << "\n";
* receive_loop(ch);
* }
* });
* } @endcode
*
* @par Thread Safety
* @e Distinct @e objects: Safe.@n
* @e Shared @e objects: Unsafe.
*
* The basic_channel class template is not thread-safe, and would typically be
* used for passing messages between application code that runs on the same
* thread or in the same strand. Consider using @ref basic_concurrent_channel,
* and its alias template @c experimental::concurrent_channel, to pass messages
* between code running in different threads.
*/
template <typename Executor, typename Traits, typename... Signatures>
class basic_channel
#if !defined(GENERATING_DOCUMENTATION)
: public detail::channel_send_functions<
basic_channel<Executor, Traits, Signatures...>,
Executor, Signatures...>
#endif // !defined(GENERATING_DOCUMENTATION)
{
private:
class initiate_async_send;
class initiate_async_receive;
typedef detail::channel_service<asio::detail::null_mutex> service_type;
typedef typename service_type::template implementation_type<
Traits, Signatures...>::payload_type payload_type;
template <typename... PayloadSignatures,
ASIO_COMPLETION_TOKEN_FOR(PayloadSignatures...) CompletionToken>
auto do_async_receive(
asio::detail::completion_payload<PayloadSignatures...>*,
CompletionToken&& token)
-> decltype(
async_initiate<CompletionToken, PayloadSignatures...>(
declval<initiate_async_receive>(), token))
{
return async_initiate<CompletionToken, PayloadSignatures...>(
initiate_async_receive(this), token);
}
public:
/// The type of the executor associated with the channel.
typedef Executor executor_type;
/// Rebinds the channel type to another executor.
template <typename Executor1>
struct rebind_executor
{
/// The channel type when rebound to the specified executor.
typedef basic_channel<Executor1, Traits, Signatures...> other;
};
/// The traits type associated with the channel.
typedef typename Traits::template rebind<Signatures...>::other traits_type;
/// Construct a basic_channel.
/**
* This constructor creates and channel.
*
* @param ex The I/O executor that the channel will use, by default, to
* dispatch handlers for any asynchronous operations performed on the channel.
*
* @param max_buffer_size The maximum number of messages that may be buffered
* in the channel.
*/
basic_channel(const executor_type& ex, std::size_t max_buffer_size = 0)
: service_(&asio::use_service<service_type>(
basic_channel::get_context(ex))),
impl_(),
executor_(ex)
{
service_->construct(impl_, max_buffer_size);
}
/// Construct and open a basic_channel.
/**
* This constructor creates and opens a channel.
*
* @param context An execution context which provides the I/O executor that
* the channel will use, by default, to dispatch handlers for any asynchronous
* operations performed on the channel.
*
* @param max_buffer_size The maximum number of messages that may be buffered
* in the channel.
*/
template <typename ExecutionContext>
basic_channel(ExecutionContext& context, std::size_t max_buffer_size = 0,
constraint_t<
is_convertible<ExecutionContext&, execution_context&>::value,
defaulted_constraint
> = defaulted_constraint())
: service_(&asio::use_service<service_type>(context)),
impl_(),
executor_(context.get_executor())
{
service_->construct(impl_, max_buffer_size);
}
/// Move-construct a basic_channel from another.
/**
* This constructor moves a channel from one object to another.
*
* @param other The other basic_channel object from which the move will occur.
*
* @note Following the move, the moved-from object is in the same state as if
* constructed using the @c basic_channel(const executor_type&) constructor.
*/
basic_channel(basic_channel&& other)
: service_(other.service_),
executor_(other.executor_)
{
service_->move_construct(impl_, other.impl_);
}
/// Move-assign a basic_channel from another.
/**
* This assignment operator moves a channel from one object to another.
* Cancels any outstanding asynchronous operations associated with the target
* object.
*
* @param other The other basic_channel object from which the move will occur.
*
* @note Following the move, the moved-from object is in the same state as if
* constructed using the @c basic_channel(const executor_type&)
* constructor.
*/
basic_channel& operator=(basic_channel&& other)
{
if (this != &other)
{
service_->move_assign(impl_, *other.service_, other.impl_);
executor_.~executor_type();
new (&executor_) executor_type(other.executor_);
service_ = other.service_;
}
return *this;
}
// All channels have access to each other's implementations.
template <typename, typename, typename...>
friend class basic_channel;
/// Move-construct a basic_channel from another.
/**
* This constructor moves a channel from one object to another.
*
* @param other The other basic_channel object from which the move will occur.
*
* @note Following the move, the moved-from object is in the same state as if
* constructed using the @c basic_channel(const executor_type&)
* constructor.
*/
template <typename Executor1>
basic_channel(
basic_channel<Executor1, Traits, Signatures...>&& other,
constraint_t<
is_convertible<Executor1, Executor>::value
> = 0)
: service_(other.service_),
executor_(other.executor_)
{
service_->move_construct(impl_, other.impl_);
}
/// Move-assign a basic_channel from another.
/**
* This assignment operator moves a channel from one object to another.
* Cancels any outstanding asynchronous operations associated with the target
* object.
*
* @param other The other basic_channel object from which the move will
* occur.
*
* @note Following the move, the moved-from object is in the same state as if
* constructed using the @c basic_channel(const executor_type&)
* constructor.
*/
template <typename Executor1>
constraint_t<
is_convertible<Executor1, Executor>::value,
basic_channel&
> operator=(basic_channel<Executor1, Traits, Signatures...>&& other)
{
if (this != &other)
{
service_->move_assign(impl_, *other.service_, other.impl_);
executor_.~executor_type();
new (&executor_) executor_type(other.executor_);
service_ = other.service_;
}
return *this;
}
/// Destructor.
~basic_channel()
{
service_->destroy(impl_);
}
/// Get the executor associated with the object.
const executor_type& get_executor() noexcept
{
return executor_;
}
/// Get the capacity of the channel's buffer.
std::size_t capacity() noexcept
{
return service_->capacity(impl_);
}
/// Determine whether the channel is open.
bool is_open() const noexcept
{
return service_->is_open(impl_);
}
/// Reset the channel to its initial state.
void reset()
{
service_->reset(impl_);
}
/// Close the channel.
void close()
{
service_->close(impl_);
}
/// Cancel all asynchronous operations waiting on the channel.
/**
* All outstanding send operations will complete with the error
* @c asio::experimental::error::channel_cancelled. Outstanding receive
* operations complete with the result as determined by the channel traits.
*/
void cancel()
{
service_->cancel(impl_);
}
/// Determine whether a message can be received without blocking.
bool ready() const noexcept
{
return service_->ready(impl_);
}
#if defined(GENERATING_DOCUMENTATION)
/// Try to send a message without blocking.
/**
* Fails if the buffer is full and there are no waiting receive operations.
*
* @returns @c true on success, @c false on failure.
*/
template <typename... Args>
bool try_send(Args&&... args);
/// Try to send a message without blocking, using dispatch semantics to call
/// the receive operation's completion handler.
/**
* Fails if the buffer is full and there are no waiting receive operations.
*
* The receive operation's completion handler may be called from inside this
* function.
*
* @returns @c true on success, @c false on failure.
*/
template <typename... Args>
bool try_send_via_dispatch(Args&&... args);
/// Try to send a number of messages without blocking.
/**
* @returns The number of messages that were sent.
*/
template <typename... Args>
std::size_t try_send_n(std::size_t count, Args&&... args);
/// Try to send a number of messages without blocking, using dispatch
/// semantics to call the receive operations' completion handlers.
/**
* The receive operations' completion handlers may be called from inside this
* function.
*
* @returns The number of messages that were sent.
*/
template <typename... Args>
std::size_t try_send_n_via_dispatch(std::size_t count, Args&&... args);
/// Asynchronously send a message.
/**
* @par Completion Signature
* @code void(asio::error_code) @endcode
*/
template <typename... Args,
ASIO_COMPLETION_TOKEN_FOR(void (asio::error_code))
CompletionToken ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)>
auto async_send(Args&&... args,
CompletionToken&& token);
#endif // defined(GENERATING_DOCUMENTATION)
/// Try to receive a message without blocking.
/**
* Fails if the buffer is full and there are no waiting receive operations.
*
* @returns @c true on success, @c false on failure.
*/
template <typename Handler>
bool try_receive(Handler&& handler)
{
return service_->try_receive(impl_, static_cast<Handler&&>(handler));
}
/// Asynchronously receive a message.
/**
* @par Completion Signature
* As determined by the <tt>Signatures...</tt> template parameter and the
* channel traits.
*/
template <typename CompletionToken
ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)>
auto async_receive(
CompletionToken&& token
ASIO_DEFAULT_COMPLETION_TOKEN(Executor))
#if !defined(GENERATING_DOCUMENTATION)
-> decltype(
this->do_async_receive(static_cast<payload_type*>(0),
static_cast<CompletionToken&&>(token)))
#endif // !defined(GENERATING_DOCUMENTATION)
{
return this->do_async_receive(static_cast<payload_type*>(0),
static_cast<CompletionToken&&>(token));
}
private:
// Disallow copying and assignment.
basic_channel(const basic_channel&) = delete;
basic_channel& operator=(const basic_channel&) = delete;
template <typename, typename, typename...>
friend class detail::channel_send_functions;
// Helper function to get an executor's context.
template <typename T>
static execution_context& get_context(const T& t,
enable_if_t<execution::is_executor<T>::value>* = 0)
{
return asio::query(t, execution::context);
}
// Helper function to get an executor's context.
template <typename T>
static execution_context& get_context(const T& t,
enable_if_t<!execution::is_executor<T>::value>* = 0)
{
return t.context();
}
class initiate_async_send
{
public:
typedef Executor executor_type;
explicit initiate_async_send(basic_channel* self)
: self_(self)
{
}
const executor_type& get_executor() const noexcept
{
return self_->get_executor();
}
template <typename SendHandler>
void operator()(SendHandler&& handler,
payload_type&& payload) const
{
asio::detail::non_const_lvalue<SendHandler> handler2(handler);
self_->service_->async_send(self_->impl_,
static_cast<payload_type&&>(payload),
handler2.value, self_->get_executor());
}
private:
basic_channel* self_;
};
class initiate_async_receive
{
public:
typedef Executor executor_type;
explicit initiate_async_receive(basic_channel* self)
: self_(self)
{
}
const executor_type& get_executor() const noexcept
{
return self_->get_executor();
}
template <typename ReceiveHandler>
void operator()(ReceiveHandler&& handler) const
{
asio::detail::non_const_lvalue<ReceiveHandler> handler2(handler);
self_->service_->async_receive(self_->impl_,
handler2.value, self_->get_executor());
}
private:
basic_channel* self_;
};
// The service associated with the I/O object.
service_type* service_;
// The underlying implementation of the I/O object.
typename service_type::template implementation_type<
Traits, Signatures...> impl_;
// The associated executor.
Executor executor_;
};
} // namespace experimental
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_EXPERIMENTAL_BASIC_CHANNEL_HPP

View File

@@ -0,0 +1,514 @@
//
// experimental/basic_concurrent_channel.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2025 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_BASIC_CONCURRENT_CHANNEL_HPP
#define ASIO_EXPERIMENTAL_BASIC_CONCURRENT_CHANNEL_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/non_const_lvalue.hpp"
#include "asio/detail/mutex.hpp"
#include "asio/execution/executor.hpp"
#include "asio/execution_context.hpp"
#include "asio/experimental/detail/channel_send_functions.hpp"
#include "asio/experimental/detail/channel_service.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
namespace experimental {
namespace detail {
} // namespace detail
/// A channel for messages.
/**
* The basic_concurrent_channel class template is used for sending messages
* between different parts of the same application. A <em>message</em> is
* defined as a collection of arguments to be passed to a completion handler,
* and the set of messages supported by a channel is specified by its @c Traits
* and <tt>Signatures...</tt> template parameters. Messages may be sent and
* received using asynchronous or non-blocking synchronous operations.
*
* Unless customising the traits, applications will typically use the @c
* experimental::concurrent_channel alias template. For example:
* @code void send_loop(int i, steady_timer& timer,
* concurrent_channel<void(error_code, int)>& ch)
* {
* if (i < 10)
* {
* timer.expires_after(chrono::seconds(1));
* timer.async_wait(
* [i, &timer, &ch](error_code error)
* {
* if (!error)
* {
* ch.async_send(error_code(), i,
* [i, &timer, &ch](error_code error)
* {
* if (!error)
* {
* send_loop(i + 1, timer, ch);
* }
* });
* }
* });
* }
* else
* {
* ch.close();
* }
* }
*
* void receive_loop(concurent_channel<void(error_code, int)>& ch)
* {
* ch.async_receive(
* [&ch](error_code error, int i)
* {
* if (!error)
* {
* std::cout << "Received " << i << "\n";
* receive_loop(ch);
* }
* });
* } @endcode
*
* @par Thread Safety
* @e Distinct @e objects: Safe.@n
* @e Shared @e objects: Safe.
*
* The basic_concurrent_channel class template is thread-safe, and would
* typically be used for passing messages between application code that run on
* different threads. Consider using @ref basic_channel, and its alias template
* @c experimental::channel, to pass messages between code running in a single
* thread or on the same strand.
*/
template <typename Executor, typename Traits, typename... Signatures>
class basic_concurrent_channel
#if !defined(GENERATING_DOCUMENTATION)
: public detail::channel_send_functions<
basic_concurrent_channel<Executor, Traits, Signatures...>,
Executor, Signatures...>
#endif // !defined(GENERATING_DOCUMENTATION)
{
private:
class initiate_async_send;
class initiate_async_receive;
typedef detail::channel_service<asio::detail::mutex> service_type;
typedef typename service_type::template implementation_type<
Traits, Signatures...>::payload_type payload_type;
template <typename... PayloadSignatures,
ASIO_COMPLETION_TOKEN_FOR(PayloadSignatures...) CompletionToken>
auto do_async_receive(
asio::detail::completion_payload<PayloadSignatures...>*,
CompletionToken&& token)
-> decltype(
async_initiate<CompletionToken, PayloadSignatures...>(
declval<initiate_async_receive>(), token))
{
return async_initiate<CompletionToken, PayloadSignatures...>(
initiate_async_receive(this), token);
}
public:
/// The type of the executor associated with the channel.
typedef Executor executor_type;
/// Rebinds the channel type to another executor.
template <typename Executor1>
struct rebind_executor
{
/// The channel type when rebound to the specified executor.
typedef basic_concurrent_channel<Executor1, Traits, Signatures...> other;
};
/// The traits type associated with the channel.
typedef typename Traits::template rebind<Signatures...>::other traits_type;
/// Construct a basic_concurrent_channel.
/**
* This constructor creates and channel.
*
* @param ex The I/O executor that the channel will use, by default, to
* dispatch handlers for any asynchronous operations performed on the channel.
*
* @param max_buffer_size The maximum number of messages that may be buffered
* in the channel.
*/
basic_concurrent_channel(const executor_type& ex,
std::size_t max_buffer_size = 0)
: service_(&asio::use_service<service_type>(
basic_concurrent_channel::get_context(ex))),
impl_(),
executor_(ex)
{
service_->construct(impl_, max_buffer_size);
}
/// Construct and open a basic_concurrent_channel.
/**
* This constructor creates and opens a channel.
*
* @param context An execution context which provides the I/O executor that
* the channel will use, by default, to dispatch handlers for any asynchronous
* operations performed on the channel.
*
* @param max_buffer_size The maximum number of messages that may be buffered
* in the channel.
*/
template <typename ExecutionContext>
basic_concurrent_channel(ExecutionContext& context,
std::size_t max_buffer_size = 0,
constraint_t<
is_convertible<ExecutionContext&, execution_context&>::value,
defaulted_constraint
> = defaulted_constraint())
: service_(&asio::use_service<service_type>(context)),
impl_(),
executor_(context.get_executor())
{
service_->construct(impl_, max_buffer_size);
}
/// Move-construct a basic_concurrent_channel from another.
/**
* This constructor moves a channel from one object to another.
*
* @param other The other basic_concurrent_channel object from which the move
* will occur.
*
* @note Following the move, the moved-from object is in the same state as if
* constructed using the @c basic_concurrent_channel(const executor_type&)
* constructor.
*/
basic_concurrent_channel(basic_concurrent_channel&& other)
: service_(other.service_),
executor_(other.executor_)
{
service_->move_construct(impl_, other.impl_);
}
/// Move-assign a basic_concurrent_channel from another.
/**
* This assignment operator moves a channel from one object to another.
* Cancels any outstanding asynchronous operations associated with the target
* object.
*
* @param other The other basic_concurrent_channel object from which the move
* will occur.
*
* @note Following the move, the moved-from object is in the same state as if
* constructed using the @c basic_concurrent_channel(const executor_type&)
* constructor.
*/
basic_concurrent_channel& operator=(basic_concurrent_channel&& other)
{
if (this != &other)
{
service_->move_assign(impl_, *other.service_, other.impl_);
executor_.~executor_type();
new (&executor_) executor_type(other.executor_);
service_ = other.service_;
}
return *this;
}
// All channels have access to each other's implementations.
template <typename, typename, typename...>
friend class basic_concurrent_channel;
/// Move-construct a basic_concurrent_channel from another.
/**
* This constructor moves a channel from one object to another.
*
* @param other The other basic_concurrent_channel object from which the move
* will occur.
*
* @note Following the move, the moved-from object is in the same state as if
* constructed using the @c basic_concurrent_channel(const executor_type&)
* constructor.
*/
template <typename Executor1>
basic_concurrent_channel(
basic_concurrent_channel<Executor1, Traits, Signatures...>&& other,
constraint_t<
is_convertible<Executor1, Executor>::value
> = 0)
: service_(other.service_),
executor_(other.executor_)
{
service_->move_construct(impl_, other.impl_);
}
/// Move-assign a basic_concurrent_channel from another.
/**
* This assignment operator moves a channel from one object to another.
* Cancels any outstanding asynchronous operations associated with the target
* object.
*
* @param other The other basic_concurrent_channel object from which the move
* will occur.
*
* @note Following the move, the moved-from object is in the same state as if
* constructed using the @c basic_concurrent_channel(const executor_type&)
* constructor.
*/
template <typename Executor1>
constraint_t<
is_convertible<Executor1, Executor>::value,
basic_concurrent_channel&
> operator=(
basic_concurrent_channel<Executor1, Traits, Signatures...>&& other)
{
if (this != &other)
{
service_->move_assign(impl_, *other.service_, other.impl_);
executor_.~executor_type();
new (&executor_) executor_type(other.executor_);
service_ = other.service_;
}
return *this;
}
/// Destructor.
~basic_concurrent_channel()
{
service_->destroy(impl_);
}
/// Get the executor associated with the object.
const executor_type& get_executor() noexcept
{
return executor_;
}
/// Get the capacity of the channel's buffer.
std::size_t capacity() noexcept
{
return service_->capacity(impl_);
}
/// Determine whether the channel is open.
bool is_open() const noexcept
{
return service_->is_open(impl_);
}
/// Reset the channel to its initial state.
void reset()
{
service_->reset(impl_);
}
/// Close the channel.
void close()
{
service_->close(impl_);
}
/// Cancel all asynchronous operations waiting on the channel.
/**
* All outstanding send operations will complete with the error
* @c asio::experimental::error::channel_cancelled. Outstanding receive
* operations complete with the result as determined by the channel traits.
*/
void cancel()
{
service_->cancel(impl_);
}
/// Determine whether a message can be received without blocking.
bool ready() const noexcept
{
return service_->ready(impl_);
}
#if defined(GENERATING_DOCUMENTATION)
/// Try to send a message without blocking.
/**
* Fails if the buffer is full and there are no waiting receive operations.
*
* @returns @c true on success, @c false on failure.
*/
template <typename... Args>
bool try_send(Args&&... args);
/// Try to send a message without blocking, using dispatch semantics to call
/// the receive operation's completion handler.
/**
* Fails if the buffer is full and there are no waiting receive operations.
*
* The receive operation's completion handler may be called from inside this
* function.
*
* @returns @c true on success, @c false on failure.
*/
template <typename... Args>
bool try_send_via_dispatch(Args&&... args);
/// Try to send a number of messages without blocking.
/**
* @returns The number of messages that were sent.
*/
template <typename... Args>
std::size_t try_send_n(std::size_t count, Args&&... args);
/// Try to send a number of messages without blocking, using dispatch
/// semantics to call the receive operations' completion handlers.
/**
* The receive operations' completion handlers may be called from inside this
* function.
*
* @returns The number of messages that were sent.
*/
template <typename... Args>
std::size_t try_send_n_via_dispatch(std::size_t count, Args&&... args);
/// Asynchronously send a message.
template <typename... Args,
ASIO_COMPLETION_TOKEN_FOR(void (asio::error_code))
CompletionToken ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)>
auto async_send(Args&&... args,
CompletionToken&& token);
#endif // defined(GENERATING_DOCUMENTATION)
/// Try to receive a message without blocking.
/**
* Fails if the buffer is full and there are no waiting receive operations.
*
* @returns @c true on success, @c false on failure.
*/
template <typename Handler>
bool try_receive(Handler&& handler)
{
return service_->try_receive(impl_, static_cast<Handler&&>(handler));
}
/// Asynchronously receive a message.
template <typename CompletionToken
ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)>
auto async_receive(
CompletionToken&& token
ASIO_DEFAULT_COMPLETION_TOKEN(Executor))
#if !defined(GENERATING_DOCUMENTATION)
-> decltype(
this->do_async_receive(static_cast<payload_type*>(0),
static_cast<CompletionToken&&>(token)))
#endif // !defined(GENERATING_DOCUMENTATION)
{
return this->do_async_receive(static_cast<payload_type*>(0),
static_cast<CompletionToken&&>(token));
}
private:
// Disallow copying and assignment.
basic_concurrent_channel(
const basic_concurrent_channel&) = delete;
basic_concurrent_channel& operator=(
const basic_concurrent_channel&) = delete;
template <typename, typename, typename...>
friend class detail::channel_send_functions;
// Helper function to get an executor's context.
template <typename T>
static execution_context& get_context(const T& t,
enable_if_t<execution::is_executor<T>::value>* = 0)
{
return asio::query(t, execution::context);
}
// Helper function to get an executor's context.
template <typename T>
static execution_context& get_context(const T& t,
enable_if_t<!execution::is_executor<T>::value>* = 0)
{
return t.context();
}
class initiate_async_send
{
public:
typedef Executor executor_type;
explicit initiate_async_send(basic_concurrent_channel* self)
: self_(self)
{
}
const executor_type& get_executor() const noexcept
{
return self_->get_executor();
}
template <typename SendHandler>
void operator()(SendHandler&& handler,
payload_type&& payload) const
{
asio::detail::non_const_lvalue<SendHandler> handler2(handler);
self_->service_->async_send(self_->impl_,
static_cast<payload_type&&>(payload),
handler2.value, self_->get_executor());
}
private:
basic_concurrent_channel* self_;
};
class initiate_async_receive
{
public:
typedef Executor executor_type;
explicit initiate_async_receive(basic_concurrent_channel* self)
: self_(self)
{
}
const executor_type& get_executor() const noexcept
{
return self_->get_executor();
}
template <typename ReceiveHandler>
void operator()(ReceiveHandler&& handler) const
{
asio::detail::non_const_lvalue<ReceiveHandler> handler2(handler);
self_->service_->async_receive(self_->impl_,
handler2.value, self_->get_executor());
}
private:
basic_concurrent_channel* self_;
};
// The service associated with the I/O object.
service_type* service_;
// The underlying implementation of the I/O object.
typename service_type::template implementation_type<
Traits, Signatures...> impl_;
// The associated executor.
Executor executor_;
};
} // namespace experimental
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_EXPERIMENTAL_BASIC_CONCURRENT_CHANNEL_HPP

View File

@@ -0,0 +1,139 @@
//
// experimental/cancellation_condition.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2025 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_CANCELLATION_CONDITION_HPP
#define ASIO_EXPERIMENTAL_CANCELLATION_CONDITION_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/config.hpp"
#include "asio/cancellation_type.hpp"
#include "asio/disposition.hpp"
#include "asio/detail/type_traits.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
namespace experimental {
/// Wait for all operations to complete.
class wait_for_all
{
public:
template <typename... Args>
constexpr cancellation_type_t operator()(Args&&...) const noexcept
{
return cancellation_type::none;
}
};
/// Wait until an operation completes, then cancel the others.
class wait_for_one
{
public:
constexpr explicit wait_for_one(
cancellation_type_t cancel_type = cancellation_type::all)
: cancel_type_(cancel_type)
{
}
template <typename... Args>
constexpr cancellation_type_t operator()(Args&&...) const noexcept
{
return cancel_type_;
}
private:
cancellation_type_t cancel_type_;
};
/// Wait until an operation completes without an error, then cancel the others.
/**
* If no operation completes without an error, waits for completion of all
* operations.
*/
class wait_for_one_success
{
public:
constexpr explicit wait_for_one_success(
cancellation_type_t cancel_type = cancellation_type::all)
: cancel_type_(cancel_type)
{
}
constexpr cancellation_type_t
operator()() const noexcept
{
return cancel_type_;
}
template <typename E, typename... Args>
constexpr constraint_t<!is_disposition<E>::value, cancellation_type_t>
operator()(const E&, Args&&...) const noexcept
{
return cancel_type_;
}
template <typename E, typename... Args>
constexpr constraint_t<is_disposition<E>::value, cancellation_type_t>
operator()(const E& e, Args&&...) const noexcept
{
return e != no_error ? cancellation_type::none : cancel_type_;
}
private:
cancellation_type_t cancel_type_;
};
/// Wait until an operation completes with an error, then cancel the others.
/**
* If no operation completes with an error, waits for completion of all
* operations.
*/
class wait_for_one_error
{
public:
constexpr explicit wait_for_one_error(
cancellation_type_t cancel_type = cancellation_type::all)
: cancel_type_(cancel_type)
{
}
constexpr cancellation_type_t operator()() const noexcept
{
return cancellation_type::none;
}
template <typename E, typename... Args>
constexpr constraint_t<!is_disposition<E>::value, cancellation_type_t>
operator()(const E&, Args&&...) const noexcept
{
return cancellation_type::none;
}
template <typename E, typename... Args>
constexpr constraint_t<is_disposition<E>::value, cancellation_type_t>
operator()(const E& e, Args&&...) const noexcept
{
return e != no_error ? cancel_type_ : cancellation_type::none;
}
private:
cancellation_type_t cancel_type_;
};
} // namespace experimental
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_EXPERIMENTAL_CANCELLATION_CONDITION_HPP

View File

@@ -0,0 +1,76 @@
//
// experimental/channel.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2025 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_CHANNEL_HPP
#define ASIO_EXPERIMENTAL_CHANNEL_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/config.hpp"
#include "asio/any_io_executor.hpp"
#include "asio/detail/type_traits.hpp"
#include "asio/execution/executor.hpp"
#include "asio/is_executor.hpp"
#include "asio/experimental/basic_channel.hpp"
#include "asio/experimental/channel_traits.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
namespace experimental {
namespace detail {
template <typename ExecutorOrSignature, typename = void>
struct channel_type
{
template <typename... Signatures>
struct inner
{
typedef basic_channel<any_io_executor, channel_traits<>,
ExecutorOrSignature, Signatures...> type;
};
};
template <typename ExecutorOrSignature>
struct channel_type<ExecutorOrSignature,
enable_if_t<
is_executor<ExecutorOrSignature>::value
|| execution::is_executor<ExecutorOrSignature>::value
>>
{
template <typename... Signatures>
struct inner
{
typedef basic_channel<ExecutorOrSignature,
channel_traits<>, Signatures...> type;
};
};
} // namespace detail
/// Template type alias for common use of channel.
#if defined(GENERATING_DOCUMENTATION)
template <typename ExecutorOrSignature, typename... Signatures>
using channel = basic_channel<
specified_executor_or_any_io_executor, channel_traits<>, signatures...>;
#else // defined(GENERATING_DOCUMENTATION)
template <typename ExecutorOrSignature, typename... Signatures>
using channel = typename detail::channel_type<
ExecutorOrSignature>::template inner<Signatures...>::type;
#endif // defined(GENERATING_DOCUMENTATION)
} // namespace experimental
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_EXPERIMENTAL_CHANNEL_HPP

View File

@@ -0,0 +1,82 @@
//
// experimental/channel_error.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2025 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_CHANNEL_ERROR_HPP
#define ASIO_EXPERIMENTAL_CHANNEL_ERROR_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/config.hpp"
#include "asio/error_code.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
namespace experimental {
namespace error {
enum channel_errors
{
/// The channel was closed.
channel_closed = 1,
/// The channel was cancelled.
channel_cancelled = 2
};
extern ASIO_DECL
const asio::error_category& get_channel_category();
static const asio::error_category&
channel_category ASIO_UNUSED_VARIABLE
= asio::experimental::error::get_channel_category();
} // namespace error
namespace channel_errc {
// Simulates a scoped enum.
using error::channel_closed;
using error::channel_cancelled;
} // namespace channel_errc
} // namespace experimental
} // namespace asio
namespace std {
template<> struct is_error_code_enum<
asio::experimental::error::channel_errors>
{
static const bool value = true;
};
} // namespace std
namespace asio {
namespace experimental {
namespace error {
inline asio::error_code make_error_code(channel_errors e)
{
return asio::error_code(
static_cast<int>(e), get_channel_category());
}
} // namespace error
} // namespace experimental
} // namespace asio
#include "asio/detail/pop_options.hpp"
#if defined(ASIO_HEADER_ONLY)
# include "asio/experimental/impl/channel_error.ipp"
#endif // defined(ASIO_HEADER_ONLY)
#endif // ASIO_EXPERIMENTAL_CHANNEL_ERROR_HPP

View File

@@ -0,0 +1,301 @@
//
// experimental/channel_traits.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2025 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_CHANNEL_TRAITS_HPP
#define ASIO_EXPERIMENTAL_CHANNEL_TRAITS_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/config.hpp"
#include <deque>
#include "asio/detail/type_traits.hpp"
#include "asio/error.hpp"
#include "asio/experimental/channel_error.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
namespace experimental {
#if defined(GENERATING_DOCUMENTATION)
template <typename... Signatures>
struct channel_traits
{
/// Rebind the traits to a new set of signatures.
/**
* This nested structure must have a single nested type @c other that
* aliases a traits type with the specified set of signatures.
*/
template <typename... NewSignatures>
struct rebind
{
typedef user_defined other;
};
/// Determine the container for the specified elements.
/**
* This nested structure must have a single nested type @c other that
* aliases a container type for the specified element type.
*/
template <typename Element>
struct container
{
typedef user_defined type;
};
/// The signature of a channel cancellation notification.
typedef void receive_cancelled_signature(...);
/// Invoke the specified handler with a cancellation notification.
template <typename F>
static void invoke_receive_cancelled(F f);
/// The signature of a channel closed notification.
typedef void receive_closed_signature(...);
/// Invoke the specified handler with a closed notification.
template <typename F>
static void invoke_receive_closed(F f);
};
#else // defined(GENERATING_DOCUMENTATION)
/// Traits used for customising channel behaviour.
template <typename... Signatures>
struct channel_traits
{
template <typename... NewSignatures>
struct rebind
{
typedef channel_traits<NewSignatures...> other;
};
};
template <typename R>
struct channel_traits<R(asio::error_code)>
{
template <typename... NewSignatures>
struct rebind
{
typedef channel_traits<NewSignatures...> other;
};
template <typename Element>
struct container
{
typedef std::deque<Element> type;
};
typedef R receive_cancelled_signature(asio::error_code);
template <typename F>
static void invoke_receive_cancelled(F f)
{
const asio::error_code e = error::channel_cancelled;
static_cast<F&&>(f)(e);
}
typedef R receive_closed_signature(asio::error_code);
template <typename F>
static void invoke_receive_closed(F f)
{
const asio::error_code e = error::channel_closed;
static_cast<F&&>(f)(e);
}
};
template <typename R, typename... Args, typename... Signatures>
struct channel_traits<R(asio::error_code, Args...), Signatures...>
{
template <typename... NewSignatures>
struct rebind
{
typedef channel_traits<NewSignatures...> other;
};
template <typename Element>
struct container
{
typedef std::deque<Element> type;
};
typedef R receive_cancelled_signature(asio::error_code, Args...);
template <typename F>
static void invoke_receive_cancelled(F f)
{
const asio::error_code e = error::channel_cancelled;
static_cast<F&&>(f)(e, decay_t<Args>()...);
}
typedef R receive_closed_signature(asio::error_code, Args...);
template <typename F>
static void invoke_receive_closed(F f)
{
const asio::error_code e = error::channel_closed;
static_cast<F&&>(f)(e, decay_t<Args>()...);
}
};
template <typename R>
struct channel_traits<R(std::exception_ptr)>
{
template <typename... NewSignatures>
struct rebind
{
typedef channel_traits<NewSignatures...> other;
};
template <typename Element>
struct container
{
typedef std::deque<Element> type;
};
typedef R receive_cancelled_signature(std::exception_ptr);
template <typename F>
static void invoke_receive_cancelled(F f)
{
const asio::error_code e = error::channel_cancelled;
static_cast<F&&>(f)(
std::make_exception_ptr(asio::system_error(e)));
}
typedef R receive_closed_signature(std::exception_ptr);
template <typename F>
static void invoke_receive_closed(F f)
{
const asio::error_code e = error::channel_closed;
static_cast<F&&>(f)(
std::make_exception_ptr(asio::system_error(e)));
}
};
template <typename R, typename... Args, typename... Signatures>
struct channel_traits<R(std::exception_ptr, Args...), Signatures...>
{
template <typename... NewSignatures>
struct rebind
{
typedef channel_traits<NewSignatures...> other;
};
template <typename Element>
struct container
{
typedef std::deque<Element> type;
};
typedef R receive_cancelled_signature(std::exception_ptr, Args...);
template <typename F>
static void invoke_receive_cancelled(F f)
{
const asio::error_code e = error::channel_cancelled;
static_cast<F&&>(f)(
std::make_exception_ptr(asio::system_error(e)),
decay_t<Args>()...);
}
typedef R receive_closed_signature(std::exception_ptr, Args...);
template <typename F>
static void invoke_receive_closed(F f)
{
const asio::error_code e = error::channel_closed;
static_cast<F&&>(f)(
std::make_exception_ptr(asio::system_error(e)),
decay_t<Args>()...);
}
};
template <typename R>
struct channel_traits<R()>
{
template <typename... NewSignatures>
struct rebind
{
typedef channel_traits<NewSignatures...> other;
};
template <typename Element>
struct container
{
typedef std::deque<Element> type;
};
typedef R receive_cancelled_signature(asio::error_code);
template <typename F>
static void invoke_receive_cancelled(F f)
{
const asio::error_code e = error::channel_cancelled;
static_cast<F&&>(f)(e);
}
typedef R receive_closed_signature(asio::error_code);
template <typename F>
static void invoke_receive_closed(F f)
{
const asio::error_code e = error::channel_closed;
static_cast<F&&>(f)(e);
}
};
template <typename R, typename T>
struct channel_traits<R(T)>
{
template <typename... NewSignatures>
struct rebind
{
typedef channel_traits<NewSignatures...> other;
};
template <typename Element>
struct container
{
typedef std::deque<Element> type;
};
typedef R receive_cancelled_signature(asio::error_code);
template <typename F>
static void invoke_receive_cancelled(F f)
{
const asio::error_code e = error::channel_cancelled;
static_cast<F&&>(f)(e);
}
typedef R receive_closed_signature(asio::error_code);
template <typename F>
static void invoke_receive_closed(F f)
{
const asio::error_code e = error::channel_closed;
static_cast<F&&>(f)(e);
}
};
#endif // defined(GENERATING_DOCUMENTATION)
} // namespace experimental
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_EXPERIMENTAL_CHANNEL_TRAITS_HPP

View File

@@ -0,0 +1,33 @@
//
// experimental/co_composed.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2025 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_COMPOSED_HPP
#define ASIO_EXPERIMENTAL_CO_COMPOSED_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/config.hpp"
#include "asio/co_composed.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
namespace experimental {
using asio::co_composed;
} // namespace experimental
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_EXPERIMENTAL_CO_COMPOSED_HPP

View File

@@ -0,0 +1,187 @@
//
// experimental/co_spawn.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2021-2023 Klemens D. Morgenstern
// (klemens dot morgenstern at gmx dot net)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_EXPERIMENTAL_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"
#include <utility>
#include "asio/compose.hpp"
#include "asio/deferred.hpp"
#include "asio/detail/type_traits.hpp"
#include "asio/experimental/coro.hpp"
#include "asio/prepend.hpp"
#include "asio/redirect_error.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
namespace experimental {
namespace detail {
template <typename T, typename U, typename Executor>
struct coro_spawn_op
{
coro<T, U, Executor> c;
void operator()(auto& self)
{
auto op = c.async_resume(deferred);
std::move(op)((prepend)(std::move(self), 0));
}
void operator()(auto& self, int, auto... res)
{
self.complete(std::move(res)...);
}
};
} // namespace detail
/// Spawn a resumable coroutine.
/**
* This function spawns the coroutine for execution on its executor. It binds
* the lifetime of the coroutine to the executor.
*
* @param c The coroutine
*
* @param token The completion token
*
* @returns Implementation defined
*/
template <typename T, typename Executor, typename CompletionToken>
ASIO_INITFN_AUTO_RESULT_TYPE(
CompletionToken, void(std::exception_ptr, T))
co_spawn(coro<void, T, Executor> c, CompletionToken&& token)
{
auto exec = c.get_executor();
return async_compose<CompletionToken, void(std::exception_ptr, T)>(
detail::coro_spawn_op<void, T, Executor>{std::move(c)},
token, exec);
}
/// Spawn a resumable coroutine.
/**
* This function spawns the coroutine for execution on its executor. It binds
* the lifetime of the coroutine to the executor.
*
* @param c The coroutine
*
* @param token The completion token
*
* @returns Implementation defined
*/
template <typename T, typename Executor, typename CompletionToken>
ASIO_INITFN_AUTO_RESULT_TYPE(
CompletionToken, void(std::exception_ptr, T))
co_spawn(coro<void(), T, Executor> c, CompletionToken&& token)
{
auto exec = c.get_executor();
return async_compose<CompletionToken, void(std::exception_ptr, T)>(
detail::coro_spawn_op<void(), T, Executor>{std::move(c)},
token, exec);
}
/// Spawn a resumable coroutine.
/**
* This function spawns the coroutine for execution on its executor. It binds
* the lifetime of the coroutine to the executor.
*
* @param c The coroutine
*
* @param token The completion token
*
* @returns Implementation defined
*/
template <typename T, typename Executor, typename CompletionToken>
ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(T))
co_spawn(coro<void() noexcept, T, Executor> c, CompletionToken&& token)
{
auto exec = c.get_executor();
return async_compose<CompletionToken, void(T)>(
detail::coro_spawn_op<void() noexcept, T, Executor>{std::move(c)},
token, exec);
}
/// Spawn a resumable coroutine.
/**
* This function spawns the coroutine for execution on its executor. It binds
* the lifetime of the coroutine to the executor.
*
* @param c The coroutine
*
* @param token The completion token
*
* @returns Implementation defined
*/
template <typename Executor, typename CompletionToken>
ASIO_INITFN_AUTO_RESULT_TYPE(
CompletionToken, void(std::exception_ptr))
co_spawn(coro<void, void, Executor> c, CompletionToken&& token)
{
auto exec = c.get_executor();
return async_compose<CompletionToken, void(std::exception_ptr)>(
detail::coro_spawn_op<void, void, Executor>{std::move(c)},
token, exec);
}
/// Spawn a resumable coroutine.
/**
* This function spawns the coroutine for execution on its executor. It binds
* the lifetime of the coroutine to the executor.
*
* @param c The coroutine
*
* @param token The completion token
*
* @returns Implementation defined
*/
template <typename Executor, typename CompletionToken>
ASIO_INITFN_AUTO_RESULT_TYPE(
CompletionToken, void(std::exception_ptr))
co_spawn(coro<void(), void, Executor> c, CompletionToken&& token)
{
auto exec = c.get_executor();
return async_compose<CompletionToken, void(std::exception_ptr)>(
detail::coro_spawn_op<void(), void, Executor>{std::move(c)},
token, exec);
}
/// Spawn a resumable coroutine.
/**
* This function spawns the coroutine for execution on its executor. It binds
* the lifetime of the coroutine to the executor.
*
* @param c The coroutine
*
* @param token The completion token
*
* @returns Implementation defined
*/
template <typename Executor, typename CompletionToken>
ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void())
co_spawn(coro<void() noexcept, void, Executor> c, CompletionToken&& token)
{
auto exec = c.get_executor();
return async_compose<CompletionToken, void()>(
detail::coro_spawn_op<void() noexcept, void, Executor>{std::move(c)},
token, exec);
}
} // namespace detail
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif //ASIO_EXPERIMENTAL_CO_SPAWN_HPP

View File

@@ -0,0 +1,76 @@
//
// experimental/concurrent_channel.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2025 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_CONCURRENT_CHANNEL_HPP
#define ASIO_EXPERIMENTAL_CONCURRENT_CHANNEL_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/config.hpp"
#include "asio/any_io_executor.hpp"
#include "asio/detail/type_traits.hpp"
#include "asio/execution/executor.hpp"
#include "asio/is_executor.hpp"
#include "asio/experimental/basic_concurrent_channel.hpp"
#include "asio/experimental/channel_traits.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
namespace experimental {
namespace detail {
template <typename ExecutorOrSignature, typename = void>
struct concurrent_channel_type
{
template <typename... Signatures>
struct inner
{
typedef basic_concurrent_channel<any_io_executor, channel_traits<>,
ExecutorOrSignature, Signatures...> type;
};
};
template <typename ExecutorOrSignature>
struct concurrent_channel_type<ExecutorOrSignature,
enable_if_t<
is_executor<ExecutorOrSignature>::value
|| execution::is_executor<ExecutorOrSignature>::value
>>
{
template <typename... Signatures>
struct inner
{
typedef basic_concurrent_channel<ExecutorOrSignature,
channel_traits<>, Signatures...> type;
};
};
} // namespace detail
/// Template type alias for common use of channel.
#if defined(GENERATING_DOCUMENTATION)
template <typename ExecutorOrSignature, typename... Signatures>
using concurrent_channel = basic_concurrent_channel<
specified_executor_or_any_io_executor, channel_traits<>, signatures...>;
#else // defined(GENERATING_DOCUMENTATION)
template <typename ExecutorOrSignature, typename... Signatures>
using concurrent_channel = typename detail::concurrent_channel_type<
ExecutorOrSignature>::template inner<Signatures...>::type;
#endif // defined(GENERATING_DOCUMENTATION)
} // namespace experimental
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_EXPERIMENTAL_CONCURRENT_CHANNEL_HPP

View File

@@ -0,0 +1,293 @@
//
// experimental/coro.hpp
// ~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2021-2023 Klemens D. Morgenstern
// (klemens dot morgenstern at gmx dot net)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_EXPERIMENTAL_CORO_HPP
#define ASIO_EXPERIMENTAL_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/dispatch.hpp"
#include "asio/error.hpp"
#include "asio/error_code.hpp"
#include "asio/experimental/coro_traits.hpp"
#include "asio/experimental/detail/coro_promise_allocator.hpp"
#include "asio/experimental/detail/partial_promise.hpp"
#include "asio/post.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
namespace experimental {
namespace detail {
template <typename Signature, typename Return,
typename Executor, typename Allocator>
struct coro_promise;
template <typename T, typename Coroutine>
struct coro_with_arg;
} // namespace detail
/// The main type of a resumable coroutine.
/**
* Template parameter @c Yield specifies type or signature used by co_yield,
* @c Return specifies the type used for co_return, and @c Executor specifies
* the underlying executor type.
*/
template <typename Yield = void, typename Return = void,
typename Executor = any_io_executor,
typename Allocator = std::allocator<void>>
struct coro
{
/// The traits of the coroutine. See asio::experimental::coro_traits
/// for details.
using traits = coro_traits<Yield, Return, Executor>;
/// The value that can be passed into a symmetrical cororoutine. @c void if
/// asymmetrical.
using input_type = typename traits::input_type;
/// The type that can be passed out through a co_yield.
using yield_type = typename traits::yield_type;
/// The type that can be passed out through a co_return.
using return_type = typename traits::return_type;
/// The type received by a co_await or async_resume. Its a combination of
/// yield and return.
using result_type = typename traits::result_type;
/// The signature used by the async_resume.
using signature_type = typename traits::signature_type;
/// Whether or not the coroutine is noexcept.
constexpr static bool is_noexcept = traits::is_noexcept;
/// The error type of the coroutine. Void for noexcept
using error_type = typename traits::error_type;
/// Completion handler type used by async_resume.
using completion_handler = typename traits::completion_handler;
/// The internal promise-type of the coroutine.
using promise_type = detail::coro_promise<Yield, Return, Executor, Allocator>;
#if !defined(GENERATING_DOCUMENTATION)
template <typename T, typename Coroutine>
friend struct detail::coro_with_arg;
#endif // !defined(GENERATING_DOCUMENTATION)
/// The executor type.
using executor_type = Executor;
/// The allocator type.
using allocator_type = Allocator;
#if !defined(GENERATING_DOCUMENTATION)
friend struct detail::coro_promise<Yield, Return, Executor, Allocator>;
#endif // !defined(GENERATING_DOCUMENTATION)
/// The default constructor, gives an invalid coroutine.
coro() = default;
/// Move constructor.
coro(coro&& lhs) noexcept
: coro_(std::exchange(lhs.coro_, nullptr))
{
}
coro(const coro&) = delete;
/// Move assignment.
coro& operator=(coro&& lhs) noexcept
{
std::swap(coro_, lhs.coro_);
return *this;
}
coro& operator=(const coro&) = delete;
/// Destructor. Destroys the coroutine, if it holds a valid one.
/**
* @note This does not cancel an active coroutine. Destructing a resumable
* coroutine, i.e. one with a call to async_resume that has not completed, is
* undefined behaviour.
*/
~coro()
{
if (coro_ != nullptr)
{
struct destroyer
{
detail::coroutine_handle<promise_type> handle;
destroyer(const detail::coroutine_handle<promise_type>& handle)
: handle(handle)
{ }
destroyer(destroyer&& lhs)
: handle(std::exchange(lhs.handle, nullptr))
{
}
destroyer(const destroyer&) = delete;
void operator()() {}
~destroyer()
{
if (handle)
handle.destroy();
}
};
auto handle =
detail::coroutine_handle<promise_type>::from_promise(*coro_);
if (handle)
asio::dispatch(coro_->get_executor(), destroyer{handle});
}
}
/// Get the used executor.
executor_type get_executor() const
{
if (coro_)
return coro_->get_executor();
if constexpr (std::is_default_constructible_v<Executor>)
return Executor{};
else
throw std::logic_error("Coroutine has no executor");
}
/// Get the used allocator.
allocator_type get_allocator() const
{
if (coro_)
return coro_->get_allocator();
if constexpr (std::is_default_constructible_v<Allocator>)
return Allocator{};
else
throw std::logic_error(
"Coroutine has no available allocator without a constructed promise");
}
/// Resume the coroutine.
/**
* @param token The completion token of the async resume.
*
* @attention Calling an invalid coroutine with a noexcept signature is
* undefined behaviour.
*
* @note This overload is only available for coroutines without an input
* value.
*/
template <typename CompletionToken>
requires std::is_void_v<input_type>
auto async_resume(CompletionToken&& token) &
{
return async_initiate<CompletionToken,
typename traits::completion_handler>(
initiate_async_resume(this), token);
}
/// Resume the coroutine.
/**
* @param token The completion token of the async resume.
*
* @attention Calling an invalid coroutine with a noexcept signature is
* undefined behaviour.
*
* @note This overload is only available for coroutines with an input value.
*/
template <typename CompletionToken, detail::convertible_to<input_type> T>
auto async_resume(T&& ip, CompletionToken&& token) &
{
return async_initiate<CompletionToken,
typename traits::completion_handler>(
initiate_async_resume(this), token, std::forward<T>(ip));
}
/// Operator used for coroutines without input value.
auto operator co_await() requires (std::is_void_v<input_type>)
{
return awaitable_t{*this};
}
/// Operator used for coroutines with input value.
/**
* @param ip The input value
*
* @returns An awaitable handle.
*
* @code
* coro<void> push_values(coro<double(int)> c)
* {
* std::optional<double> res = co_await c(42);
* }
* @endcode
*/
template <detail::convertible_to<input_type> T>
auto operator()(T&& ip)
{
return detail::coro_with_arg<std::decay_t<T>, coro>{
std::forward<T>(ip), *this};
}
/// Check whether the coroutine is open, i.e. can be resumed.
bool is_open() const
{
if (coro_)
{
auto handle =
detail::coroutine_handle<promise_type>::from_promise(*coro_);
return handle && !handle.done();
}
else
return false;
}
/// Check whether the coroutine is open, i.e. can be resumed.
explicit operator bool() const { return is_open(); }
private:
struct awaitable_t;
struct initiate_async_resume;
explicit coro(promise_type* const cr) : coro_(cr) {}
promise_type* coro_{nullptr};
};
/// A generator is a coro that returns void and yields value.
template<typename T, typename Executor = asio::any_io_executor,
typename Allocator = std::allocator<void>>
using generator = coro<T, void, Executor, Allocator>;
/// A task is a coro that does not yield values
template<typename T, typename Executor = asio::any_io_executor,
typename Allocator = std::allocator<void>>
using task = coro<void(), T, Executor, Allocator>;
} // namespace experimental
} // namespace asio
#include "asio/detail/pop_options.hpp"
#include "asio/experimental/impl/coro.hpp"
#endif // ASIO_EXPERIMENTAL_CORO_HPP

View File

@@ -0,0 +1,228 @@
//
// experimental/detail/coro_traits.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2021-2023 Klemens D. Morgenstern
// (klemens dot morgenstern at gmx dot net)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_EXPERIMENTAL_DETAIL_CORO_TRAITS_HPP
#define ASIO_EXPERIMENTAL_DETAIL_CORO_TRAITS_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/config.hpp"
#include <optional>
#include <variant>
#include "asio/any_io_executor.hpp"
namespace asio {
namespace experimental {
namespace detail {
template <class From, class To>
concept convertible_to = std::is_convertible_v<From, To>;
template <typename T>
concept decays_to_executor = execution::executor<std::decay_t<T>>;
template <typename T, typename Executor = any_io_executor>
concept execution_context = requires (T& t)
{
{t.get_executor()} -> convertible_to<Executor>;
};
template <typename Yield, typename Return>
struct coro_result
{
using type = std::variant<Yield, Return>;
};
template <typename Yield>
struct coro_result<Yield, void>
{
using type = std::optional<Yield>;
};
template <typename Return>
struct coro_result<void, Return>
{
using type = Return;
};
template <typename YieldReturn>
struct coro_result<YieldReturn, YieldReturn>
{
using type = YieldReturn;
};
template <>
struct coro_result<void, void>
{
using type = void;
};
template <typename Yield, typename Return>
using coro_result_t = typename coro_result<Yield, Return>::type;
template <typename Result, bool IsNoexcept>
struct coro_handler;
template <>
struct coro_handler<void, false>
{
using type = void(std::exception_ptr);
};
template <>
struct coro_handler<void, true>
{
using type = void();
};
template <typename T>
struct coro_handler<T, false>
{
using type = void(std::exception_ptr, T);
};
template <typename T>
struct coro_handler<T, true>
{
using type = void(T);
};
template <typename Result, bool IsNoexcept>
using coro_handler_t = typename coro_handler<Result, IsNoexcept>::type;
} // namespace detail
#if defined(GENERATING_DOCUMENTATION)
/// The traits describing the resumable coroutine behaviour.
/**
* Template parameter @c Yield specifies type or signature used by co_yield,
* @c Return specifies the type used for co_return, and @c Executor specifies
* the underlying executor type.
*/
template <typename Yield, typename Return, typename Executor>
struct coro_traits
{
/// The value that can be passed into a symmetrical cororoutine. @c void if
/// asymmetrical.
using input_type = argument_dependent;
/// The type that can be passed out through a co_yield.
using yield_type = argument_dependent;
/// The type that can be passed out through a co_return.
using return_type = argument_dependent;
/// The type received by a co_await or async_resume. It's a combination of
/// yield and return.
using result_type = argument_dependent;
/// The signature used by the async_resume.
using signature_type = argument_dependent;
/// Whether or not the coroutine is noexcept.
constexpr static bool is_noexcept = argument_dependent;
/// The error type of the coroutine. @c void for noexcept.
using error_type = argument_dependent;
/// Completion handler type used by async_resume.
using completion_handler = argument_dependent;
};
#else // defined(GENERATING_DOCUMENTATION)
template <typename Yield, typename Return, typename Executor>
struct coro_traits
{
using input_type = void;
using yield_type = Yield;
using return_type = Return;
using result_type = detail::coro_result_t<yield_type, return_type>;
using signature_type = result_type();
constexpr static bool is_noexcept = false;
using error_type = std::conditional_t<is_noexcept, void, std::exception_ptr>;
using completion_handler = detail::coro_handler_t<result_type, is_noexcept>;
};
template <typename T, typename Return, typename Executor>
struct coro_traits<T(), Return, Executor>
{
using input_type = void;
using yield_type = T;
using return_type = Return;
using result_type = detail::coro_result_t<yield_type, return_type>;
using signature_type = result_type();
constexpr static bool is_noexcept = false;
using error_type = std::conditional_t<is_noexcept, void, std::exception_ptr>;
using completion_handler = detail::coro_handler_t<result_type, is_noexcept>;
};
template <typename T, typename Return, typename Executor>
struct coro_traits<T() noexcept, Return, Executor>
{
using input_type = void;
using yield_type = T;
using return_type = Return;
using result_type = detail::coro_result_t<yield_type, return_type>;
using signature_type = result_type();
constexpr static bool is_noexcept = true;
using error_type = std::conditional_t<is_noexcept, void, std::exception_ptr>;
using completion_handler = detail::coro_handler_t<result_type, is_noexcept>;
};
template <typename T, typename U, typename Return, typename Executor>
struct coro_traits<T(U), Return, Executor>
{
using input_type = U;
using yield_type = T;
using return_type = Return;
using result_type = detail::coro_result_t<yield_type, return_type>;
using signature_type = result_type(input_type);
constexpr static bool is_noexcept = false;
using error_type = std::conditional_t<is_noexcept, void, std::exception_ptr>;
using completion_handler = detail::coro_handler_t<result_type, is_noexcept>;
};
template <typename T, typename U, typename Return, typename Executor>
struct coro_traits<T(U) noexcept, Return, Executor>
{
using input_type = U;
using yield_type = T;
using return_type = Return;
using result_type = detail::coro_result_t<yield_type, return_type>;
using signature_type = result_type(input_type);
constexpr static bool is_noexcept = true;
using error_type = std::conditional_t<is_noexcept, void, std::exception_ptr>;
using completion_handler = detail::coro_handler_t<result_type, is_noexcept>;
};
template <typename Executor>
struct coro_traits<void() noexcept, void, Executor>
{
using input_type = void;
using yield_type = void;
using return_type = void;
using result_type = detail::coro_result_t<yield_type, return_type>;
using signature_type = result_type(input_type);
constexpr static bool is_noexcept = true;
using error_type = std::conditional_t<is_noexcept, void, std::exception_ptr>;
using completion_handler = detail::coro_handler_t<result_type, is_noexcept>;
};
#endif // defined(GENERATING_DOCUMENTATION)
} // namespace experimental
} // namespace asio
#endif // ASIO_EXPERIMENTAL_DETAIL_CORO_TRAITS_HPP

View File

@@ -0,0 +1,361 @@
//
// experimental/detail/channel_operation.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2025 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_DETAIL_CHANNEL_OPERATION_HPP
#define ASIO_EXPERIMENTAL_DETAIL_CHANNEL_OPERATION_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/config.hpp"
#include "asio/associated_allocator.hpp"
#include "asio/associated_executor.hpp"
#include "asio/associated_immediate_executor.hpp"
#include "asio/detail/initiate_post.hpp"
#include "asio/detail/initiate_dispatch.hpp"
#include "asio/detail/op_queue.hpp"
#include "asio/detail/type_traits.hpp"
#include "asio/execution/executor.hpp"
#include "asio/execution/outstanding_work.hpp"
#include "asio/executor_work_guard.hpp"
#include "asio/prefer.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
namespace experimental {
namespace detail {
// Base class for all channel operations. A function pointer is used instead of
// virtual functions to avoid the associated overhead.
class channel_operation ASIO_INHERIT_TRACKED_HANDLER
{
public:
template <typename Executor, typename = void, typename = void>
class handler_work_base;
template <typename Handler, typename IoExecutor, typename = void>
class handler_work;
void destroy()
{
func_(this, destroy_op, 0);
}
protected:
enum action
{
destroy_op = 0,
immediate_op = 1,
post_op = 2,
dispatch_op = 3,
cancel_op = 4,
close_op = 5
};
typedef void (*func_type)(channel_operation*, action, void*);
channel_operation(func_type func)
: next_(0),
func_(func),
cancellation_key_(0)
{
}
// Prevents deletion through this type.
~channel_operation()
{
}
friend class asio::detail::op_queue_access;
channel_operation* next_;
func_type func_;
public:
// The operation key used for targeted cancellation.
void* cancellation_key_;
};
template <typename Executor, typename, typename>
class channel_operation::handler_work_base
{
public:
typedef decay_t<
prefer_result_t<Executor,
execution::outstanding_work_t::tracked_t
>
> executor_type;
handler_work_base(int, const Executor& ex)
: executor_(asio::prefer(ex, execution::outstanding_work.tracked))
{
}
const executor_type& get_executor() const noexcept
{
return executor_;
}
template <typename IoExecutor, typename Function, typename Handler>
void post(const IoExecutor& io_exec, Function& function, Handler&)
{
(asio::detail::initiate_post_with_executor<IoExecutor>(io_exec))(
static_cast<Function&&>(function));
}
template <typename Function, typename Handler>
void dispatch(Function& function, Handler& handler)
{
associated_allocator_t<Handler> allocator =
(get_associated_allocator)(handler);
asio::prefer(executor_,
execution::allocator(allocator)
).execute(static_cast<Function&&>(function));
}
private:
executor_type executor_;
};
template <typename Executor>
class channel_operation::handler_work_base<Executor,
enable_if_t<
execution::is_executor<Executor>::value
>,
enable_if_t<
can_require<Executor, execution::blocking_t::never_t>::value
>
>
{
public:
typedef decay_t<
prefer_result_t<Executor,
execution::outstanding_work_t::tracked_t
>
> executor_type;
handler_work_base(int, const Executor& ex)
: executor_(asio::prefer(ex, execution::outstanding_work.tracked))
{
}
const executor_type& get_executor() const noexcept
{
return executor_;
}
template <typename IoExecutor, typename Function, typename Handler>
void post(const IoExecutor&, Function& function, Handler& handler)
{
associated_allocator_t<Handler> allocator =
(get_associated_allocator)(handler);
asio::prefer(
asio::require(executor_, execution::blocking.never),
execution::allocator(allocator)
).execute(static_cast<Function&&>(function));
}
template <typename Function, typename Handler>
void dispatch(Function& function, Handler& handler)
{
associated_allocator_t<Handler> allocator =
(get_associated_allocator)(handler);
asio::prefer(executor_,
execution::allocator(allocator)
).execute(static_cast<Function&&>(function));
}
private:
executor_type executor_;
};
#if !defined(ASIO_NO_TS_EXECUTORS)
template <typename Executor>
class channel_operation::handler_work_base<Executor,
enable_if_t<
!execution::is_executor<Executor>::value
>
>
{
public:
typedef Executor executor_type;
handler_work_base(int, const Executor& ex)
: work_(ex)
{
}
executor_type get_executor() const noexcept
{
return work_.get_executor();
}
template <typename IoExecutor, typename Function, typename Handler>
void post(const IoExecutor&, Function& function, Handler& handler)
{
associated_allocator_t<Handler> allocator =
(get_associated_allocator)(handler);
work_.get_executor().post(
static_cast<Function&&>(function), allocator);
}
template <typename Function, typename Handler>
void dispatch(Function& function, Handler& handler)
{
associated_allocator_t<Handler> allocator =
(get_associated_allocator)(handler);
work_.get_executor().dispatch(
static_cast<Function&&>(function), allocator);
}
private:
executor_work_guard<Executor> work_;
};
#endif // !defined(ASIO_NO_TS_EXECUTORS)
template <typename Handler, typename IoExecutor, typename>
class channel_operation::handler_work :
channel_operation::handler_work_base<IoExecutor>,
channel_operation::handler_work_base<
associated_executor_t<Handler, IoExecutor>, IoExecutor>
{
public:
typedef channel_operation::handler_work_base<IoExecutor> base1_type;
typedef channel_operation::handler_work_base<
associated_executor_t<Handler, IoExecutor>, IoExecutor>
base2_type;
handler_work(Handler& handler, const IoExecutor& io_ex) noexcept
: base1_type(0, io_ex),
base2_type(0, (get_associated_executor)(handler, io_ex))
{
}
template <typename Function>
void post(Function& function, Handler& handler)
{
base2_type::post(base1_type::get_executor(), function, handler);
}
template <typename Function>
void dispatch(Function& function, Handler& handler)
{
base2_type::dispatch(function, handler);
}
template <typename Function>
void immediate(Function& function, Handler& handler, ...)
{
typedef associated_immediate_executor_t<Handler,
typename base1_type::executor_type> immediate_ex_type;
immediate_ex_type immediate_ex = (get_associated_immediate_executor)(
handler, base1_type::get_executor());
(asio::detail::initiate_dispatch_with_executor<immediate_ex_type>(
immediate_ex))(static_cast<Function&&>(function));
}
template <typename Function>
void immediate(Function& function, Handler&,
enable_if_t<
is_same<
typename associated_immediate_executor<
conditional_t<false, Function, Handler>,
typename base1_type::executor_type>::
asio_associated_immediate_executor_is_unspecialised,
void
>::value
>*)
{
(asio::detail::initiate_post_with_executor<
typename base1_type::executor_type>(
base1_type::get_executor()))(
static_cast<Function&&>(function));
}
};
template <typename Handler, typename IoExecutor>
class channel_operation::handler_work<
Handler, IoExecutor,
enable_if_t<
is_same<
typename associated_executor<Handler,
IoExecutor>::asio_associated_executor_is_unspecialised,
void
>::value
>
> : handler_work_base<IoExecutor>
{
public:
typedef channel_operation::handler_work_base<IoExecutor> base1_type;
handler_work(Handler&, const IoExecutor& io_ex) noexcept
: base1_type(0, io_ex)
{
}
template <typename Function>
void post(Function& function, Handler& handler)
{
base1_type::post(base1_type::get_executor(), function, handler);
}
template <typename Function>
void dispatch(Function& function, Handler& handler)
{
base1_type::dispatch(function, handler);
}
template <typename Function>
void immediate(Function& function, Handler& handler, ...)
{
typedef associated_immediate_executor_t<Handler,
typename base1_type::executor_type> immediate_ex_type;
immediate_ex_type immediate_ex = (get_associated_immediate_executor)(
handler, base1_type::get_executor());
(asio::detail::initiate_dispatch_with_executor<immediate_ex_type>(
immediate_ex))(static_cast<Function&&>(function));
}
template <typename Function>
void immediate(Function& function, Handler& handler,
enable_if_t<
is_same<
typename associated_immediate_executor<
conditional_t<false, Function, Handler>,
typename base1_type::executor_type>::
asio_associated_immediate_executor_is_unspecialised,
void
>::value
>*)
{
base1_type::post(base1_type::get_executor(), function, handler);
}
};
} // namespace detail
} // namespace experimental
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_EXPERIMENTAL_DETAIL_CHANNEL_OPERATION_HPP

View File

@@ -0,0 +1,126 @@
//
// experimental/detail/channel_receive_op.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2025 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_DETAIL_CHANNEL_RECEIVE_OP_HPP
#define ASIO_EXPERIMENTAL_DETAIL_CHANNEL_RECEIVE_OP_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/bind_handler.hpp"
#include "asio/detail/completion_handler.hpp"
#include "asio/detail/handler_alloc_helpers.hpp"
#include "asio/error.hpp"
#include "asio/experimental/detail/channel_operation.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
namespace experimental {
namespace detail {
template <typename Payload>
class channel_receive : public channel_operation
{
public:
void immediate(Payload payload)
{
func_(this, immediate_op, &payload);
}
void post(Payload payload)
{
func_(this, post_op, &payload);
}
void dispatch(Payload payload)
{
func_(this, dispatch_op, &payload);
}
protected:
channel_receive(func_type func)
: channel_operation(func)
{
}
};
template <typename Payload, typename Handler, typename IoExecutor>
class channel_receive_op : public channel_receive<Payload>
{
public:
ASIO_DEFINE_HANDLER_PTR(channel_receive_op);
template <typename... Args>
channel_receive_op(Handler& handler, const IoExecutor& io_ex)
: channel_receive<Payload>(&channel_receive_op::do_action),
handler_(static_cast<Handler&&>(handler)),
work_(handler_, io_ex)
{
}
static void do_action(channel_operation* base,
channel_operation::action a, void* v)
{
// Take ownership of the operation object.
channel_receive_op* o(static_cast<channel_receive_op*>(base));
ptr p = { asio::detail::addressof(o->handler_), o, o };
ASIO_HANDLER_COMPLETION((*o));
// Take ownership of the operation's outstanding work.
channel_operation::handler_work<Handler, IoExecutor> w(
static_cast<channel_operation::handler_work<Handler, IoExecutor>&&>(
o->work_));
// Make a copy of the handler so that the memory can be deallocated before
// the handler is posted. Even if we're not about to post the handler, a
// sub-object of the handler may be the true owner of the memory associated
// with the handler. Consequently, a local copy of the handler is required
// to ensure that any owning sub-object remains valid until after we have
// deallocated the memory here.
if (a != channel_operation::destroy_op)
{
Payload* payload = static_cast<Payload*>(v);
asio::detail::completion_payload_handler<Payload, Handler> handler(
static_cast<Payload&&>(*payload), o->handler_);
p.h = asio::detail::addressof(handler.handler_);
p.reset();
ASIO_HANDLER_INVOCATION_BEGIN(());
if (a == channel_operation::immediate_op)
w.immediate(handler, handler.handler_, 0);
else if (a == channel_operation::dispatch_op)
w.dispatch(handler, handler.handler_);
else
w.post(handler, handler.handler_);
ASIO_HANDLER_INVOCATION_END;
}
else
{
asio::detail::binder0<Handler> handler(o->handler_);
p.h = asio::detail::addressof(handler.handler_);
p.reset();
}
}
private:
Handler handler_;
channel_operation::handler_work<Handler, IoExecutor> work_;
};
} // namespace detail
} // namespace experimental
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_EXPERIMENTAL_DETAIL_CHANNEL_RECEIVE_OP_HPP

View File

@@ -0,0 +1,200 @@
//
// experimental/detail/channel_send_functions.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2025 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_DETAIL_CHANNEL_SEND_FUNCTIONS_HPP
#define ASIO_EXPERIMENTAL_DETAIL_CHANNEL_SEND_FUNCTIONS_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/completion_message.hpp"
#include "asio/detail/type_traits.hpp"
#include "asio/error_code.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
namespace experimental {
namespace detail {
template <typename Derived, typename Executor, typename... Signatures>
class channel_send_functions;
template <typename Derived, typename Executor, typename R, typename... Args>
class channel_send_functions<Derived, Executor, R(Args...)>
{
public:
template <typename... Args2>
enable_if_t<
is_constructible<asio::detail::completion_message<R(Args...)>,
int, Args2...>::value,
bool
> try_send(Args2&&... args)
{
typedef asio::detail::completion_message<R(Args...)> message_type;
Derived* self = static_cast<Derived*>(this);
return self->service_->template try_send<message_type>(
self->impl_, false, static_cast<Args2&&>(args)...);
}
template <typename... Args2>
enable_if_t<
is_constructible<asio::detail::completion_message<R(Args...)>,
int, Args2...>::value,
bool
> try_send_via_dispatch(Args2&&... args)
{
typedef asio::detail::completion_message<R(Args...)> message_type;
Derived* self = static_cast<Derived*>(this);
return self->service_->template try_send<message_type>(
self->impl_, true, static_cast<Args2&&>(args)...);
}
template <typename... Args2>
enable_if_t<
is_constructible<asio::detail::completion_message<R(Args...)>,
int, Args2...>::value,
std::size_t
> try_send_n(std::size_t count, Args2&&... args)
{
typedef asio::detail::completion_message<R(Args...)> message_type;
Derived* self = static_cast<Derived*>(this);
return self->service_->template try_send_n<message_type>(
self->impl_, count, false, static_cast<Args2&&>(args)...);
}
template <typename... Args2>
enable_if_t<
is_constructible<asio::detail::completion_message<R(Args...)>,
int, Args2...>::value,
std::size_t
> try_send_n_via_dispatch(std::size_t count, Args2&&... args)
{
typedef asio::detail::completion_message<R(Args...)> message_type;
Derived* self = static_cast<Derived*>(this);
return self->service_->template try_send_n<message_type>(
self->impl_, count, true, static_cast<Args2&&>(args)...);
}
template <
ASIO_COMPLETION_TOKEN_FOR(void (asio::error_code))
CompletionToken ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(Executor)>
auto async_send(Args... args,
CompletionToken&& token
ASIO_DEFAULT_COMPLETION_TOKEN(Executor))
-> decltype(
async_initiate<CompletionToken, void (asio::error_code)>(
declval<typename conditional_t<false, CompletionToken,
Derived>::initiate_async_send>(), token,
declval<typename conditional_t<false, CompletionToken,
Derived>::payload_type>()))
{
typedef typename Derived::payload_type payload_type;
typedef asio::detail::completion_message<R(Args...)> message_type;
Derived* self = static_cast<Derived*>(this);
return async_initiate<CompletionToken, void (asio::error_code)>(
typename Derived::initiate_async_send(self), token,
payload_type(message_type(0, static_cast<Args&&>(args)...)));
}
};
template <typename Derived, typename Executor,
typename R, typename... Args, typename... Signatures>
class channel_send_functions<Derived, Executor, R(Args...), Signatures...> :
public channel_send_functions<Derived, Executor, Signatures...>
{
public:
using channel_send_functions<Derived, Executor, Signatures...>::try_send;
using channel_send_functions<Derived, Executor, Signatures...>::async_send;
template <typename... Args2>
enable_if_t<
is_constructible<asio::detail::completion_message<R(Args...)>,
int, Args2...>::value,
bool
> try_send(Args2&&... args)
{
typedef asio::detail::completion_message<R(Args...)> message_type;
Derived* self = static_cast<Derived*>(this);
return self->service_->template try_send<message_type>(
self->impl_, false, static_cast<Args2&&>(args)...);
}
template <typename... Args2>
enable_if_t<
is_constructible<asio::detail::completion_message<R(Args...)>,
int, Args2...>::value,
bool
> try_send_via_dispatch(Args2&&... args)
{
typedef asio::detail::completion_message<R(Args...)> message_type;
Derived* self = static_cast<Derived*>(this);
return self->service_->template try_send<message_type>(
self->impl_, true, static_cast<Args2&&>(args)...);
}
template <typename... Args2>
enable_if_t<
is_constructible<asio::detail::completion_message<R(Args...)>,
int, Args2...>::value,
std::size_t
> try_send_n(std::size_t count, Args2&&... args)
{
typedef asio::detail::completion_message<R(Args...)> message_type;
Derived* self = static_cast<Derived*>(this);
return self->service_->template try_send_n<message_type>(
self->impl_, count, false, static_cast<Args2&&>(args)...);
}
template <typename... Args2>
enable_if_t<
is_constructible<asio::detail::completion_message<R(Args...)>,
int, Args2...>::value,
std::size_t
> try_send_n_via_dispatch(std::size_t count, Args2&&... args)
{
typedef asio::detail::completion_message<R(Args...)> message_type;
Derived* self = static_cast<Derived*>(this);
return self->service_->template try_send_n<message_type>(
self->impl_, count, true, static_cast<Args2&&>(args)...);
}
template <
ASIO_COMPLETION_TOKEN_FOR(void (asio::error_code))
CompletionToken ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(Executor)>
auto async_send(Args... args,
CompletionToken&& token
ASIO_DEFAULT_COMPLETION_TOKEN(Executor))
-> decltype(
async_initiate<CompletionToken, void (asio::error_code)>(
declval<typename conditional_t<false, CompletionToken,
Derived>::initiate_async_send>(), token,
declval<typename conditional_t<false, CompletionToken,
Derived>::payload_type>()))
{
typedef typename Derived::payload_type payload_type;
typedef asio::detail::completion_message<R(Args...)> message_type;
Derived* self = static_cast<Derived*>(this);
return async_initiate<CompletionToken, void (asio::error_code)>(
typename Derived::initiate_async_send(self), token,
payload_type(message_type(0, static_cast<Args&&>(args)...)));
}
};
} // namespace detail
} // namespace experimental
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_EXPERIMENTAL_DETAIL_CHANNEL_SEND_FUNCTIONS_HPP

View File

@@ -0,0 +1,147 @@
//
// experimental/detail/channel_send_op.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2025 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_DETAIL_CHANNEL_SEND_OP_HPP
#define ASIO_EXPERIMENTAL_DETAIL_CHANNEL_SEND_OP_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/bind_handler.hpp"
#include "asio/detail/handler_alloc_helpers.hpp"
#include "asio/error.hpp"
#include "asio/experimental/channel_error.hpp"
#include "asio/experimental/detail/channel_operation.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
namespace experimental {
namespace detail {
template <typename Payload>
class channel_send : public channel_operation
{
public:
Payload get_payload()
{
return static_cast<Payload&&>(payload_);
}
void immediate()
{
func_(this, immediate_op, 0);
}
void post()
{
func_(this, post_op, 0);
}
void cancel()
{
func_(this, cancel_op, 0);
}
void close()
{
func_(this, close_op, 0);
}
protected:
channel_send(func_type func, Payload&& payload)
: channel_operation(func),
payload_(static_cast<Payload&&>(payload))
{
}
private:
Payload payload_;
};
template <typename Payload, typename Handler, typename IoExecutor>
class channel_send_op : public channel_send<Payload>
{
public:
ASIO_DEFINE_HANDLER_PTR(channel_send_op);
channel_send_op(Payload&& payload,
Handler& handler, const IoExecutor& io_ex)
: channel_send<Payload>(&channel_send_op::do_action,
static_cast<Payload&&>(payload)),
handler_(static_cast<Handler&&>(handler)),
work_(handler_, io_ex)
{
}
static void do_action(channel_operation* base,
channel_operation::action a, void*)
{
// Take ownership of the operation object.
channel_send_op* o(static_cast<channel_send_op*>(base));
ptr p = { asio::detail::addressof(o->handler_), o, o };
ASIO_HANDLER_COMPLETION((*o));
// Take ownership of the operation's outstanding work.
channel_operation::handler_work<Handler, IoExecutor> w(
static_cast<channel_operation::handler_work<Handler, IoExecutor>&&>(
o->work_));
asio::error_code ec;
switch (a)
{
case channel_operation::cancel_op:
ec = error::channel_cancelled;
break;
case channel_operation::close_op:
ec = error::channel_closed;
break;
default:
break;
}
// Make a copy of the handler so that the memory can be deallocated before
// the handler is posted. Even if we're not about to post the handler, a
// sub-object of the handler may be the true owner of the memory associated
// with the handler. Consequently, a local copy of the handler is required
// to ensure that any owning sub-object remains valid until after we have
// deallocated the memory here.
asio::detail::binder1<Handler, asio::error_code>
handler(o->handler_, ec);
p.h = asio::detail::addressof(handler.handler_);
p.reset();
// Post the completion if required.
if (a != channel_operation::destroy_op)
{
ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_));
if (a == channel_operation::immediate_op)
w.immediate(handler, handler.handler_, 0);
else
w.post(handler, handler.handler_);
ASIO_HANDLER_INVOCATION_END;
}
}
private:
Handler handler_;
channel_operation::handler_work<Handler, IoExecutor> work_;
};
} // namespace detail
} // namespace experimental
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_EXPERIMENTAL_DETAIL_CHANNEL_SEND_OP_HPP

View File

@@ -0,0 +1,679 @@
//
// experimental/detail/channel_service.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2025 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_DETAIL_CHANNEL_SERVICE_HPP
#define ASIO_EXPERIMENTAL_DETAIL_CHANNEL_SERVICE_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/config.hpp"
#include "asio/associated_cancellation_slot.hpp"
#include "asio/cancellation_type.hpp"
#include "asio/detail/completion_message.hpp"
#include "asio/detail/completion_payload.hpp"
#include "asio/detail/completion_payload_handler.hpp"
#include "asio/detail/mutex.hpp"
#include "asio/detail/op_queue.hpp"
#include "asio/execution_context.hpp"
#include "asio/experimental/detail/channel_receive_op.hpp"
#include "asio/experimental/detail/channel_send_op.hpp"
#include "asio/experimental/detail/has_signature.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
namespace experimental {
namespace detail {
template <typename Mutex>
class channel_service
: public asio::detail::execution_context_service_base<
channel_service<Mutex>>
{
public:
// Possible states for a channel end.
enum state
{
buffer = 0,
waiter = 1,
block = 2,
closed = 3
};
// The base implementation type of all channels.
struct base_implementation_type
{
// Default constructor.
base_implementation_type()
: receive_state_(block),
send_state_(block),
max_buffer_size_(0),
next_(0),
prev_(0)
{
}
// The current state of the channel.
state receive_state_ : 16;
state send_state_ : 16;
// The maximum number of elements that may be buffered in the channel.
std::size_t max_buffer_size_;
// The operations that are waiting on the channel.
asio::detail::op_queue<channel_operation> waiters_;
// Pointers to adjacent channel implementations in linked list.
base_implementation_type* next_;
base_implementation_type* prev_;
// The mutex type to protect the internal implementation.
mutable Mutex mutex_;
};
// The implementation for a specific value type.
template <typename Traits, typename... Signatures>
struct implementation_type;
// Constructor.
channel_service(asio::execution_context& ctx);
// Destroy all user-defined handler objects owned by the service.
void shutdown();
// Construct a new channel implementation.
void construct(base_implementation_type& impl, std::size_t max_buffer_size);
// Destroy a channel implementation.
template <typename Traits, typename... Signatures>
void destroy(implementation_type<Traits, Signatures...>& impl);
// Move-construct a new channel implementation.
template <typename Traits, typename... Signatures>
void move_construct(implementation_type<Traits, Signatures...>& impl,
implementation_type<Traits, Signatures...>& other_impl);
// Move-assign from another channel implementation.
template <typename Traits, typename... Signatures>
void move_assign(implementation_type<Traits, Signatures...>& impl,
channel_service& other_service,
implementation_type<Traits, Signatures...>& other_impl);
// Get the capacity of the channel.
std::size_t capacity(
const base_implementation_type& impl) const noexcept;
// Determine whether the channel is open.
bool is_open(const base_implementation_type& impl) const noexcept;
// Reset the channel to its initial state.
template <typename Traits, typename... Signatures>
void reset(implementation_type<Traits, Signatures...>& impl);
// Close the channel.
template <typename Traits, typename... Signatures>
void close(implementation_type<Traits, Signatures...>& impl);
// Cancel all operations associated with the channel.
template <typename Traits, typename... Signatures>
void cancel(implementation_type<Traits, Signatures...>& impl);
// Cancel the operation associated with the channel that has the given key.
template <typename Traits, typename... Signatures>
void cancel_by_key(implementation_type<Traits, Signatures...>& impl,
void* cancellation_key);
// Determine whether a value can be read from the channel without blocking.
bool ready(const base_implementation_type& impl) const noexcept;
// Synchronously send a new value into the channel.
template <typename Message, typename Traits,
typename... Signatures, typename... Args>
bool try_send(implementation_type<Traits, Signatures...>& impl,
bool via_dispatch, Args&&... args);
// Synchronously send a number of new values into the channel.
template <typename Message, typename Traits,
typename... Signatures, typename... Args>
std::size_t try_send_n(implementation_type<Traits, Signatures...>& impl,
std::size_t count, bool via_dispatch, Args&&... args);
// Asynchronously send a new value into the channel.
template <typename Traits, typename... Signatures,
typename Handler, typename IoExecutor>
void async_send(implementation_type<Traits, Signatures...>& impl,
typename implementation_type<Traits,
Signatures...>::payload_type&& payload,
Handler& handler, const IoExecutor& io_ex)
{
associated_cancellation_slot_t<Handler> slot
= asio::get_associated_cancellation_slot(handler);
// Allocate and construct an operation to wrap the handler.
typedef channel_send_op<
typename implementation_type<Traits, Signatures...>::payload_type,
Handler, IoExecutor> op;
typename op::ptr p = { asio::detail::addressof(handler),
op::ptr::allocate(handler), 0 };
p.p = new (p.v) op(static_cast<typename implementation_type<
Traits, Signatures...>::payload_type&&>(payload), handler, io_ex);
// Optionally register for per-operation cancellation.
if (slot.is_connected())
{
p.p->cancellation_key_ =
&slot.template emplace<op_cancellation<Traits, Signatures...>>(
this, &impl);
}
ASIO_HANDLER_CREATION((this->context(), *p.p,
"channel", &impl, 0, "async_send"));
start_send_op(impl, p.p);
p.v = p.p = 0;
}
// Synchronously receive a value from the channel.
template <typename Traits, typename... Signatures, typename Handler>
bool try_receive(implementation_type<Traits, Signatures...>& impl,
Handler&& handler);
// Asynchronously receive a value from the channel.
template <typename Traits, typename... Signatures,
typename Handler, typename IoExecutor>
void async_receive(implementation_type<Traits, Signatures...>& impl,
Handler& handler, const IoExecutor& io_ex)
{
associated_cancellation_slot_t<Handler> slot
= asio::get_associated_cancellation_slot(handler);
// Allocate and construct an operation to wrap the handler.
typedef channel_receive_op<
typename implementation_type<Traits, Signatures...>::payload_type,
Handler, IoExecutor> op;
typename op::ptr p = { asio::detail::addressof(handler),
op::ptr::allocate(handler), 0 };
p.p = new (p.v) op(handler, io_ex);
// Optionally register for per-operation cancellation.
if (slot.is_connected())
{
p.p->cancellation_key_ =
&slot.template emplace<op_cancellation<Traits, Signatures...>>(
this, &impl);
}
ASIO_HANDLER_CREATION((this->context(), *p.p,
"channel", &impl, 0, "async_receive"));
start_receive_op(impl, p.p);
p.v = p.p = 0;
}
private:
// Helper function object to handle a closed notification.
template <typename Payload, typename Signature>
struct post_receive
{
explicit post_receive(channel_receive<Payload>* op)
: op_(op)
{
}
template <typename... Args>
void operator()(Args&&... args)
{
op_->post(
asio::detail::completion_message<Signature>(0,
static_cast<Args&&>(args)...));
}
channel_receive<Payload>* op_;
};
// Destroy a base channel implementation.
void base_destroy(base_implementation_type& impl);
// Helper function to start an asynchronous put operation.
template <typename Traits, typename... Signatures>
void start_send_op(implementation_type<Traits, Signatures...>& impl,
channel_send<typename implementation_type<
Traits, Signatures...>::payload_type>* send_op);
// Helper function to start an asynchronous get operation.
template <typename Traits, typename... Signatures>
void start_receive_op(implementation_type<Traits, Signatures...>& impl,
channel_receive<typename implementation_type<
Traits, Signatures...>::payload_type>* receive_op);
// Helper class used to implement per-operation cancellation.
template <typename Traits, typename... Signatures>
class op_cancellation
{
public:
op_cancellation(channel_service* s,
implementation_type<Traits, Signatures...>* impl)
: service_(s),
impl_(impl)
{
}
void operator()(cancellation_type_t type)
{
if (!!(type &
(cancellation_type::terminal
| cancellation_type::partial
| cancellation_type::total)))
{
service_->cancel_by_key(*impl_, this);
}
}
private:
channel_service* service_;
implementation_type<Traits, Signatures...>* impl_;
};
// Mutex to protect access to the linked list of implementations.
asio::detail::mutex mutex_;
// The head of a linked list of all implementations.
base_implementation_type* impl_list_;
};
// The implementation for a specific value type.
template <typename Mutex>
template <typename Traits, typename... Signatures>
struct channel_service<Mutex>::implementation_type : base_implementation_type
{
// The traits type associated with the channel.
typedef typename Traits::template rebind<Signatures...>::other traits_type;
// Type of an element stored in the buffer.
typedef conditional_t<
has_signature<
typename traits_type::receive_cancelled_signature,
Signatures...
>::value,
conditional_t<
has_signature<
typename traits_type::receive_closed_signature,
Signatures...
>::value,
asio::detail::completion_payload<Signatures...>,
asio::detail::completion_payload<
Signatures...,
typename traits_type::receive_closed_signature
>
>,
conditional_t<
has_signature<
typename traits_type::receive_closed_signature,
Signatures...,
typename traits_type::receive_cancelled_signature
>::value,
asio::detail::completion_payload<
Signatures...,
typename traits_type::receive_cancelled_signature
>,
asio::detail::completion_payload<
Signatures...,
typename traits_type::receive_cancelled_signature,
typename traits_type::receive_closed_signature
>
>
> payload_type;
// Move from another buffer.
void buffer_move_from(implementation_type& other)
{
buffer_ = static_cast<
typename traits_type::template container<payload_type>::type&&>(
other.buffer_);
other.buffer_clear();
}
// Get number of buffered elements.
std::size_t buffer_size() const
{
return buffer_.size();
}
// Push a new value to the back of the buffer.
void buffer_push(payload_type payload)
{
buffer_.push_back(static_cast<payload_type&&>(payload));
}
// Push new values to the back of the buffer.
std::size_t buffer_push_n(std::size_t count, payload_type payload)
{
std::size_t i = 0;
for (; i < count && buffer_.size() < this->max_buffer_size_; ++i)
buffer_.push_back(payload);
return i;
}
// Get the element at the front of the buffer.
payload_type buffer_front()
{
return static_cast<payload_type&&>(buffer_.front());
}
// Pop a value from the front of the buffer.
void buffer_pop()
{
buffer_.pop_front();
}
// Clear all buffered values.
void buffer_clear()
{
buffer_.clear();
}
private:
// Buffered values.
typename traits_type::template container<payload_type>::type buffer_;
};
// The implementation for a void value type.
template <typename Mutex>
template <typename Traits, typename R>
struct channel_service<Mutex>::implementation_type<Traits, R()>
: channel_service::base_implementation_type
{
// The traits type associated with the channel.
typedef typename Traits::template rebind<R()>::other traits_type;
// Type of an element stored in the buffer.
typedef conditional_t<
has_signature<
typename traits_type::receive_cancelled_signature,
R()
>::value,
conditional_t<
has_signature<
typename traits_type::receive_closed_signature,
R()
>::value,
asio::detail::completion_payload<R()>,
asio::detail::completion_payload<
R(),
typename traits_type::receive_closed_signature
>
>,
conditional_t<
has_signature<
typename traits_type::receive_closed_signature,
R(),
typename traits_type::receive_cancelled_signature
>::value,
asio::detail::completion_payload<
R(),
typename traits_type::receive_cancelled_signature
>,
asio::detail::completion_payload<
R(),
typename traits_type::receive_cancelled_signature,
typename traits_type::receive_closed_signature
>
>
> payload_type;
// Construct with empty buffer.
implementation_type()
: buffer_(0)
{
}
// Move from another buffer.
void buffer_move_from(implementation_type& other)
{
buffer_ = other.buffer_;
other.buffer_ = 0;
}
// Get number of buffered elements.
std::size_t buffer_size() const
{
return buffer_;
}
// Push a new value to the back of the buffer.
void buffer_push(payload_type)
{
++buffer_;
}
// Push new values to the back of the buffer.
std::size_t buffer_push_n(std::size_t count, payload_type)
{
std::size_t available = this->max_buffer_size_ - buffer_;
count = (count < available) ? count : available;
buffer_ += count;
return count;
}
// Get the element at the front of the buffer.
payload_type buffer_front()
{
return payload_type(asio::detail::completion_message<R()>(0));
}
// Pop a value from the front of the buffer.
void buffer_pop()
{
--buffer_;
}
// Clear all values from the buffer.
void buffer_clear()
{
buffer_ = 0;
}
private:
// Number of buffered "values".
std::size_t buffer_;
};
// The implementation for an error_code signature.
template <typename Mutex>
template <typename Traits, typename R>
struct channel_service<Mutex>::implementation_type<
Traits, R(asio::error_code)>
: channel_service::base_implementation_type
{
// The traits type associated with the channel.
typedef typename Traits::template rebind<R(asio::error_code)>::other
traits_type;
// Type of an element stored in the buffer.
typedef conditional_t<
has_signature<
typename traits_type::receive_cancelled_signature,
R(asio::error_code)
>::value,
conditional_t<
has_signature<
typename traits_type::receive_closed_signature,
R(asio::error_code)
>::value,
asio::detail::completion_payload<R(asio::error_code)>,
asio::detail::completion_payload<
R(asio::error_code),
typename traits_type::receive_closed_signature
>
>,
conditional_t<
has_signature<
typename traits_type::receive_closed_signature,
R(asio::error_code),
typename traits_type::receive_cancelled_signature
>::value,
asio::detail::completion_payload<
R(asio::error_code),
typename traits_type::receive_cancelled_signature
>,
asio::detail::completion_payload<
R(asio::error_code),
typename traits_type::receive_cancelled_signature,
typename traits_type::receive_closed_signature
>
>
> payload_type;
// Construct with empty buffer.
implementation_type()
: size_(0)
{
first_.count_ = 0;
}
// Move from another buffer.
void buffer_move_from(implementation_type& other)
{
size_ = other.size_;
other.size_ = 0;
first_ = other.first_;
other.first_.count_ = 0;
rest_ = static_cast<
typename traits_type::template container<buffered_value>::type&&>(
other.rest_);
other.buffer_clear();
}
// Get number of buffered elements.
std::size_t buffer_size() const
{
return size_;
}
// Push a new value to the back of the buffer.
void buffer_push(payload_type payload)
{
buffered_value& last = rest_.empty() ? first_ : rest_.back();
if (last.count_ == 0)
{
value_handler handler{last.value_};
payload.receive(handler);
last.count_ = 1;
}
else
{
asio::error_code value{last.value_};
value_handler handler{value};
payload.receive(handler);
if (last.value_ == value)
++last.count_;
else
rest_.push_back({value, 1});
}
++size_;
}
// Push new values to the back of the buffer.
std::size_t buffer_push_n(std::size_t count, payload_type payload)
{
std::size_t available = this->max_buffer_size_ - size_;
count = (count < available) ? count : available;
if (count > 0)
{
buffered_value& last = rest_.empty() ? first_ : rest_.back();
if (last.count_ == 0)
{
payload.receive(value_handler{last.value_});
last.count_ = count;
}
else
{
asio::error_code value{last.value_};
payload.receive(value_handler{value});
if (last.value_ == value)
last.count_ += count;
else
rest_.push_back({value, count});
}
size_ += count;
}
return count;
}
// Get the element at the front of the buffer.
payload_type buffer_front()
{
return payload_type({0, first_.value_});
}
// Pop a value from the front of the buffer.
void buffer_pop()
{
--size_;
if (--first_.count_ == 0 && !rest_.empty())
{
first_ = rest_.front();
rest_.pop_front();
}
}
// Clear all values from the buffer.
void buffer_clear()
{
size_ = 0;
first_.count_ = 0;
rest_.clear();
}
private:
struct buffered_value
{
asio::error_code value_;
std::size_t count_;
};
struct value_handler
{
asio::error_code& target_;
template <typename... Args>
void operator()(const asio::error_code& value, Args&&...)
{
target_ = value;
}
};
buffered_value& last_value()
{
return rest_.empty() ? first_ : rest_.back();
}
// Total number of buffered values.
std::size_t size_;
// The first buffered value is maintained as a separate data member to avoid
// allocating space in the container in the common case.
buffered_value first_;
// The rest of the buffered values.
typename traits_type::template container<buffered_value>::type rest_;
};
} // namespace detail
} // namespace experimental
} // namespace asio
#include "asio/detail/pop_options.hpp"
#include "asio/experimental/detail/impl/channel_service.hpp"
#endif // ASIO_EXPERIMENTAL_DETAIL_CHANNEL_SERVICE_HPP

View File

@@ -0,0 +1,169 @@
//
// experimental/detail/coro_completion_handler.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2021-2023 Klemens D. Morgenstern
// (klemens dot morgenstern at gmx dot net)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_EXPERIMENTAL_DETAIL_CORO_COMPLETION_HANDLER_HPP
#define ASIO_EXPERIMENTAL_DETAIL_CORO_COMPLETION_HANDLER_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/config.hpp"
#include "asio/deferred.hpp"
#include "asio/experimental/coro.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
namespace experimental {
namespace detail {
template <typename Promise, typename... Args>
struct coro_completion_handler
{
coro_completion_handler(coroutine_handle<Promise> h,
std::optional<std::tuple<Args...>>& result)
: self(h),
result(result)
{
}
coro_completion_handler(coro_completion_handler&&) = default;
coroutine_handle<Promise> self;
std::optional<std::tuple<Args...>>& result;
using promise_type = Promise;
void operator()(Args... args)
{
result.emplace(std::move(args)...);
self.resume();
}
using allocator_type = typename promise_type::allocator_type;
allocator_type get_allocator() const noexcept
{
return self.promise().get_allocator();
}
using executor_type = typename promise_type::executor_type;
executor_type get_executor() const noexcept
{
return self.promise().get_executor();
}
using cancellation_slot_type = typename promise_type::cancellation_slot_type;
cancellation_slot_type get_cancellation_slot() const noexcept
{
return self.promise().get_cancellation_slot();
}
};
template <typename Signature>
struct coro_completion_handler_type;
template <typename... Args>
struct coro_completion_handler_type<void(Args...)>
{
using type = std::tuple<Args...>;
template <typename Promise>
using completion_handler = coro_completion_handler<Promise, Args...>;
};
template <typename Signature>
using coro_completion_handler_type_t =
typename coro_completion_handler_type<Signature>::type;
inline void coro_interpret_result(std::tuple<>&&)
{
}
template <typename... Args>
inline auto coro_interpret_result(std::tuple<Args...>&& args)
{
return std::move(args);
}
template <typename... Args>
auto coro_interpret_result(std::tuple<std::exception_ptr, Args...>&& args)
{
if (std::get<0>(args))
std::rethrow_exception(std::get<0>(args));
return std::apply(
[](auto, auto&&... rest)
{
return std::make_tuple(std::move(rest)...);
}, std::move(args));
}
template <typename... Args>
auto coro_interpret_result(
std::tuple<asio::error_code, Args...>&& args)
{
if (std::get<0>(args))
asio::detail::throw_exception(
asio::system_error(std::get<0>(args)));
return std::apply(
[](auto, auto&&... rest)
{
return std::make_tuple(std::move(rest)...);
}, std::move(args));
}
template <typename Arg>
inline auto coro_interpret_result(std::tuple<Arg>&& args)
{
return std::get<0>(std::move(args));
}
template <typename Arg>
auto coro_interpret_result(std::tuple<std::exception_ptr, Arg>&& args)
{
if (std::get<0>(args))
std::rethrow_exception(std::get<0>(args));
return std::get<1>(std::move(args));
}
inline auto coro_interpret_result(
std::tuple<asio::error_code>&& args)
{
if (std::get<0>(args))
asio::detail::throw_exception(
asio::system_error(std::get<0>(args)));
}
inline auto coro_interpret_result(std::tuple<std::exception_ptr>&& args)
{
if (std::get<0>(args))
std::rethrow_exception(std::get<0>(args));
}
template <typename Arg>
auto coro_interpret_result(std::tuple<asio::error_code, Arg>&& args)
{
if (std::get<0>(args))
asio::detail::throw_exception(
asio::system_error(std::get<0>(args)));
return std::get<1>(std::move(args));
}
} // namespace detail
} // namespace experimental
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_EXPERIMENTAL_DETAIL_CORO_COMPLETION_HANDLER_HPP

View File

@@ -0,0 +1,141 @@
//
// experimental/detail/coro_promise_allocator.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2021-2023 Klemens D. Morgenstern
// (klemens dot morgenstern at gmx dot net)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_EXPERIMENTAL_DETAIL_CORO_PROMISE_ALLOCATOR_HPP
#define ASIO_EXPERIMENTAL_DETAIL_CORO_PROMISE_ALLOCATOR_HPP
#include "asio/detail/config.hpp"
#include <limits>
#include "asio/experimental/coro_traits.hpp"
namespace asio {
namespace experimental {
namespace detail {
/// Allocate the memory and put the allocator behind the coro memory
template <typename AllocatorType>
void* allocate_coroutine(const std::size_t size, AllocatorType alloc_)
{
using alloc_type = typename std::allocator_traits<AllocatorType>::template
rebind_alloc<unsigned char>;
alloc_type alloc{alloc_};
const auto align_needed = size % alignof(alloc_type);
const auto align_offset = align_needed != 0
? alignof(alloc_type) - align_needed : 0ull;
const auto alloc_size = size + sizeof(alloc_type) + align_offset;
const auto raw =
std::allocator_traits<alloc_type>::allocate(alloc, alloc_size);
new(raw + size + align_offset) alloc_type(std::move(alloc));
return raw;
}
/// Deallocate the memory and destroy the allocator in the coro memory.
template <typename AllocatorType>
void deallocate_coroutine(void* raw_, const std::size_t size)
{
using alloc_type = typename std::allocator_traits<AllocatorType>::template
rebind_alloc<unsigned char>;
const auto raw = static_cast<unsigned char *>(raw_);
const auto align_needed = size % alignof(alloc_type);
const auto align_offset = align_needed != 0
? alignof(alloc_type) - align_needed : 0ull;
const auto alloc_size = size + sizeof(alloc_type) + align_offset;
auto alloc_p = reinterpret_cast<alloc_type *>(raw + size + align_offset);
auto alloc = std::move(*alloc_p);
alloc_p->~alloc_type();
std::allocator_traits<alloc_type>::deallocate(alloc, raw, alloc_size);
}
template <typename T>
constexpr std::size_t variadic_first(std::size_t = 0u)
{
return std::numeric_limits<std::size_t>::max();
}
template <typename T, typename First, typename... Args>
constexpr std::size_t variadic_first(std::size_t pos = 0u)
{
if constexpr (std::is_same_v<std::decay_t<First>, T>)
return pos;
else
return variadic_first<T, Args...>(pos+1);
}
template <std::size_t Idx, typename First, typename... Args>
requires (Idx <= sizeof...(Args))
constexpr decltype(auto) get_variadic(First&& first, Args&&... args)
{
if constexpr (Idx == 0u)
return static_cast<First>(first);
else
return get_variadic<Idx-1u>(static_cast<Args>(args)...);
}
template <std::size_t Idx>
constexpr decltype(auto) get_variadic();
template <typename Allocator>
struct coro_promise_allocator
{
using allocator_type = Allocator;
allocator_type get_allocator() const {return alloc_;}
template <typename... Args>
void* operator new(std::size_t size, Args & ... args)
{
return allocate_coroutine(size,
get_variadic<variadic_first<std::allocator_arg_t,
std::decay_t<Args>...>() + 1u>(args...));
}
void operator delete(void* raw, std::size_t size)
{
deallocate_coroutine<allocator_type>(raw, size);
}
template <typename... Args>
coro_promise_allocator(Args&& ... args)
: alloc_(
get_variadic<variadic_first<std::allocator_arg_t,
std::decay_t<Args>...>() + 1u>(args...))
{
}
private:
allocator_type alloc_;
};
template <>
struct coro_promise_allocator<std::allocator<void>>
{
using allocator_type = std::allocator<void>;
template <typename... Args>
coro_promise_allocator(Args&&...)
{
}
allocator_type get_allocator() const
{
return {};
}
};
} // namespace detail
} // namespace experimental
} // namespace asio
#endif // ASIO_EXPERIMENTAL_DETAIL_CORO_PROMISE_ALLOCATOR_HPP

View File

@@ -0,0 +1,54 @@
//
// experimental/detail/has_signature.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2025 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_DETAIL_HAS_SIGNATURE_HPP
#define ASIO_EXPERIMENTAL_DETAIL_HAS_SIGNATURE_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/type_traits.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
namespace experimental {
namespace detail {
template <typename S, typename... Signatures>
struct has_signature;
template <typename S, typename... Signatures>
struct has_signature;
template <typename S>
struct has_signature<S> : false_type
{
};
template <typename S, typename... Signatures>
struct has_signature<S, S, Signatures...> : true_type
{
};
template <typename S, typename Head, typename... Tail>
struct has_signature<S, Head, Tail...> : has_signature<S, Tail...>
{
};
} // namespace detail
} // namespace experimental
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_EXPERIMENTAL_DETAIL_HAS_SIGNATURE_HPP

View File

@@ -0,0 +1,623 @@
//
// experimental/detail/impl/channel_service.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2025 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_DETAIL_IMPL_CHANNEL_SERVICE_HPP
#define ASIO_EXPERIMENTAL_DETAIL_IMPL_CHANNEL_SERVICE_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
namespace asio {
namespace experimental {
namespace detail {
template <typename Mutex>
inline channel_service<Mutex>::channel_service(
asio::execution_context& ctx)
: asio::detail::execution_context_service_base<channel_service>(ctx),
mutex_(),
impl_list_(0)
{
}
template <typename Mutex>
inline void channel_service<Mutex>::shutdown()
{
// Abandon all pending operations.
asio::detail::op_queue<channel_operation> ops;
asio::detail::mutex::scoped_lock lock(mutex_);
base_implementation_type* impl = impl_list_;
while (impl)
{
ops.push(impl->waiters_);
impl = impl->next_;
}
}
template <typename Mutex>
inline void channel_service<Mutex>::construct(
channel_service<Mutex>::base_implementation_type& impl,
std::size_t max_buffer_size)
{
impl.max_buffer_size_ = max_buffer_size;
impl.receive_state_ = block;
impl.send_state_ = max_buffer_size ? buffer : block;
// Insert implementation into linked list of all implementations.
asio::detail::mutex::scoped_lock lock(mutex_);
impl.next_ = impl_list_;
impl.prev_ = 0;
if (impl_list_)
impl_list_->prev_ = &impl;
impl_list_ = &impl;
}
template <typename Mutex>
template <typename Traits, typename... Signatures>
void channel_service<Mutex>::destroy(
channel_service<Mutex>::implementation_type<Traits, Signatures...>& impl)
{
cancel(impl);
base_destroy(impl);
}
template <typename Mutex>
template <typename Traits, typename... Signatures>
void channel_service<Mutex>::move_construct(
channel_service<Mutex>::implementation_type<Traits, Signatures...>& impl,
channel_service<Mutex>::implementation_type<
Traits, Signatures...>& other_impl)
{
impl.max_buffer_size_ = other_impl.max_buffer_size_;
impl.receive_state_ = other_impl.receive_state_;
other_impl.receive_state_ = block;
impl.send_state_ = other_impl.send_state_;
other_impl.send_state_ = other_impl.max_buffer_size_ ? buffer : block;
impl.buffer_move_from(other_impl);
// Insert implementation into linked list of all implementations.
asio::detail::mutex::scoped_lock lock(mutex_);
impl.next_ = impl_list_;
impl.prev_ = 0;
if (impl_list_)
impl_list_->prev_ = &impl;
impl_list_ = &impl;
}
template <typename Mutex>
template <typename Traits, typename... Signatures>
void channel_service<Mutex>::move_assign(
channel_service<Mutex>::implementation_type<Traits, Signatures...>& impl,
channel_service& other_service,
channel_service<Mutex>::implementation_type<
Traits, Signatures...>& other_impl)
{
cancel(impl);
if (this != &other_service)
{
// Remove implementation from linked list of all implementations.
asio::detail::mutex::scoped_lock lock(mutex_);
if (impl_list_ == &impl)
impl_list_ = impl.next_;
if (impl.prev_)
impl.prev_->next_ = impl.next_;
if (impl.next_)
impl.next_->prev_= impl.prev_;
impl.next_ = 0;
impl.prev_ = 0;
}
impl.max_buffer_size_ = other_impl.max_buffer_size_;
impl.receive_state_ = other_impl.receive_state_;
other_impl.receive_state_ = block;
impl.send_state_ = other_impl.send_state_;
other_impl.send_state_ = other_impl.max_buffer_size_ ? buffer : block;
impl.buffer_move_from(other_impl);
if (this != &other_service)
{
// Insert implementation into linked list of all implementations.
asio::detail::mutex::scoped_lock lock(other_service.mutex_);
impl.next_ = other_service.impl_list_;
impl.prev_ = 0;
if (other_service.impl_list_)
other_service.impl_list_->prev_ = &impl;
other_service.impl_list_ = &impl;
}
}
template <typename Mutex>
inline void channel_service<Mutex>::base_destroy(
channel_service<Mutex>::base_implementation_type& impl)
{
// Remove implementation from linked list of all implementations.
asio::detail::mutex::scoped_lock lock(mutex_);
if (impl_list_ == &impl)
impl_list_ = impl.next_;
if (impl.prev_)
impl.prev_->next_ = impl.next_;
if (impl.next_)
impl.next_->prev_= impl.prev_;
impl.next_ = 0;
impl.prev_ = 0;
}
template <typename Mutex>
inline std::size_t channel_service<Mutex>::capacity(
const channel_service<Mutex>::base_implementation_type& impl)
const noexcept
{
typename Mutex::scoped_lock lock(impl.mutex_);
return impl.max_buffer_size_;
}
template <typename Mutex>
inline bool channel_service<Mutex>::is_open(
const channel_service<Mutex>::base_implementation_type& impl)
const noexcept
{
typename Mutex::scoped_lock lock(impl.mutex_);
return impl.send_state_ != closed;
}
template <typename Mutex>
template <typename Traits, typename... Signatures>
void channel_service<Mutex>::reset(
channel_service<Mutex>::implementation_type<Traits, Signatures...>& impl)
{
cancel(impl);
typename Mutex::scoped_lock lock(impl.mutex_);
impl.receive_state_ = block;
impl.send_state_ = impl.max_buffer_size_ ? buffer : block;
impl.buffer_clear();
}
template <typename Mutex>
template <typename Traits, typename... Signatures>
void channel_service<Mutex>::close(
channel_service<Mutex>::implementation_type<Traits, Signatures...>& impl)
{
typedef typename implementation_type<Traits,
Signatures...>::traits_type traits_type;
typedef typename implementation_type<Traits,
Signatures...>::payload_type payload_type;
typename Mutex::scoped_lock lock(impl.mutex_);
if (impl.receive_state_ == block)
{
while (channel_operation* op = impl.waiters_.front())
{
impl.waiters_.pop();
traits_type::invoke_receive_closed(
post_receive<payload_type,
typename traits_type::receive_closed_signature>(
static_cast<channel_receive<payload_type>*>(op)));
}
}
impl.send_state_ = closed;
if (impl.receive_state_ != buffer)
impl.receive_state_ = closed;
}
template <typename Mutex>
template <typename Traits, typename... Signatures>
void channel_service<Mutex>::cancel(
channel_service<Mutex>::implementation_type<Traits, Signatures...>& impl)
{
typedef typename implementation_type<Traits,
Signatures...>::traits_type traits_type;
typedef typename implementation_type<Traits,
Signatures...>::payload_type payload_type;
typename Mutex::scoped_lock lock(impl.mutex_);
while (channel_operation* op = impl.waiters_.front())
{
if (impl.send_state_ == block)
{
impl.waiters_.pop();
static_cast<channel_send<payload_type>*>(op)->cancel();
}
else
{
impl.waiters_.pop();
traits_type::invoke_receive_cancelled(
post_receive<payload_type,
typename traits_type::receive_cancelled_signature>(
static_cast<channel_receive<payload_type>*>(op)));
}
}
if (impl.receive_state_ == waiter)
impl.receive_state_ = block;
if (impl.send_state_ == waiter)
impl.send_state_ = impl.max_buffer_size_ ? buffer : block;
}
template <typename Mutex>
template <typename Traits, typename... Signatures>
void channel_service<Mutex>::cancel_by_key(
channel_service<Mutex>::implementation_type<Traits, Signatures...>& impl,
void* cancellation_key)
{
typedef typename implementation_type<Traits,
Signatures...>::traits_type traits_type;
typedef typename implementation_type<Traits,
Signatures...>::payload_type payload_type;
typename Mutex::scoped_lock lock(impl.mutex_);
asio::detail::op_queue<channel_operation> other_ops;
while (channel_operation* op = impl.waiters_.front())
{
if (op->cancellation_key_ == cancellation_key)
{
if (impl.send_state_ == block)
{
impl.waiters_.pop();
static_cast<channel_send<payload_type>*>(op)->cancel();
}
else
{
impl.waiters_.pop();
traits_type::invoke_receive_cancelled(
post_receive<payload_type,
typename traits_type::receive_cancelled_signature>(
static_cast<channel_receive<payload_type>*>(op)));
}
}
else
{
impl.waiters_.pop();
other_ops.push(op);
}
}
impl.waiters_.push(other_ops);
if (impl.waiters_.empty())
{
if (impl.receive_state_ == waiter)
impl.receive_state_ = block;
if (impl.send_state_ == waiter)
impl.send_state_ = impl.max_buffer_size_ ? buffer : block;
}
}
template <typename Mutex>
inline bool channel_service<Mutex>::ready(
const channel_service<Mutex>::base_implementation_type& impl)
const noexcept
{
typename Mutex::scoped_lock lock(impl.mutex_);
return impl.receive_state_ != block;
}
template <typename Mutex>
template <typename Message, typename Traits,
typename... Signatures, typename... Args>
bool channel_service<Mutex>::try_send(
channel_service<Mutex>::implementation_type<Traits, Signatures...>& impl,
bool via_dispatch, Args&&... args)
{
typedef typename implementation_type<Traits,
Signatures...>::payload_type payload_type;
typename Mutex::scoped_lock lock(impl.mutex_);
switch (impl.send_state_)
{
case block:
{
return false;
}
case buffer:
{
impl.buffer_push(Message(0, static_cast<Args&&>(args)...));
impl.receive_state_ = buffer;
if (impl.buffer_size() == impl.max_buffer_size_)
impl.send_state_ = block;
return true;
}
case waiter:
{
payload_type payload(Message(0, static_cast<Args&&>(args)...));
channel_receive<payload_type>* receive_op =
static_cast<channel_receive<payload_type>*>(impl.waiters_.front());
impl.waiters_.pop();
if (impl.waiters_.empty())
impl.send_state_ = impl.max_buffer_size_ ? buffer : block;
lock.unlock();
if (via_dispatch)
receive_op->dispatch(static_cast<payload_type&&>(payload));
else
receive_op->post(static_cast<payload_type&&>(payload));
return true;
}
case closed:
default:
{
return false;
}
}
}
template <typename Mutex>
template <typename Message, typename Traits,
typename... Signatures, typename... Args>
std::size_t channel_service<Mutex>::try_send_n(
channel_service<Mutex>::implementation_type<Traits, Signatures...>& impl,
std::size_t count, bool via_dispatch, Args&&... args)
{
typedef typename implementation_type<Traits,
Signatures...>::payload_type payload_type;
typename Mutex::scoped_lock lock(impl.mutex_);
if (count == 0)
return 0;
switch (impl.send_state_)
{
case block:
return 0;
case buffer:
case waiter:
break;
case closed:
default:
return 0;
}
payload_type payload(Message(0, static_cast<Args&&>(args)...));
for (std::size_t i = 0; i < count; ++i)
{
switch (impl.send_state_)
{
case block:
{
return i;
}
case buffer:
{
i += impl.buffer_push_n(count - i,
static_cast<payload_type&&>(payload));
impl.receive_state_ = buffer;
if (impl.buffer_size() == impl.max_buffer_size_)
impl.send_state_ = block;
return i;
}
case waiter:
{
channel_receive<payload_type>* receive_op =
static_cast<channel_receive<payload_type>*>(impl.waiters_.front());
impl.waiters_.pop();
if (impl.waiters_.empty())
impl.send_state_ = impl.max_buffer_size_ ? buffer : block;
lock.unlock();
if (via_dispatch)
receive_op->dispatch(payload);
else
receive_op->post(payload);
break;
}
case closed:
default:
{
return i;
}
}
}
return count;
}
template <typename Mutex>
template <typename Traits, typename... Signatures>
void channel_service<Mutex>::start_send_op(
channel_service<Mutex>::implementation_type<Traits, Signatures...>& impl,
channel_send<typename implementation_type<
Traits, Signatures...>::payload_type>* send_op)
{
typedef typename implementation_type<Traits,
Signatures...>::payload_type payload_type;
typename Mutex::scoped_lock lock(impl.mutex_);
switch (impl.send_state_)
{
case block:
{
impl.waiters_.push(send_op);
if (impl.receive_state_ == block)
impl.receive_state_ = waiter;
return;
}
case buffer:
{
impl.buffer_push(send_op->get_payload());
impl.receive_state_ = buffer;
if (impl.buffer_size() == impl.max_buffer_size_)
impl.send_state_ = block;
send_op->immediate();
break;
}
case waiter:
{
channel_receive<payload_type>* receive_op =
static_cast<channel_receive<payload_type>*>(impl.waiters_.front());
impl.waiters_.pop();
if (impl.waiters_.empty())
impl.send_state_ = impl.max_buffer_size_ ? buffer : block;
receive_op->post(send_op->get_payload());
send_op->immediate();
break;
}
case closed:
default:
{
send_op->close();
break;
}
}
}
template <typename Mutex>
template <typename Traits, typename... Signatures, typename Handler>
bool channel_service<Mutex>::try_receive(
channel_service<Mutex>::implementation_type<Traits, Signatures...>& impl,
Handler&& handler)
{
typedef typename implementation_type<Traits,
Signatures...>::payload_type payload_type;
typename Mutex::scoped_lock lock(impl.mutex_);
switch (impl.receive_state_)
{
case block:
{
return false;
}
case buffer:
{
payload_type payload(impl.buffer_front());
if (channel_send<payload_type>* send_op =
static_cast<channel_send<payload_type>*>(impl.waiters_.front()))
{
impl.buffer_pop();
impl.buffer_push(send_op->get_payload());
impl.waiters_.pop();
send_op->post();
}
else
{
impl.buffer_pop();
if (impl.buffer_size() == 0)
impl.receive_state_ = (impl.send_state_ == closed) ? closed : block;
impl.send_state_ = (impl.send_state_ == closed) ? closed : buffer;
}
lock.unlock();
asio::detail::non_const_lvalue<Handler> handler2(handler);
asio::detail::completion_payload_handler<
payload_type, decay_t<Handler>>(
static_cast<payload_type&&>(payload), handler2.value)();
return true;
}
case waiter:
{
channel_send<payload_type>* send_op =
static_cast<channel_send<payload_type>*>(impl.waiters_.front());
payload_type payload = send_op->get_payload();
impl.waiters_.pop();
if (impl.waiters_.front() == 0)
impl.receive_state_ = (impl.send_state_ == closed) ? closed : block;
send_op->post();
lock.unlock();
asio::detail::non_const_lvalue<Handler> handler2(handler);
asio::detail::completion_payload_handler<
payload_type, decay_t<Handler>>(
static_cast<payload_type&&>(payload), handler2.value)();
return true;
}
case closed:
default:
{
return false;
}
}
}
template <typename Mutex>
template <typename Traits, typename... Signatures>
void channel_service<Mutex>::start_receive_op(
channel_service<Mutex>::implementation_type<Traits, Signatures...>& impl,
channel_receive<typename implementation_type<
Traits, Signatures...>::payload_type>* receive_op)
{
typedef typename implementation_type<Traits,
Signatures...>::traits_type traits_type;
typedef typename implementation_type<Traits,
Signatures...>::payload_type payload_type;
typename Mutex::scoped_lock lock(impl.mutex_);
switch (impl.receive_state_)
{
case block:
{
impl.waiters_.push(receive_op);
if (impl.send_state_ != closed)
impl.send_state_ = waiter;
return;
}
case buffer:
{
payload_type payload(
static_cast<payload_type&&>(impl.buffer_front()));
if (channel_send<payload_type>* send_op =
static_cast<channel_send<payload_type>*>(impl.waiters_.front()))
{
impl.buffer_pop();
impl.buffer_push(send_op->get_payload());
impl.waiters_.pop();
send_op->post();
}
else
{
impl.buffer_pop();
if (impl.buffer_size() == 0)
impl.receive_state_ = (impl.send_state_ == closed) ? closed : block;
impl.send_state_ = (impl.send_state_ == closed) ? closed : buffer;
}
receive_op->immediate(static_cast<payload_type&&>(payload));
break;
}
case waiter:
{
channel_send<payload_type>* send_op =
static_cast<channel_send<payload_type>*>(impl.waiters_.front());
payload_type payload = send_op->get_payload();
impl.waiters_.pop();
if (impl.waiters_.front() == 0)
impl.receive_state_ = (impl.send_state_ == closed) ? closed : block;
send_op->post();
receive_op->immediate(static_cast<payload_type&&>(payload));
break;
}
case closed:
default:
{
traits_type::invoke_receive_closed(
post_receive<payload_type,
typename traits_type::receive_closed_signature>(receive_op));
break;
}
}
}
} // namespace detail
} // namespace experimental
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_EXPERIMENTAL_DETAIL_IMPL_CHANNEL_SERVICE_HPP

View File

@@ -0,0 +1,195 @@
//
// experimental/detail/partial_promise.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2021-2023 Klemens D. Morgenstern
// (klemens dot morgenstern at gmx dot net)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_EXPERIMENTAL_DETAIL_PARTIAL_PROMISE_HPP
#define ASIO_EXPERIMENTAL_DETAIL_PARTIAL_PROMISE_HPP
#include "asio/detail/config.hpp"
#include "asio/append.hpp"
#include "asio/awaitable.hpp"
#include "asio/experimental/coro_traits.hpp"
#if defined(ASIO_HAS_STD_COROUTINE)
# include <coroutine>
#else // defined(ASIO_HAS_STD_COROUTINE)
# include <experimental/coroutine>
#endif // defined(ASIO_HAS_STD_COROUTINE)
namespace asio {
namespace experimental {
namespace detail {
#if defined(ASIO_HAS_STD_COROUTINE)
using std::coroutine_handle;
using std::coroutine_traits;
using std::suspend_never;
using std::suspend_always;
using std::noop_coroutine;
#else // defined(ASIO_HAS_STD_COROUTINE)
using std::experimental::coroutine_handle;
using std::experimental::coroutine_traits;
using std::experimental::suspend_never;
using std::experimental::suspend_always;
using std::experimental::noop_coroutine;
#endif // defined(ASIO_HAS_STD_COROUTINE)
struct partial_coro
{
coroutine_handle<void> handle{nullptr};
};
template <typename Allocator>
struct partial_promise_base
{
template <typename Executor, typename Token, typename... Args>
void* operator new(std::size_t size, Executor&, Token& tk, Args&...)
{
return allocate_coroutine<Allocator>(size, get_associated_allocator(tk));
}
void operator delete(void* raw, std::size_t size)
{
deallocate_coroutine<Allocator>(raw, size);
}
};
template <>
struct partial_promise_base<std::allocator<void>>
{
};
template <typename Allocator>
struct partial_promise : partial_promise_base<Allocator>
{
auto initial_suspend() noexcept
{
return asio::detail::suspend_always{};
}
auto final_suspend() noexcept
{
struct awaitable_t
{
partial_promise *p;
constexpr bool await_ready() noexcept { return true; }
auto await_suspend(asio::detail::coroutine_handle<>) noexcept
{
p->get_return_object().handle.destroy();
}
constexpr void await_resume() noexcept {}
};
return awaitable_t{this};
}
void return_void() {}
partial_coro get_return_object()
{
return partial_coro{coroutine_handle<partial_promise>::from_promise(*this)};
}
void unhandled_exception()
{
assert(false);
}
};
} // namespace detail
} // namespace experimental
} // namespace asio
#if defined(ASIO_HAS_STD_COROUTINE)
namespace std {
template <typename Executor, typename Completion, typename... Args>
struct coroutine_traits<
asio::experimental::detail::partial_coro,
Executor, Completion, Args...>
{
using promise_type =
asio::experimental::detail::partial_promise<
asio::associated_allocator_t<Completion>>;
};
} // namespace std
#else // defined(ASIO_HAS_STD_COROUTINE)
namespace std { namespace experimental {
template <typename Executor, typename Completion, typename... Args>
struct coroutine_traits<
asio::experimental::detail::partial_coro,
Executor, Completion, Args...>
{
using promise_type =
asio::experimental::detail::partial_promise<
asio::associated_allocator_t<Completion>>;
};
}} // namespace std::experimental
#endif // defined(ASIO_HAS_STD_COROUTINE)
namespace asio {
namespace experimental {
namespace detail {
template <execution::executor Executor,
typename CompletionToken, typename... Args>
partial_coro post_coroutine(Executor exec,
CompletionToken token, Args&&... args) noexcept
{
post(exec, asio::append(std::move(token), std::move(args)...));
co_return;
}
template <detail::execution_context Context,
typename CompletionToken, typename... Args>
partial_coro post_coroutine(Context& ctx,
CompletionToken token, Args&&... args) noexcept
{
post(ctx, asio::append(std::move(token), std::move(args)...));
co_return;
}
template <execution::executor Executor,
typename CompletionToken, typename... Args>
partial_coro dispatch_coroutine(Executor exec,
CompletionToken token, Args&&... args) noexcept
{
dispatch(exec, asio::append(std::move(token), std::move(args)...));
co_return;
}
template <detail::execution_context Context,
typename CompletionToken, typename... Args>
partial_coro dispatch_coroutine(Context& ctx,
CompletionToken token, Args &&... args) noexcept
{
dispatch(ctx, asio::append(std::move(token), std::move(args)...));
co_return;
}
} // namespace detail
} // namespace experimental
} // namespace asio
#endif // ASIO_EXPERIMENTAL_DETAIL_PARTIAL_PROMISE_HPP

View File

@@ -0,0 +1,181 @@
//
// experimental/impl/as_single.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2025 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_EXPERIMENTAL_AS_SINGLE_HPP
#define ASIO_IMPL_EXPERIMENTAL_AS_SINGLE_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/config.hpp"
#include <tuple>
#include "asio/associator.hpp"
#include "asio/async_result.hpp"
#include "asio/detail/handler_cont_helpers.hpp"
#include "asio/detail/initiation_base.hpp"
#include "asio/detail/type_traits.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
namespace experimental {
namespace detail {
// Class to adapt a as_single_t as a completion handler.
template <typename Handler>
class as_single_handler
{
public:
typedef void result_type;
template <typename CompletionToken>
as_single_handler(as_single_t<CompletionToken> e)
: handler_(static_cast<CompletionToken&&>(e.token_))
{
}
template <typename RedirectedHandler>
as_single_handler(RedirectedHandler&& h)
: handler_(static_cast<RedirectedHandler&&>(h))
{
}
void operator()()
{
static_cast<Handler&&>(handler_)();
}
template <typename Arg>
void operator()(Arg&& arg)
{
static_cast<Handler&&>(handler_)(static_cast<Arg&&>(arg));
}
template <typename... Args>
void operator()(Args&&... args)
{
static_cast<Handler&&>(handler_)(
std::make_tuple(static_cast<Args&&>(args)...));
}
//private:
Handler handler_;
};
template <typename Handler>
inline bool asio_handler_is_continuation(
as_single_handler<Handler>* this_handler)
{
return asio_handler_cont_helpers::is_continuation(
this_handler->handler_);
}
template <typename Signature>
struct as_single_signature
{
typedef Signature type;
};
template <typename R>
struct as_single_signature<R()>
{
typedef R type();
};
template <typename R, typename Arg>
struct as_single_signature<R(Arg)>
{
typedef R type(Arg);
};
template <typename R, typename... Args>
struct as_single_signature<R(Args...)>
{
typedef R type(std::tuple<decay_t<Args>...>);
};
} // namespace detail
} // namespace experimental
#if !defined(GENERATING_DOCUMENTATION)
template <typename CompletionToken, typename Signature>
struct async_result<experimental::as_single_t<CompletionToken>, Signature>
{
template <typename Initiation>
struct init_wrapper : detail::initiation_base<Initiation>
{
using detail::initiation_base<Initiation>::initiation_base;
template <typename Handler, typename... Args>
void operator()(Handler&& handler, Args&&... args) &&
{
static_cast<Initiation&&>(*this)(
experimental::detail::as_single_handler<decay_t<Handler>>(
static_cast<Handler&&>(handler)),
static_cast<Args&&>(args)...);
}
template <typename Handler, typename... Args>
void operator()(Handler&& handler, Args&&... args) const &
{
static_cast<const Initiation&>(*this)(
experimental::detail::as_single_handler<decay_t<Handler>>(
static_cast<Handler&&>(handler)),
static_cast<Args&&>(args)...);
}
};
template <typename Initiation, typename RawCompletionToken, typename... Args>
static auto initiate(Initiation&& initiation,
RawCompletionToken&& token, Args&&... args)
-> decltype(
async_initiate<CompletionToken,
typename experimental::detail::as_single_signature<Signature>::type>(
init_wrapper<decay_t<Initiation>>(
static_cast<Initiation&&>(initiation)),
token.token_, static_cast<Args&&>(args)...))
{
return async_initiate<CompletionToken,
typename experimental::detail::as_single_signature<Signature>::type>(
init_wrapper<decay_t<Initiation>>(
static_cast<Initiation&&>(initiation)),
token.token_, static_cast<Args&&>(args)...);
}
};
template <template <typename, typename> class Associator,
typename Handler, typename DefaultCandidate>
struct associator<Associator,
experimental::detail::as_single_handler<Handler>, DefaultCandidate>
: Associator<Handler, DefaultCandidate>
{
static typename Associator<Handler, DefaultCandidate>::type get(
const experimental::detail::as_single_handler<Handler>& h) noexcept
{
return Associator<Handler, DefaultCandidate>::get(h.handler_);
}
static auto get(const experimental::detail::as_single_handler<Handler>& h,
const DefaultCandidate& c) noexcept
-> decltype(Associator<Handler, DefaultCandidate>::get(h.handler_, c))
{
return Associator<Handler, DefaultCandidate>::get(h.handler_, c);
}
};
#endif // !defined(GENERATING_DOCUMENTATION)
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_IMPL_EXPERIMENTAL_AS_SINGLE_HPP

View File

@@ -0,0 +1,61 @@
//
// experimental/impl/channel_error.ipp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2025 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_CHANNEL_ERROR_IPP
#define ASIO_EXPERIMENTAL_IMPL_CHANNEL_ERROR_IPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/config.hpp"
#include "asio/experimental/channel_error.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
namespace experimental {
namespace error {
namespace detail {
class channel_category : public asio::error_category
{
public:
const char* name() const noexcept
{
return "asio.channel";
}
std::string message(int value) const
{
switch (value)
{
case channel_closed: return "Channel closed";
case channel_cancelled: return "Channel cancelled";
default: return "asio.channel error";
}
}
};
} // namespace detail
const asio::error_category& get_channel_category()
{
static detail::channel_category instance;
return instance;
}
} // namespace error
} // namespace experimental
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_EXPERIMENTAL_IMPL_CHANNEL_ERROR_IPP

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,789 @@
//
// experimental/impl/parallel_group.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2025 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_EXPERIMENTAL_PARALLEL_GROUP_HPP
#define ASIO_IMPL_EXPERIMENTAL_PARALLEL_GROUP_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/config.hpp"
#include <atomic>
#include <deque>
#include <memory>
#include <new>
#include <tuple>
#include "asio/associated_cancellation_slot.hpp"
#include "asio/detail/recycling_allocator.hpp"
#include "asio/detail/type_traits.hpp"
#include "asio/dispatch.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
namespace experimental {
namespace detail {
// Stores the result from an individual asynchronous operation.
template <typename T, typename = void>
struct parallel_group_op_result
{
public:
parallel_group_op_result()
: has_value_(false)
{
}
parallel_group_op_result(parallel_group_op_result&& other)
: has_value_(other.has_value_)
{
if (has_value_)
new (&u_.value_) T(std::move(other.get()));
}
~parallel_group_op_result()
{
if (has_value_)
u_.value_.~T();
}
T& get() noexcept
{
return u_.value_;
}
template <typename... Args>
void emplace(Args&&... args)
{
new (&u_.value_) T(std::forward<Args>(args)...);
has_value_ = true;
}
private:
union u
{
u() {}
~u() {}
char c_;
T value_;
} u_;
bool has_value_;
};
// Proxy completion handler for the group of parallel operations. Unpacks and
// concatenates the individual operations' results, and invokes the user's
// completion handler.
template <typename Handler, typename... Ops>
struct parallel_group_completion_handler
{
typedef decay_t<
prefer_result_t<
associated_executor_t<Handler>,
execution::outstanding_work_t::tracked_t
>
> executor_type;
parallel_group_completion_handler(Handler&& h)
: handler_(std::move(h)),
executor_(
asio::prefer(
asio::get_associated_executor(handler_),
execution::outstanding_work.tracked))
{
}
executor_type get_executor() const noexcept
{
return executor_;
}
void operator()()
{
this->invoke(asio::detail::make_index_sequence<sizeof...(Ops)>());
}
template <std::size_t... I>
void invoke(asio::detail::index_sequence<I...>)
{
this->invoke(std::tuple_cat(std::move(std::get<I>(args_).get())...));
}
template <typename... Args>
void invoke(std::tuple<Args...>&& args)
{
this->invoke(std::move(args),
asio::detail::index_sequence_for<Args...>());
}
template <typename... Args, std::size_t... I>
void invoke(std::tuple<Args...>&& args,
asio::detail::index_sequence<I...>)
{
std::move(handler_)(completion_order_, std::move(std::get<I>(args))...);
}
Handler handler_;
executor_type executor_;
std::array<std::size_t, sizeof...(Ops)> completion_order_{};
std::tuple<
parallel_group_op_result<
typename parallel_op_signature_as_tuple<
completion_signature_of_t<Ops>
>::type
>...
> args_{};
};
// Shared state for the parallel group.
template <typename Condition, typename Handler, typename... Ops>
struct parallel_group_state
{
parallel_group_state(Condition&& c, Handler&& h)
: cancellation_condition_(std::move(c)),
handler_(std::move(h))
{
}
// The number of operations that have completed so far. Used to determine the
// order of completion.
std::atomic<unsigned int> completed_{0};
// The non-none cancellation type that resulted from a cancellation condition.
// Stored here for use by the group's initiating function.
std::atomic<cancellation_type_t> cancel_type_{cancellation_type::none};
// The number of cancellations that have been requested, either on completion
// of the operations within the group, or via the cancellation slot for the
// group operation. Initially set to the number of operations to prevent
// cancellation signals from being emitted until after all of the group's
// operations' initiating functions have completed.
std::atomic<unsigned int> cancellations_requested_{sizeof...(Ops)};
// The number of operations that are yet to complete. Used to determine when
// it is safe to invoke the user's completion handler.
std::atomic<unsigned int> outstanding_{sizeof...(Ops)};
// The cancellation signals for each operation in the group.
asio::cancellation_signal cancellation_signals_[sizeof...(Ops)];
// The cancellation condition is used to determine whether the results from an
// individual operation warrant a cancellation request for the whole group.
Condition cancellation_condition_;
// The proxy handler to be invoked once all operations in the group complete.
parallel_group_completion_handler<Handler, Ops...> handler_;
};
// Handler for an individual operation within the parallel group.
template <std::size_t I, typename Condition, typename Handler, typename... Ops>
struct parallel_group_op_handler
{
typedef asio::cancellation_slot cancellation_slot_type;
parallel_group_op_handler(
std::shared_ptr<parallel_group_state<Condition, Handler, Ops...>> state)
: state_(std::move(state))
{
}
cancellation_slot_type get_cancellation_slot() const noexcept
{
return state_->cancellation_signals_[I].slot();
}
template <typename... Args>
void operator()(Args... args)
{
// Capture this operation into the completion order.
state_->handler_.completion_order_[state_->completed_++] = I;
// Determine whether the results of this operation require cancellation of
// the whole group.
cancellation_type_t cancel_type = state_->cancellation_condition_(args...);
// Capture the result of the operation into the proxy completion handler.
std::get<I>(state_->handler_.args_).emplace(std::move(args)...);
if (cancel_type != cancellation_type::none)
{
// Save the type for potential use by the group's initiating function.
state_->cancel_type_ = cancel_type;
// If we are the first operation to request cancellation, emit a signal
// for each operation in the group.
if (state_->cancellations_requested_++ == 0)
for (std::size_t i = 0; i < sizeof...(Ops); ++i)
if (i != I)
state_->cancellation_signals_[i].emit(cancel_type);
}
// If this is the last outstanding operation, invoke the user's handler.
if (--state_->outstanding_ == 0)
asio::dispatch(std::move(state_->handler_));
}
std::shared_ptr<parallel_group_state<Condition, Handler, Ops...>> state_;
};
// Handler for an individual operation within the parallel group that has an
// explicitly specified executor.
template <typename Executor, std::size_t I,
typename Condition, typename Handler, typename... Ops>
struct parallel_group_op_handler_with_executor :
parallel_group_op_handler<I, Condition, Handler, Ops...>
{
typedef parallel_group_op_handler<I, Condition, Handler, Ops...> base_type;
typedef asio::cancellation_slot cancellation_slot_type;
typedef Executor executor_type;
parallel_group_op_handler_with_executor(
std::shared_ptr<parallel_group_state<Condition, Handler, Ops...>> state,
executor_type ex)
: parallel_group_op_handler<I, Condition, Handler, Ops...>(std::move(state))
{
cancel_proxy_ =
&this->state_->cancellation_signals_[I].slot().template
emplace<cancel_proxy>(this->state_, std::move(ex));
}
cancellation_slot_type get_cancellation_slot() const noexcept
{
return cancel_proxy_->signal_.slot();
}
executor_type get_executor() const noexcept
{
return cancel_proxy_->executor_;
}
// Proxy handler that forwards the emitted signal to the correct executor.
struct cancel_proxy
{
cancel_proxy(
std::shared_ptr<parallel_group_state<
Condition, Handler, Ops...>> state,
executor_type ex)
: state_(std::move(state)),
executor_(std::move(ex))
{
}
void operator()(cancellation_type_t type)
{
if (auto state = state_.lock())
{
asio::cancellation_signal* sig = &signal_;
asio::dispatch(executor_,
[state, sig, type]{ sig->emit(type); });
}
}
std::weak_ptr<parallel_group_state<Condition, Handler, Ops...>> state_;
asio::cancellation_signal signal_;
executor_type executor_;
};
cancel_proxy* cancel_proxy_;
};
// Helper to launch an operation using the correct executor, if any.
template <std::size_t I, typename Op, typename = void>
struct parallel_group_op_launcher
{
template <typename Condition, typename Handler, typename... Ops>
static void launch(Op& op,
const std::shared_ptr<parallel_group_state<
Condition, Handler, Ops...>>& state)
{
typedef associated_executor_t<Op> ex_type;
ex_type ex = asio::get_associated_executor(op);
std::move(op)(
parallel_group_op_handler_with_executor<ex_type, I,
Condition, Handler, Ops...>(state, std::move(ex)));
}
};
// Specialised launcher for operations that specify no executor.
template <std::size_t I, typename Op>
struct parallel_group_op_launcher<I, Op,
enable_if_t<
is_same<
typename associated_executor<
Op>::asio_associated_executor_is_unspecialised,
void
>::value
>>
{
template <typename Condition, typename Handler, typename... Ops>
static void launch(Op& op,
const std::shared_ptr<parallel_group_state<
Condition, Handler, Ops...>>& state)
{
std::move(op)(
parallel_group_op_handler<I, Condition, Handler, Ops...>(state));
}
};
template <typename Condition, typename Handler, typename... Ops>
struct parallel_group_cancellation_handler
{
parallel_group_cancellation_handler(
std::shared_ptr<parallel_group_state<Condition, Handler, Ops...>> state)
: state_(std::move(state))
{
}
void operator()(cancellation_type_t cancel_type)
{
// If we are the first place to request cancellation, i.e. no operation has
// yet completed and requested cancellation, emit a signal for each
// operation in the group.
if (cancel_type != cancellation_type::none)
if (auto state = state_.lock())
if (state->cancellations_requested_++ == 0)
for (std::size_t i = 0; i < sizeof...(Ops); ++i)
state->cancellation_signals_[i].emit(cancel_type);
}
std::weak_ptr<parallel_group_state<Condition, Handler, Ops...>> state_;
};
template <typename Condition, typename Handler,
typename... Ops, std::size_t... I>
void parallel_group_launch(Condition cancellation_condition, Handler handler,
std::tuple<Ops...>& ops, asio::detail::index_sequence<I...>)
{
// Get the user's completion handler's cancellation slot, so that we can allow
// cancellation of the entire group.
associated_cancellation_slot_t<Handler> slot
= asio::get_associated_cancellation_slot(handler);
// Create the shared state for the operation.
typedef parallel_group_state<Condition, Handler, Ops...> state_type;
std::shared_ptr<state_type> state = std::allocate_shared<state_type>(
asio::detail::recycling_allocator<state_type,
asio::detail::thread_info_base::parallel_group_tag>(),
std::move(cancellation_condition), std::move(handler));
// Initiate each individual operation in the group.
int fold[] = { 0,
( parallel_group_op_launcher<I, Ops>::launch(std::get<I>(ops), state),
0 )...
};
(void)fold;
// Check if any of the operations has already requested cancellation, and if
// so, emit a signal for each operation in the group.
if ((state->cancellations_requested_ -= sizeof...(Ops)) > 0)
for (auto& signal : state->cancellation_signals_)
signal.emit(state->cancel_type_);
// Register a handler with the user's completion handler's cancellation slot.
if (slot.is_connected())
slot.template emplace<
parallel_group_cancellation_handler<
Condition, Handler, Ops...>>(state);
}
// Proxy completion handler for the ranged group of parallel operations.
// Unpacks and recombines the individual operations' results, and invokes the
// user's completion handler.
template <typename Handler, typename Op, typename Allocator>
struct ranged_parallel_group_completion_handler
{
typedef decay_t<
prefer_result_t<
associated_executor_t<Handler>,
execution::outstanding_work_t::tracked_t
>
> executor_type;
typedef typename parallel_op_signature_as_tuple<
completion_signature_of_t<Op>
>::type op_tuple_type;
typedef parallel_group_op_result<op_tuple_type> op_result_type;
ranged_parallel_group_completion_handler(Handler&& h,
std::size_t size, const Allocator& allocator)
: handler_(std::move(h)),
executor_(
asio::prefer(
asio::get_associated_executor(handler_),
execution::outstanding_work.tracked)),
allocator_(allocator),
completion_order_(size, 0,
ASIO_REBIND_ALLOC(Allocator, std::size_t)(allocator)),
args_(ASIO_REBIND_ALLOC(Allocator, op_result_type)(allocator))
{
for (std::size_t i = 0; i < size; ++i)
args_.emplace_back();
}
executor_type get_executor() const noexcept
{
return executor_;
}
void operator()()
{
this->invoke(
asio::detail::make_index_sequence<
std::tuple_size<op_tuple_type>::value>());
}
template <std::size_t... I>
void invoke(asio::detail::index_sequence<I...>)
{
typedef typename parallel_op_signature_as_tuple<
typename ranged_parallel_group_signature<
completion_signature_of_t<Op>,
Allocator
>::raw_type
>::type vectors_type;
// Construct all result vectors using the supplied allocator.
vectors_type vectors{
typename std::tuple_element<I, vectors_type>::type(
ASIO_REBIND_ALLOC(Allocator, int)(allocator_))...};
// Reserve sufficient space in each of the result vectors.
int reserve_fold[] = { 0,
( std::get<I>(vectors).reserve(completion_order_.size()),
0 )...
};
(void)reserve_fold;
// Copy the results from all operations into the result vectors.
for (std::size_t idx = 0; idx < completion_order_.size(); ++idx)
{
int pushback_fold[] = { 0,
( std::get<I>(vectors).push_back(
std::move(std::get<I>(args_[idx].get()))),
0 )...
};
(void)pushback_fold;
}
std::move(handler_)(std::move(completion_order_),
std::move(std::get<I>(vectors))...);
}
Handler handler_;
executor_type executor_;
Allocator allocator_;
std::vector<std::size_t,
ASIO_REBIND_ALLOC(Allocator, std::size_t)> completion_order_;
std::deque<op_result_type,
ASIO_REBIND_ALLOC(Allocator, op_result_type)> args_;
};
// Shared state for the parallel group.
template <typename Condition, typename Handler, typename Op, typename Allocator>
struct ranged_parallel_group_state
{
ranged_parallel_group_state(Condition&& c, Handler&& h,
std::size_t size, const Allocator& allocator)
: cancellations_requested_(size),
outstanding_(size),
cancellation_signals_(
ASIO_REBIND_ALLOC(Allocator,
asio::cancellation_signal)(allocator)),
cancellation_condition_(std::move(c)),
handler_(std::move(h), size, allocator)
{
for (std::size_t i = 0; i < size; ++i)
cancellation_signals_.emplace_back();
}
// The number of operations that have completed so far. Used to determine the
// order of completion.
std::atomic<unsigned int> completed_{0};
// The non-none cancellation type that resulted from a cancellation condition.
// Stored here for use by the group's initiating function.
std::atomic<cancellation_type_t> cancel_type_{cancellation_type::none};
// The number of cancellations that have been requested, either on completion
// of the operations within the group, or via the cancellation slot for the
// group operation. Initially set to the number of operations to prevent
// cancellation signals from being emitted until after all of the group's
// operations' initiating functions have completed.
std::atomic<unsigned int> cancellations_requested_;
// The number of operations that are yet to complete. Used to determine when
// it is safe to invoke the user's completion handler.
std::atomic<unsigned int> outstanding_;
// The cancellation signals for each operation in the group.
std::deque<asio::cancellation_signal,
ASIO_REBIND_ALLOC(Allocator, asio::cancellation_signal)>
cancellation_signals_;
// The cancellation condition is used to determine whether the results from an
// individual operation warrant a cancellation request for the whole group.
Condition cancellation_condition_;
// The proxy handler to be invoked once all operations in the group complete.
ranged_parallel_group_completion_handler<Handler, Op, Allocator> handler_;
};
// Handler for an individual operation within the parallel group.
template <typename Condition, typename Handler, typename Op, typename Allocator>
struct ranged_parallel_group_op_handler
{
typedef asio::cancellation_slot cancellation_slot_type;
ranged_parallel_group_op_handler(
std::shared_ptr<ranged_parallel_group_state<
Condition, Handler, Op, Allocator>> state,
std::size_t idx)
: state_(std::move(state)),
idx_(idx)
{
}
cancellation_slot_type get_cancellation_slot() const noexcept
{
return state_->cancellation_signals_[idx_].slot();
}
template <typename... Args>
void operator()(Args... args)
{
// Capture this operation into the completion order.
state_->handler_.completion_order_[state_->completed_++] = idx_;
// Determine whether the results of this operation require cancellation of
// the whole group.
cancellation_type_t cancel_type = state_->cancellation_condition_(args...);
// Capture the result of the operation into the proxy completion handler.
state_->handler_.args_[idx_].emplace(std::move(args)...);
if (cancel_type != cancellation_type::none)
{
// Save the type for potential use by the group's initiating function.
state_->cancel_type_ = cancel_type;
// If we are the first operation to request cancellation, emit a signal
// for each operation in the group.
if (state_->cancellations_requested_++ == 0)
for (std::size_t i = 0; i < state_->cancellation_signals_.size(); ++i)
if (i != idx_)
state_->cancellation_signals_[i].emit(cancel_type);
}
// If this is the last outstanding operation, invoke the user's handler.
if (--state_->outstanding_ == 0)
asio::dispatch(std::move(state_->handler_));
}
std::shared_ptr<ranged_parallel_group_state<
Condition, Handler, Op, Allocator>> state_;
std::size_t idx_;
};
// Handler for an individual operation within the parallel group that has an
// explicitly specified executor.
template <typename Executor, typename Condition,
typename Handler, typename Op, typename Allocator>
struct ranged_parallel_group_op_handler_with_executor :
ranged_parallel_group_op_handler<Condition, Handler, Op, Allocator>
{
typedef ranged_parallel_group_op_handler<
Condition, Handler, Op, Allocator> base_type;
typedef asio::cancellation_slot cancellation_slot_type;
typedef Executor executor_type;
ranged_parallel_group_op_handler_with_executor(
std::shared_ptr<ranged_parallel_group_state<
Condition, Handler, Op, Allocator>> state,
executor_type ex, std::size_t idx)
: ranged_parallel_group_op_handler<Condition, Handler, Op, Allocator>(
std::move(state), idx)
{
cancel_proxy_ =
&this->state_->cancellation_signals_[idx].slot().template
emplace<cancel_proxy>(this->state_, std::move(ex));
}
cancellation_slot_type get_cancellation_slot() const noexcept
{
return cancel_proxy_->signal_.slot();
}
executor_type get_executor() const noexcept
{
return cancel_proxy_->executor_;
}
// Proxy handler that forwards the emitted signal to the correct executor.
struct cancel_proxy
{
cancel_proxy(
std::shared_ptr<ranged_parallel_group_state<
Condition, Handler, Op, Allocator>> state,
executor_type ex)
: state_(std::move(state)),
executor_(std::move(ex))
{
}
void operator()(cancellation_type_t type)
{
if (auto state = state_.lock())
{
asio::cancellation_signal* sig = &signal_;
asio::dispatch(executor_,
[state, sig, type]{ sig->emit(type); });
}
}
std::weak_ptr<ranged_parallel_group_state<
Condition, Handler, Op, Allocator>> state_;
asio::cancellation_signal signal_;
executor_type executor_;
};
cancel_proxy* cancel_proxy_;
};
template <typename Condition, typename Handler, typename Op, typename Allocator>
struct ranged_parallel_group_cancellation_handler
{
ranged_parallel_group_cancellation_handler(
std::shared_ptr<ranged_parallel_group_state<
Condition, Handler, Op, Allocator>> state)
: state_(std::move(state))
{
}
void operator()(cancellation_type_t cancel_type)
{
// If we are the first place to request cancellation, i.e. no operation has
// yet completed and requested cancellation, emit a signal for each
// operation in the group.
if (cancel_type != cancellation_type::none)
if (auto state = state_.lock())
if (state->cancellations_requested_++ == 0)
for (std::size_t i = 0; i < state->cancellation_signals_.size(); ++i)
state->cancellation_signals_[i].emit(cancel_type);
}
std::weak_ptr<ranged_parallel_group_state<
Condition, Handler, Op, Allocator>> state_;
};
template <typename Condition, typename Handler,
typename Range, typename Allocator>
void ranged_parallel_group_launch(Condition cancellation_condition,
Handler handler, Range&& range, const Allocator& allocator)
{
// Get the user's completion handler's cancellation slot, so that we can allow
// cancellation of the entire group.
associated_cancellation_slot_t<Handler> slot
= asio::get_associated_cancellation_slot(handler);
// The type of the asynchronous operation.
typedef decay_t<decltype(*declval<typename Range::iterator>())> op_type;
// Create the shared state for the operation.
typedef ranged_parallel_group_state<Condition,
Handler, op_type, Allocator> state_type;
std::shared_ptr<state_type> state = std::allocate_shared<state_type>(
asio::detail::recycling_allocator<state_type,
asio::detail::thread_info_base::parallel_group_tag>(),
std::move(cancellation_condition),
std::move(handler), range.size(), allocator);
std::size_t idx = 0;
std::size_t range_size = range.size();
for (auto&& op : std::forward<Range>(range))
{
typedef associated_executor_t<op_type> ex_type;
ex_type ex = asio::get_associated_executor(op);
std::move(op)(
ranged_parallel_group_op_handler_with_executor<
ex_type, Condition, Handler, op_type, Allocator>(
state, std::move(ex), idx++));
}
// Check if any of the operations has already requested cancellation, and if
// so, emit a signal for each operation in the group.
if ((state->cancellations_requested_ -= range_size) > 0)
for (auto& signal : state->cancellation_signals_)
signal.emit(state->cancel_type_);
// Register a handler with the user's completion handler's cancellation slot.
if (slot.is_connected())
slot.template emplace<
ranged_parallel_group_cancellation_handler<
Condition, Handler, op_type, Allocator>>(state);
}
} // namespace detail
} // namespace experimental
template <template <typename, typename> class Associator,
typename Handler, typename... Ops, typename DefaultCandidate>
struct associator<Associator,
experimental::detail::parallel_group_completion_handler<Handler, Ops...>,
DefaultCandidate>
: Associator<Handler, DefaultCandidate>
{
static typename Associator<Handler, DefaultCandidate>::type get(
const experimental::detail::parallel_group_completion_handler<
Handler, Ops...>& h) noexcept
{
return Associator<Handler, DefaultCandidate>::get(h.handler_);
}
static auto get(
const experimental::detail::parallel_group_completion_handler<
Handler, Ops...>& h,
const DefaultCandidate& c) noexcept
-> decltype(Associator<Handler, DefaultCandidate>::get(h.handler_, c))
{
return Associator<Handler, DefaultCandidate>::get(h.handler_, c);
}
};
template <template <typename, typename> class Associator, typename Handler,
typename Op, typename Allocator, typename DefaultCandidate>
struct associator<Associator,
experimental::detail::ranged_parallel_group_completion_handler<
Handler, Op, Allocator>,
DefaultCandidate>
: Associator<Handler, DefaultCandidate>
{
static typename Associator<Handler, DefaultCandidate>::type get(
const experimental::detail::ranged_parallel_group_completion_handler<
Handler, Op, Allocator>& h) noexcept
{
return Associator<Handler, DefaultCandidate>::get(h.handler_);
}
static auto get(
const experimental::detail::ranged_parallel_group_completion_handler<
Handler, Op, Allocator>& h,
const DefaultCandidate& c) noexcept
-> decltype(Associator<Handler, DefaultCandidate>::get(h.handler_, c))
{
return Associator<Handler, DefaultCandidate>::get(h.handler_, c);
}
};
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_IMPL_EXPERIMENTAL_PARALLEL_GROUP_HPP

View File

@@ -0,0 +1,255 @@
//
// experimental/impl/promise.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2021-2023 Klemens D. Morgenstern
// (klemens dot morgenstern at gmx dot net)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_EXPERIMENTAL_IMPL_PROMISE_HPP
#define ASIO_EXPERIMENTAL_IMPL_PROMISE_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/config.hpp"
#include "asio/cancellation_signal.hpp"
#include "asio/detail/utility.hpp"
#include "asio/error.hpp"
#include "asio/system_error.hpp"
#include <tuple>
#include "asio/detail/push_options.hpp"
namespace asio {
namespace experimental {
template<typename Signature = void(),
typename Executor = asio::any_io_executor,
typename Allocator = std::allocator<void>>
struct promise;
namespace detail {
template<typename Signature, typename Executor, typename Allocator>
struct promise_impl;
template<typename... Ts, typename Executor, typename Allocator>
struct promise_impl<void(Ts...), Executor, Allocator>
{
using result_type = std::tuple<Ts...>;
promise_impl(Allocator allocator, Executor executor)
: allocator(std::move(allocator)), executor(std::move(executor))
{
}
promise_impl(const promise_impl&) = delete;
~promise_impl()
{
if (completion)
this->cancel_();
if (done)
reinterpret_cast<result_type*>(&result)->~result_type();
}
aligned_storage_t<sizeof(result_type), alignof(result_type)> result;
std::atomic<bool> done{false};
cancellation_signal cancel;
Allocator allocator;
Executor executor;
template<typename Func, std::size_t... Idx>
void apply_impl(Func f, asio::detail::index_sequence<Idx...>)
{
auto& result_type = *reinterpret_cast<promise_impl::result_type*>(&result);
f(std::get<Idx>(std::move(result_type))...);
}
using allocator_type = Allocator;
allocator_type get_allocator() {return allocator;}
using executor_type = Executor;
executor_type get_executor() {return executor;}
template<typename Func>
void apply(Func f)
{
apply_impl(std::forward<Func>(f),
asio::detail::make_index_sequence<sizeof...(Ts)>{});
}
struct completion_base
{
virtual void invoke(Ts&&...ts) = 0;
};
template<typename Alloc, typename WaitHandler_>
struct completion_impl final : completion_base
{
WaitHandler_ handler;
Alloc allocator;
void invoke(Ts&&... ts)
{
auto h = std::move(handler);
using alloc_t = typename std::allocator_traits<
typename asio::decay<Alloc>::type>::template
rebind_alloc<completion_impl>;
alloc_t alloc_{allocator};
this->~completion_impl();
std::allocator_traits<alloc_t>::deallocate(alloc_, this, 1u);
std::move(h)(std::forward<Ts>(ts)...);
}
template<typename Alloc_, typename Handler_>
completion_impl(Alloc_&& alloc, Handler_&& wh)
: handler(std::forward<Handler_>(wh)),
allocator(std::forward<Alloc_>(alloc))
{
}
};
completion_base* completion = nullptr;
typename asio::aligned_storage<sizeof(void*) * 4,
alignof(completion_base)>::type completion_opt;
template<typename Alloc, typename Handler>
void set_completion(Alloc&& alloc, Handler&& handler)
{
if (completion)
cancel_();
using impl_t = completion_impl<
typename asio::decay<Alloc>::type, Handler>;
using alloc_t = typename std::allocator_traits<
typename asio::decay<Alloc>::type>::template rebind_alloc<impl_t>;
alloc_t alloc_{alloc};
auto p = std::allocator_traits<alloc_t>::allocate(alloc_, 1u);
completion = new (p) impl_t(std::forward<Alloc>(alloc),
std::forward<Handler>(handler));
}
template<typename... T_>
void complete(T_&&... ts)
{
assert(completion);
std::exchange(completion, nullptr)->invoke(std::forward<T_>(ts)...);
}
template<std::size_t... Idx>
void complete_with_result_impl(asio::detail::index_sequence<Idx...>)
{
auto& result_type = *reinterpret_cast<promise_impl::result_type*>(&result);
this->complete(std::get<Idx>(std::move(result_type))...);
}
void complete_with_result()
{
complete_with_result_impl(
asio::detail::make_index_sequence<sizeof...(Ts)>{});
}
template<typename... T_>
void cancel_impl_(std::exception_ptr*, T_*...)
{
complete(
std::make_exception_ptr(
asio::system_error(
asio::error::operation_aborted)),
T_{}...);
}
template<typename... T_>
void cancel_impl_(asio::error_code*, T_*...)
{
complete(asio::error::operation_aborted, T_{}...);
}
template<typename... T_>
void cancel_impl_(T_*...)
{
complete(T_{}...);
}
void cancel_()
{
cancel_impl_(static_cast<Ts*>(nullptr)...);
}
};
template<typename Signature = void(),
typename Executor = asio::any_io_executor,
typename Allocator = std::allocator<void>>
struct promise_handler;
template<typename... Ts, typename Executor, typename Allocator>
struct promise_handler<void(Ts...), Executor, Allocator>
{
using promise_type = promise<void(Ts...), Executor, Allocator>;
promise_handler(
Allocator allocator, Executor executor) // get_associated_allocator(exec)
: impl_(
std::allocate_shared<promise_impl<void(Ts...), Executor, Allocator>>(
allocator, allocator, executor))
{
}
std::shared_ptr<promise_impl<void(Ts...), Executor, Allocator>> impl_;
using cancellation_slot_type = cancellation_slot;
cancellation_slot_type get_cancellation_slot() const noexcept
{
return impl_->cancel.slot();
}
using allocator_type = Allocator;
allocator_type get_allocator() const noexcept
{
return impl_->get_allocator();
}
using executor_type = Executor;
Executor get_executor() const noexcept
{
return impl_->get_executor();
}
auto make_promise() -> promise<void(Ts...), executor_type, allocator_type>
{
return promise<void(Ts...), executor_type, allocator_type>{impl_};
}
void operator()(std::remove_reference_t<Ts>... ts)
{
assert(impl_);
using result_type = typename promise_impl<
void(Ts...), allocator_type, executor_type>::result_type ;
new (&impl_->result) result_type(std::move(ts)...);
impl_->done = true;
if (impl_->completion)
impl_->complete_with_result();
}
};
} // namespace detail
} // namespace experimental
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_EXPERIMENTAL_IMPL_PROMISE_HPP

View File

@@ -0,0 +1,214 @@
//
// experimental/impl/use_coro.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2021-2023 Klemens D. Morgenstern
// (klemens dot morgenstern at gmx dot net)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_EXPERIMENTAL_IMPL_USE_CORO_HPP
#define ASIO_EXPERIMENTAL_IMPL_USE_CORO_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/deferred.hpp"
#include "asio/experimental/coro.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
#if !defined(GENERATING_DOCUMENTATION)
template <typename Allocator, typename R>
struct async_result<experimental::use_coro_t<Allocator>, R()>
{
template <typename Initiation, typename... InitArgs>
static auto initiate_impl(Initiation initiation,
std::allocator_arg_t, Allocator, InitArgs... args)
-> experimental::coro<void() noexcept, void,
asio::associated_executor_t<Initiation>, Allocator>
{
co_await deferred_async_operation<R(), Initiation, InitArgs...>(
deferred_init_tag{}, std::move(initiation), std::move(args)...);
}
template <typename... InitArgs>
static auto initiate_impl(asio::detail::initiation_archetype<R()>,
std::allocator_arg_t, Allocator, InitArgs... args)
-> experimental::coro<void(), void,
asio::any_io_executor, Allocator>;
template <typename Initiation, typename... InitArgs>
static auto initiate(Initiation initiation,
experimental::use_coro_t<Allocator> tk, InitArgs&&... args)
{
return initiate_impl(std::move(initiation), std::allocator_arg,
tk.get_allocator(), std::forward<InitArgs>(args)...);
}
};
template <typename Allocator, typename R>
struct async_result<
experimental::use_coro_t<Allocator>, R(asio::error_code)>
{
template <typename Initiation, typename... InitArgs>
static auto initiate_impl(Initiation initiation,
std::allocator_arg_t, Allocator, InitArgs... args)
-> experimental::coro<void() noexcept, void,
asio::associated_executor_t<Initiation>, Allocator>
{
co_await deferred_async_operation<
R(asio::error_code), Initiation, InitArgs...>(
deferred_init_tag{}, std::move(initiation), std::move(args)...);
}
template <typename... InitArgs>
static auto initiate_impl(
asio::detail::initiation_archetype<R(asio::error_code)>,
std::allocator_arg_t, Allocator, InitArgs... args)
-> experimental::coro<void(), void,
asio::any_io_executor, Allocator>;
template <typename Initiation, typename... InitArgs>
static auto initiate(Initiation initiation,
experimental::use_coro_t<Allocator> tk, InitArgs&&... args)
{
return initiate_impl(std::move(initiation), std::allocator_arg,
tk.get_allocator(), std::forward<InitArgs>(args)...);
}
};
template <typename Allocator, typename R>
struct async_result<
experimental::use_coro_t<Allocator>, R(std::exception_ptr)>
{
template <typename Initiation, typename... InitArgs>
static auto initiate_impl(Initiation initiation,
std::allocator_arg_t, Allocator, InitArgs... args)
-> experimental::coro<void(), void,
asio::associated_executor_t<Initiation>, Allocator>
{
co_await deferred_async_operation<
R(std::exception_ptr), Initiation, InitArgs...>(
deferred_init_tag{}, std::move(initiation), std::move(args)...);
}
template <typename... InitArgs>
static auto initiate_impl(
asio::detail::initiation_archetype<R(std::exception_ptr)>,
std::allocator_arg_t, Allocator, InitArgs... args)
-> experimental::coro<void(), void,
asio::any_io_executor, Allocator>;
template <typename Initiation, typename... InitArgs>
static auto initiate(Initiation initiation,
experimental::use_coro_t<Allocator> tk, InitArgs&&... args)
{
return initiate_impl(std::move(initiation), std::allocator_arg,
tk.get_allocator(), std::forward<InitArgs>(args)...);
}
};
template <typename Allocator, typename R, typename T>
struct async_result<experimental::use_coro_t<Allocator>, R(T)>
{
template <typename Initiation, typename... InitArgs>
static auto initiate_impl(Initiation initiation,
std::allocator_arg_t, Allocator, InitArgs... args)
-> experimental::coro<void() noexcept, T,
asio::associated_executor_t<Initiation>, Allocator>
{
co_return co_await deferred_async_operation<R(T), Initiation, InitArgs...>(
deferred_init_tag{}, std::move(initiation), std::move(args)...);
}
template <typename... InitArgs>
static auto initiate_impl(asio::detail::initiation_archetype<R(T)>,
std::allocator_arg_t, Allocator, InitArgs... args)
-> experimental::coro<void() noexcept, T,
asio::any_io_executor, Allocator>;
template <typename Initiation, typename... InitArgs>
static auto initiate(Initiation initiation,
experimental::use_coro_t<Allocator> tk, InitArgs&&... args)
{
return initiate_impl(std::move(initiation), std::allocator_arg,
tk.get_allocator(), std::forward<InitArgs>(args)...);
}
};
template <typename Allocator, typename R, typename T>
struct async_result<
experimental::use_coro_t<Allocator>, R(asio::error_code, T)>
{
template <typename Initiation, typename... InitArgs>
static auto initiate_impl(Initiation initiation,
std::allocator_arg_t, Allocator, InitArgs... args)
-> experimental::coro<void(), T,
asio::associated_executor_t<Initiation>, Allocator>
{
co_return co_await deferred_async_operation<
R(asio::error_code, T), Initiation, InitArgs...>(
deferred_init_tag{}, std::move(initiation), std::move(args)...);
}
template <typename... InitArgs>
static auto initiate_impl(
asio::detail::initiation_archetype<
R(asio::error_code, T)>,
std::allocator_arg_t, Allocator, InitArgs... args)
-> experimental::coro<void(), T, asio::any_io_executor, Allocator>;
template <typename Initiation, typename... InitArgs>
static auto initiate(Initiation initiation,
experimental::use_coro_t<Allocator> tk, InitArgs&&... args)
{
return initiate_impl(std::move(initiation), std::allocator_arg,
tk.get_allocator(), std::forward<InitArgs>(args)...);
}
};
template <typename Allocator, typename R, typename T>
struct async_result<
experimental::use_coro_t<Allocator>, R(std::exception_ptr, T)>
{
template <typename Initiation, typename... InitArgs>
static auto initiate_impl(Initiation initiation,
std::allocator_arg_t, Allocator, InitArgs... args)
-> experimental::coro<void(), T,
asio::associated_executor_t<Initiation>, Allocator>
{
co_return co_await deferred_async_operation<
R(std::exception_ptr, T), Initiation, InitArgs...>(
deferred_init_tag{}, std::move(initiation), std::move(args)...);
}
template <typename... InitArgs>
static auto initiate_impl(
asio::detail::initiation_archetype<R(std::exception_ptr, T)>,
std::allocator_arg_t, Allocator, InitArgs... args)
-> experimental::coro<void(), T, asio::any_io_executor, Allocator>;
template <typename Initiation, typename... InitArgs>
static auto initiate(Initiation initiation,
experimental::use_coro_t<Allocator> tk, InitArgs&&... args)
{
return initiate_impl(std::move(initiation), std::allocator_arg,
tk.get_allocator(), std::forward<InitArgs>(args)...);
}
};
#endif // !defined(GENERATING_DOCUMENTATION)
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_EXPERIMENTAL_IMPL_USE_CORO_HPP

View File

@@ -0,0 +1,66 @@
//
// experimental/impl/use_promise.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2021-2023 Klemens D. Morgenstern
// (klemens dot morgenstern at gmx dot net)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_EXPERIMENTAL_IMPL_USE_PROMISE_HPP
#define ASIO_EXPERIMENTAL_IMPL_USE_PROMISE_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/config.hpp"
#include <memory>
#include "asio/async_result.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
namespace experimental {
template <typename Allocator>
struct use_promise_t;
namespace detail {
template<typename Signature, typename Executor, typename Allocator>
struct promise_handler;
} // namespace detail
} // namespace experimental
#if !defined(GENERATING_DOCUMENTATION)
template <typename Allocator, typename R, typename... Args>
struct async_result<experimental::use_promise_t<Allocator>, R(Args...)>
{
template <typename Initiation, typename... InitArgs>
static auto initiate(Initiation initiation,
experimental::use_promise_t<Allocator> up, InitArgs... args)
-> experimental::promise<void(decay_t<Args>...),
asio::associated_executor_t<Initiation>, Allocator>
{
using handler_type = experimental::detail::promise_handler<
void(decay_t<Args>...),
asio::associated_executor_t<Initiation>, Allocator>;
handler_type ht{up.get_allocator(), get_associated_executor(initiation)};
std::move(initiation)(ht, std::move(args)...);
return ht.make_promise();
}
};
#endif // !defined(GENERATING_DOCUMENTATION)
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_EXPERIMENTAL_IMPL_USE_PROMISE_HPP

View File

@@ -0,0 +1,455 @@
//
// experimental/parallel_group.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2025 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_PARALLEL_GROUP_HPP
#define ASIO_EXPERIMENTAL_PARALLEL_GROUP_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/config.hpp"
#include <vector>
#include "asio/async_result.hpp"
#include "asio/detail/array.hpp"
#include "asio/detail/memory.hpp"
#include "asio/detail/type_traits.hpp"
#include "asio/detail/utility.hpp"
#include "asio/experimental/cancellation_condition.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
namespace experimental {
namespace detail {
// Helper trait for getting a tuple from a completion signature.
template <typename Signature>
struct parallel_op_signature_as_tuple;
template <typename R, typename... Args>
struct parallel_op_signature_as_tuple<R(Args...)>
{
typedef std::tuple<decay_t<Args>...> type;
};
// Helper trait for concatenating completion signatures.
template <std::size_t N, typename Offsets, typename... Signatures>
struct parallel_group_signature;
template <std::size_t N, typename R0, typename... Args0>
struct parallel_group_signature<N, R0(Args0...)>
{
typedef asio::detail::array<std::size_t, N> order_type;
typedef R0 raw_type(Args0...);
typedef R0 type(order_type, Args0...);
};
template <std::size_t N,
typename R0, typename... Args0,
typename R1, typename... Args1>
struct parallel_group_signature<N, R0(Args0...), R1(Args1...)>
{
typedef asio::detail::array<std::size_t, N> order_type;
typedef R0 raw_type(Args0..., Args1...);
typedef R0 type(order_type, Args0..., Args1...);
};
template <std::size_t N, typename Sig0,
typename Sig1, typename... SigN>
struct parallel_group_signature<N, Sig0, Sig1, SigN...>
{
typedef asio::detail::array<std::size_t, N> order_type;
typedef typename parallel_group_signature<N,
typename parallel_group_signature<N, Sig0, Sig1>::raw_type,
SigN...>::raw_type raw_type;
typedef typename parallel_group_signature<N,
typename parallel_group_signature<N, Sig0, Sig1>::raw_type,
SigN...>::type type;
};
template <typename Condition, typename Handler,
typename... Ops, std::size_t... I>
void parallel_group_launch(Condition cancellation_condition, Handler handler,
std::tuple<Ops...>& ops, asio::detail::index_sequence<I...>);
// Helper trait for determining ranged parallel group completion signatures.
template <typename Signature, typename Allocator>
struct ranged_parallel_group_signature;
template <typename R, typename... Args, typename Allocator>
struct ranged_parallel_group_signature<R(Args...), Allocator>
{
typedef std::vector<std::size_t,
ASIO_REBIND_ALLOC(Allocator, std::size_t)> order_type;
typedef R raw_type(
std::vector<Args, ASIO_REBIND_ALLOC(Allocator, Args)>...);
typedef R type(order_type,
std::vector<Args, ASIO_REBIND_ALLOC(Allocator, Args)>...);
};
template <typename Condition, typename Handler,
typename Range, typename Allocator>
void ranged_parallel_group_launch(Condition cancellation_condition,
Handler handler, Range&& range, const Allocator& allocator);
char (&parallel_group_has_iterator_helper(...))[2];
template <typename T>
char parallel_group_has_iterator_helper(T*, typename T::iterator* = 0);
template <typename T>
struct parallel_group_has_iterator_typedef
{
enum { value = (sizeof((parallel_group_has_iterator_helper)((T*)(0))) == 1) };
};
} // namespace detail
/// Type trait used to determine whether a type is a range of asynchronous
/// operations that can be used with with @c make_parallel_group.
template <typename T>
struct is_async_operation_range
{
#if defined(GENERATING_DOCUMENTATION)
/// The value member is true if the type may be used as a range of
/// asynchronous operations.
static const bool value;
#else
enum
{
value = detail::parallel_group_has_iterator_typedef<T>::value
};
#endif
};
/// A group of asynchronous operations that may be launched in parallel.
/**
* See the documentation for asio::experimental::make_parallel_group for
* a usage example.
*/
template <typename... Ops>
class parallel_group
{
private:
struct initiate_async_wait
{
template <typename Handler, typename Condition>
void operator()(Handler&& h, Condition&& c, std::tuple<Ops...>&& ops) const
{
detail::parallel_group_launch(
std::forward<Condition>(c), std::forward<Handler>(h),
ops, asio::detail::index_sequence_for<Ops...>());
}
};
std::tuple<Ops...> ops_;
public:
/// Constructor.
explicit parallel_group(Ops... ops)
: ops_(std::move(ops)...)
{
}
/// The completion signature for the group of operations.
typedef typename detail::parallel_group_signature<sizeof...(Ops),
completion_signature_of_t<Ops>...>::type signature;
/// Initiate an asynchronous wait for the group of operations.
/**
* Launches the group and asynchronously waits for completion.
*
* @param cancellation_condition A function object, called on completion of
* an operation within the group, that is used to determine whether to cancel
* the remaining operations. The function object is passed the arguments of
* the completed operation's handler. To trigger cancellation of the remaining
* operations, it must return a asio::cancellation_type value other
* than <tt>asio::cancellation_type::none</tt>.
*
* @param token A @ref completion_token whose signature is comprised of
* a @c std::array<std::size_t, N> indicating the completion order of the
* operations, followed by all operations' completion handler arguments.
*
* The library provides the following @c cancellation_condition types:
*
* @li asio::experimental::wait_for_all
* @li asio::experimental::wait_for_one
* @li asio::experimental::wait_for_one_error
* @li asio::experimental::wait_for_one_success
*/
template <typename CancellationCondition,
ASIO_COMPLETION_TOKEN_FOR(signature) CompletionToken>
auto async_wait(CancellationCondition cancellation_condition,
CompletionToken&& token)
-> decltype(
asio::async_initiate<CompletionToken, signature>(
declval<initiate_async_wait>(), token,
std::move(cancellation_condition), std::move(ops_)))
{
return asio::async_initiate<CompletionToken, signature>(
initiate_async_wait(), token,
std::move(cancellation_condition), std::move(ops_));
}
};
/// Create a group of operations that may be launched in parallel.
/**
* For example:
* @code asio::experimental::make_parallel_group(
* in.async_read_some(asio::buffer(data)),
* timer.async_wait()
* ).async_wait(
* asio::experimental::wait_for_all(),
* [](
* std::array<std::size_t, 2> completion_order,
* std::error_code ec1, std::size_t n1,
* std::error_code ec2
* )
* {
* switch (completion_order[0])
* {
* case 0:
* {
* std::cout << "descriptor finished: " << ec1 << ", " << n1 << "\n";
* }
* break;
* case 1:
* {
* std::cout << "timer finished: " << ec2 << "\n";
* }
* break;
* }
* }
* );
* @endcode
*
* If preferred, the asynchronous operations may be explicitly packaged as
* function objects:
* @code asio::experimental::make_parallel_group(
* [&](auto token)
* {
* return in.async_read_some(asio::buffer(data), token);
* },
* [&](auto token)
* {
* return timer.async_wait(token);
* }
* ).async_wait(
* asio::experimental::wait_for_all(),
* [](
* std::array<std::size_t, 2> completion_order,
* std::error_code ec1, std::size_t n1,
* std::error_code ec2
* )
* {
* switch (completion_order[0])
* {
* case 0:
* {
* std::cout << "descriptor finished: " << ec1 << ", " << n1 << "\n";
* }
* break;
* case 1:
* {
* std::cout << "timer finished: " << ec2 << "\n";
* }
* break;
* }
* }
* );
* @endcode
*/
template <typename... Ops>
ASIO_NODISCARD inline parallel_group<Ops...>
make_parallel_group(Ops... ops)
{
return parallel_group<Ops...>(std::move(ops)...);
}
/// A range-based group of asynchronous operations that may be launched in
/// parallel.
/**
* See the documentation for asio::experimental::make_parallel_group for
* a usage example.
*/
template <typename Range, typename Allocator = std::allocator<void>>
class ranged_parallel_group
{
private:
struct initiate_async_wait
{
template <typename Handler, typename Condition>
void operator()(Handler&& h, Condition&& c,
Range&& range, const Allocator& allocator) const
{
detail::ranged_parallel_group_launch(std::move(c),
std::move(h), std::forward<Range>(range), allocator);
}
};
Range range_;
Allocator allocator_;
public:
/// Constructor.
explicit ranged_parallel_group(Range range,
const Allocator& allocator = Allocator())
: range_(std::move(range)),
allocator_(allocator)
{
}
/// The completion signature for the group of operations.
typedef typename detail::ranged_parallel_group_signature<
completion_signature_of_t<
decay_t<decltype(*std::declval<typename Range::iterator>())>>,
Allocator>::type signature;
/// Initiate an asynchronous wait for the group of operations.
/**
* Launches the group and asynchronously waits for completion.
*
* @param cancellation_condition A function object, called on completion of
* an operation within the group, that is used to determine whether to cancel
* the remaining operations. The function object is passed the arguments of
* the completed operation's handler. To trigger cancellation of the remaining
* operations, it must return a asio::cancellation_type value other
* than <tt>asio::cancellation_type::none</tt>.
*
* @param token A @ref completion_token whose signature is comprised of
* a @c std::vector<std::size_t, Allocator> indicating the completion order of
* the operations, followed by a vector for each of the completion signature's
* arguments.
*
* The library provides the following @c cancellation_condition types:
*
* @li asio::experimental::wait_for_all
* @li asio::experimental::wait_for_one
* @li asio::experimental::wait_for_one_error
* @li asio::experimental::wait_for_one_success
*/
template <typename CancellationCondition,
ASIO_COMPLETION_TOKEN_FOR(signature) CompletionToken>
auto async_wait(CancellationCondition cancellation_condition,
CompletionToken&& token)
-> decltype(
asio::async_initiate<CompletionToken, signature>(
declval<initiate_async_wait>(), token,
std::move(cancellation_condition),
std::move(range_), allocator_))
{
return asio::async_initiate<CompletionToken, signature>(
initiate_async_wait(), token,
std::move(cancellation_condition),
std::move(range_), allocator_);
}
};
/// Create a group of operations that may be launched in parallel.
/**
* @param range A range containing the operations to be launched.
*
* For example:
* @code
* using op_type =
* decltype(socket1.async_read_some(asio::buffer(data1)));
*
* std::vector<op_type> ops;
* ops.push_back(socket1.async_read_some(asio::buffer(data1)));
* ops.push_back(socket2.async_read_some(asio::buffer(data2)));
*
* asio::experimental::make_parallel_group(ops).async_wait(
* asio::experimental::wait_for_all(),
* [](
* std::vector<std::size_t> completion_order,
* std::vector<std::error_code> e,
* std::vector<std::size_t> n
* )
* {
* for (std::size_t i = 0; i < completion_order.size(); ++i)
* {
* std::size_t idx = completion_order[i];
* std::cout << "socket " << idx << " finished: ";
* std::cout << e[idx] << ", " << n[idx] << "\n";
* }
* }
* );
* @endcode
*/
template <typename Range>
ASIO_NODISCARD inline ranged_parallel_group<decay_t<Range>>
make_parallel_group(Range&& range,
constraint_t<
is_async_operation_range<decay_t<Range>>::value
> = 0)
{
return ranged_parallel_group<decay_t<Range>>(std::forward<Range>(range));
}
/// Create a group of operations that may be launched in parallel.
/**
* @param allocator Specifies the allocator to be used with the result vectors.
*
* @param range A range containing the operations to be launched.
*
* For example:
* @code
* using op_type =
* decltype(socket1.async_read_some(asio::buffer(data1)));
*
* std::vector<op_type> ops;
* ops.push_back(socket1.async_read_some(asio::buffer(data1)));
* ops.push_back(socket2.async_read_some(asio::buffer(data2)));
*
* asio::experimental::make_parallel_group(
* std::allocator_arg_t,
* my_allocator,
* ops
* ).async_wait(
* asio::experimental::wait_for_all(),
* [](
* std::vector<std::size_t> completion_order,
* std::vector<std::error_code> e,
* std::vector<std::size_t> n
* )
* {
* for (std::size_t i = 0; i < completion_order.size(); ++i)
* {
* std::size_t idx = completion_order[i];
* std::cout << "socket " << idx << " finished: ";
* std::cout << e[idx] << ", " << n[idx] << "\n";
* }
* }
* );
* @endcode
*/
template <typename Allocator, typename Range>
ASIO_NODISCARD inline ranged_parallel_group<decay_t<Range>, Allocator>
make_parallel_group(allocator_arg_t, const Allocator& allocator, Range&& range,
constraint_t<
is_async_operation_range<decay_t<Range>>::value
> = 0)
{
return ranged_parallel_group<decay_t<Range>, Allocator>(
std::forward<Range>(range), allocator);
}
} // namespace experimental
} // namespace asio
#include "asio/detail/pop_options.hpp"
#include "asio/experimental/impl/parallel_group.hpp"
#endif // ASIO_EXPERIMENTAL_PARALLEL_GROUP_HPP

View File

@@ -0,0 +1,224 @@
//
// experimental/promise.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2021-2023 Klemens D. Morgenstern
// (klemens dot morgenstern at gmx dot net)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_EXPERIMENTAL_PROMISE_HPP
#define ASIO_EXPERIMENTAL_PROMISE_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/type_traits.hpp"
#include "asio/any_io_executor.hpp"
#include "asio/associated_cancellation_slot.hpp"
#include "asio/associated_executor.hpp"
#include "asio/bind_executor.hpp"
#include "asio/cancellation_signal.hpp"
#include "asio/dispatch.hpp"
#include "asio/experimental/impl/promise.hpp"
#include "asio/post.hpp"
#include <algorithm>
#include "asio/detail/push_options.hpp"
namespace asio {
namespace experimental {
template <typename T>
struct is_promise : std::false_type {};
template <typename ... Ts>
struct is_promise<promise<Ts...>> : std::true_type {};
template <typename T>
constexpr bool is_promise_v = is_promise<T>::value;
template <typename ... Ts>
struct promise_value_type
{
using type = std::tuple<Ts...>;
};
template <typename T>
struct promise_value_type<T>
{
using type = T;
};
template <>
struct promise_value_type<>
{
using type = std::tuple<>;
};
#if defined(GENERATING_DOCUMENTATION)
/// A disposable handle for an eager operation.
/**
* @tparam Signature The signature of the operation.
*
* @tparam Executor The executor to be used by the promise (taken from the
* operation).
*
* @tparam Allocator The allocator used for the promise. Can be set through
* use_allocator.
*
* A promise can be used to initiate an asynchronous option that can be
* completed later. If the promise gets destroyed before completion, the
* operation gets a cancel signal and the result is ignored.
*
* A promise fulfills the requirements of async_operation.
*
* @par Examples
* Reading and writing from one coroutine.
* @code
* awaitable<void> read_write_some(asio::ip::tcp::socket & sock,
* asio::mutable_buffer read_buf, asio::const_buffer to_write)
* {
* auto p = asio::async_read(read_buf,
* asio::experimental::use_promise);
* co_await asio::async_write_some(to_write);
* co_await p;
* }
* @endcode
*/
template<typename Signature = void(),
typename Executor = asio::any_io_executor,
typename Allocator = std::allocator<void>>
struct promise
#else
template <typename ... Ts, typename Executor, typename Allocator>
struct promise<void(Ts...), Executor, Allocator>
#endif // defined(GENERATING_DOCUMENTATION)
{
/// The value that's returned by the promise.
using value_type = typename promise_value_type<Ts...>::type;
/// Cancel the promise. Usually done through the destructor.
void cancel(cancellation_type level = cancellation_type::all)
{
if (impl_ && !impl_->done)
{
asio::dispatch(impl_->executor,
[level, impl = impl_]{ impl->cancel.emit(level); });
}
}
/// Check if the promise is completed already.
bool completed() const noexcept
{
return impl_ && impl_->done;
}
/// Wait for the promise to become ready.
template <ASIO_COMPLETION_TOKEN_FOR(void(Ts...)) CompletionToken>
inline ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(Ts...))
operator()(CompletionToken&& token)
{
assert(impl_);
return async_initiate<CompletionToken, void(Ts...)>(
initiate_async_wait{impl_}, token);
}
promise() = delete;
promise(const promise& ) = delete;
promise(promise&& ) noexcept = default;
/// Destruct the promise and cancel the operation.
/**
* It is safe to destruct a promise of a promise that didn't complete.
*/
~promise() { cancel(); }
private:
#if !defined(GENERATING_DOCUMENTATION)
template <typename, typename, typename> friend struct promise;
friend struct detail::promise_handler<void(Ts...), Executor, Allocator>;
#endif // !defined(GENERATING_DOCUMENTATION)
std::shared_ptr<detail::promise_impl<
void(Ts...), Executor, Allocator>> impl_;
promise(
std::shared_ptr<detail::promise_impl<
void(Ts...), Executor, Allocator>> impl)
: impl_(impl)
{
}
struct initiate_async_wait
{
std::shared_ptr<detail::promise_impl<
void(Ts...), Executor, Allocator>> self_;
template <typename WaitHandler>
void operator()(WaitHandler&& handler) const
{
const auto alloc = get_associated_allocator(
handler, self_->get_allocator());
auto cancel = get_associated_cancellation_slot(handler);
if (self_->done)
{
auto exec = asio::get_associated_executor(
handler, self_->get_executor());
asio::post(exec,
[self = std::move(self_),
handler = std::forward<WaitHandler>(handler)]() mutable
{
self->apply(std::move(handler));
});
}
else
{
if (cancel.is_connected())
{
struct cancel_handler
{
std::weak_ptr<detail::promise_impl<
void(Ts...), Executor, Allocator>> self;
cancel_handler(
std::weak_ptr<detail::promise_impl<
void(Ts...), Executor, Allocator>> self)
: self(std::move(self))
{
}
void operator()(cancellation_type level) const
{
if (auto p = self.lock())
{
p->cancel.emit(level);
p->cancel_();
}
}
};
cancel.template emplace<cancel_handler>(self_);
}
self_->set_completion(alloc, std::forward<WaitHandler>(handler));
}
}
};
};
} // namespace experimental
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_EXPERIMENTAL_PROMISE_HPP

View File

@@ -0,0 +1,189 @@
//
// experimental/use_coro.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2021-2023 Klemens D. Morgenstern
// (klemens dot morgenstern at gmx dot net)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_EXPERIMENTAL_USE_CORO_HPP
#define ASIO_EXPERIMENTAL_USE_CORO_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/config.hpp"
#include <memory>
#include "asio/deferred.hpp"
#include "asio/detail/source_location.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
class any_io_executor;
namespace experimental {
/// A @ref completion_token that creates another coro for the task completion.
/**
* The @c use_coro_t class, with its value @c use_coro, is used to represent an
* operation that can be awaited by the current resumable coroutine. This
* completion token may be passed as a handler to an asynchronous operation.
* For example:
*
* @code coro<void> my_coroutine(tcp::socket my_socket)
* {
* std::size_t n = co_await my_socket.async_read_some(buffer, use_coro);
* ...
* } @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.
*
* Note that this token is not the most efficient (use the default completion
* token @c asio::deferred for that) but does provide type erasure, as it
* will always return a @c coro.
*/
template <typename Allocator = std::allocator<void>>
struct use_coro_t
{
/// The allocator type. The allocator is used when constructing the
/// @c std::promise object for a given asynchronous operation.
typedef Allocator allocator_type;
/// Default constructor.
constexpr use_coro_t(
allocator_type allocator = allocator_type{}
#if defined(ASIO_ENABLE_HANDLER_TRACKING)
# if defined(ASIO_HAS_SOURCE_LOCATION)
, asio::detail::source_location location =
asio::detail::source_location::current()
# endif // defined(ASIO_HAS_SOURCE_LOCATION)
#endif // defined(ASIO_ENABLE_HANDLER_TRACKING)
)
: allocator_(allocator)
#if defined(ASIO_ENABLE_HANDLER_TRACKING)
# if defined(ASIO_HAS_SOURCE_LOCATION)
, file_name_(location.file_name()),
line_(location.line()),
function_name_(location.function_name())
# else // defined(ASIO_HAS_SOURCE_LOCATION)
, file_name_(0),
line_(0),
function_name_(0)
# endif // defined(ASIO_HAS_SOURCE_LOCATION)
#endif // defined(ASIO_ENABLE_HANDLER_TRACKING)
{
}
/// Specify an alternate allocator.
template <typename OtherAllocator>
use_coro_t<OtherAllocator> rebind(const OtherAllocator& allocator) const
{
return use_future_t<OtherAllocator>(allocator);
}
/// Obtain allocator.
allocator_type get_allocator() const
{
return allocator_;
}
/// Constructor used to specify file name, line, and function name.
constexpr use_coro_t(const char* file_name,
int line, const char* function_name,
allocator_type allocator = allocator_type{}) :
#if defined(ASIO_ENABLE_HANDLER_TRACKING)
file_name_(file_name),
line_(line),
function_name_(function_name),
#endif // defined(ASIO_ENABLE_HANDLER_TRACKING)
allocator_(allocator)
{
#if !defined(ASIO_ENABLE_HANDLER_TRACKING)
(void)file_name;
(void)line;
(void)function_name;
#endif // !defined(ASIO_ENABLE_HANDLER_TRACKING)
}
/// Adapts an executor to add the @c use_coro_t completion token as the
/// default.
template <typename InnerExecutor>
struct executor_with_default : InnerExecutor
{
/// Specify @c use_coro_t as the default completion token type.
typedef use_coro_t default_completion_token_type;
/// Construct the adapted executor from the inner executor type.
template <typename InnerExecutor1>
executor_with_default(const InnerExecutor1& ex,
constraint_t<
conditional_t<
!is_same<InnerExecutor1, executor_with_default>::value,
is_convertible<InnerExecutor1, InnerExecutor>,
false_type
>::value
> = 0) noexcept
: InnerExecutor(ex)
{
}
};
/// Type alias to adapt an I/O object to use @c use_coro_t as its
/// default completion token type.
template <typename T>
using as_default_on_t = typename T::template rebind_executor<
executor_with_default<typename T::executor_type>>::other;
/// Function helper to adapt an I/O object to use @c use_coro_t as its
/// default completion token type.
template <typename T>
static typename decay_t<T>::template rebind_executor<
executor_with_default<typename decay_t<T>::executor_type>
>::other
as_default_on(T&& object)
{
return typename decay_t<T>::template rebind_executor<
executor_with_default<typename decay_t<T>::executor_type>
>::other(static_cast<T&&>(object));
}
#if defined(ASIO_ENABLE_HANDLER_TRACKING)
const char* file_name_;
int line_;
const char* function_name_;
#endif // defined(ASIO_ENABLE_HANDLER_TRACKING)
private:
Allocator allocator_;
};
/// A @ref completion_token object that represents the currently executing
/// resumable coroutine.
/**
* See the documentation for asio::use_coro_t for a usage example.
*/
#if defined(GENERATING_DOCUMENTATION)
ASIO_INLINE_VARIABLE constexpr use_coro_t<> use_coro;
#else
ASIO_INLINE_VARIABLE constexpr use_coro_t<> use_coro(0, 0, 0);
#endif
} // namespace experimental
} // namespace asio
#include "asio/detail/pop_options.hpp"
#include "asio/experimental/impl/use_coro.hpp"
#include "asio/experimental/coro.hpp"
#endif // ASIO_EXPERIMENTAL_USE_CORO_HPP

View File

@@ -0,0 +1,111 @@
//
// experimental/use_promise.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2021-2023 Klemens D. Morgenstern
// (klemens dot morgenstern at gmx dot net)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_EXPERIMENTAL_USE_PROMISE_HPP
#define ASIO_EXPERIMENTAL_USE_PROMISE_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/config.hpp"
#include <memory>
#include "asio/detail/type_traits.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
namespace experimental {
template <typename Allocator = std::allocator<void>>
struct use_promise_t
{
/// The allocator type. The allocator is used when constructing the
/// @c promise object for a given asynchronous operation.
typedef Allocator allocator_type;
/// Construct using default-constructed allocator.
constexpr use_promise_t()
{
}
/// Construct using specified allocator.
explicit use_promise_t(const Allocator& allocator)
: allocator_(allocator)
{
}
/// Obtain allocator.
allocator_type get_allocator() const noexcept
{
return allocator_;
}
/// Adapts an executor to add the @c use_promise_t completion token as the
/// default.
template <typename InnerExecutor>
struct executor_with_default : InnerExecutor
{
/// Specify @c use_promise_t as the default completion token type.
typedef use_promise_t<Allocator> default_completion_token_type;
/// Construct the adapted executor from the inner executor type.
executor_with_default(const InnerExecutor& ex) noexcept
: InnerExecutor(ex)
{
}
/// Convert the specified executor to the inner executor type, then use
/// that to construct the adapted executor.
template <typename OtherExecutor>
executor_with_default(const OtherExecutor& ex,
constraint_t<
is_convertible<OtherExecutor, InnerExecutor>::value
> = 0) noexcept
: InnerExecutor(ex)
{
}
};
/// Function helper to adapt an I/O object to use @c use_promise_t as its
/// default completion token type.
template <typename T>
static typename decay_t<T>::template rebind_executor<
executor_with_default<typename decay_t<T>::executor_type>
>::other
as_default_on(T&& object)
{
return typename decay_t<T>::template rebind_executor<
executor_with_default<typename decay_t<T>::executor_type>
>::other(static_cast<T&&>(object));
}
/// Specify an alternate allocator.
template <typename OtherAllocator>
use_promise_t<OtherAllocator> rebind(const OtherAllocator& allocator) const
{
return use_promise_t<OtherAllocator>(allocator);
}
private:
Allocator allocator_;
};
ASIO_INLINE_VARIABLE constexpr use_promise_t<> use_promise;
} // namespace experimental
} // namespace asio
#include "asio/detail/pop_options.hpp"
#include "asio/experimental/impl/use_promise.hpp"
#endif // ASIO_EXPERIMENTAL_USE_CORO_HPP