early-access version 3088

This commit is contained in:
pineappleEA
2022-11-05 15:35:56 +01:00
parent 4e4fc25ce3
commit b601909c6d
35519 changed files with 5996896 additions and 860 deletions

View File

@@ -0,0 +1,73 @@
//
// experimental/append.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2022 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 BOOST_ASIO_EXPERIMENTAL_APPEND_HPP
#define BOOST_ASIO_EXPERIMENTAL_APPEND_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include <boost/asio/detail/config.hpp>
#include <tuple>
#include <boost/asio/detail/type_traits.hpp>
#include <boost/asio/detail/push_options.hpp>
namespace boost {
namespace asio {
namespace experimental {
/// Completion token type used to specify that the completion handler
/// arguments should be passed additional values after the results of the
/// operation.
template <typename CompletionToken, typename... Values>
class append_t
{
public:
/// Constructor.
template <typename T, typename... V>
BOOST_ASIO_CONSTEXPR explicit append_t(
BOOST_ASIO_MOVE_ARG(T) completion_token,
BOOST_ASIO_MOVE_ARG(V)... values)
: token_(BOOST_ASIO_MOVE_CAST(T)(completion_token)),
values_(BOOST_ASIO_MOVE_CAST(V)(values)...)
{
}
//private:
CompletionToken token_;
std::tuple<Values...> values_;
};
/// Completion token type used to specify that the completion handler
/// arguments should be passed additional values after the results of the
/// operation.
template <typename CompletionToken, typename... Values>
BOOST_ASIO_NODISCARD inline BOOST_ASIO_CONSTEXPR append_t<
typename decay<CompletionToken>::type, typename decay<Values>::type...>
append(BOOST_ASIO_MOVE_ARG(CompletionToken) completion_token,
BOOST_ASIO_MOVE_ARG(Values)... values)
{
return append_t<
typename decay<CompletionToken>::type, typename decay<Values>::type...>(
BOOST_ASIO_MOVE_CAST(CompletionToken)(completion_token),
BOOST_ASIO_MOVE_CAST(Values)(values)...);
}
} // namespace experimental
} // namespace asio
} // namespace boost
#include <boost/asio/detail/pop_options.hpp>
#include <boost/asio/experimental/impl/append.hpp>
#endif // BOOST_ASIO_EXPERIMENTAL_APPEND_HPP

View File

@@ -0,0 +1,138 @@
//
// experimental/as_single.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2022 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 BOOST_ASIO_EXPERIMENTAL_AS_SINGLE_HPP
#define BOOST_ASIO_EXPERIMENTAL_AS_SINGLE_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include <boost/asio/detail/config.hpp>
#include <boost/asio/detail/type_traits.hpp>
#include <boost/asio/detail/push_options.hpp>
namespace boost {
namespace asio {
namespace experimental {
/// Completion token type 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.
*/
BOOST_ASIO_CONSTEXPR as_single_t(
default_constructor_tag = default_constructor_tag(),
CompletionToken token = CompletionToken())
: token_(BOOST_ASIO_MOVE_CAST(CompletionToken)(token))
{
}
/// Constructor.
template <typename T>
BOOST_ASIO_CONSTEXPR explicit as_single_t(
BOOST_ASIO_MOVE_ARG(T) completion_token)
: token_(BOOST_ASIO_MOVE_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) BOOST_ASIO_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,
typename constraint<
is_convertible<OtherExecutor, InnerExecutor>::value
>::type = 0) BOOST_ASIO_NOEXCEPT
: InnerExecutor(ex)
{
}
};
/// Type alias to adapt an I/O object to use @c as_single_t as its
/// default completion token type.
#if defined(BOOST_ASIO_HAS_ALIAS_TEMPLATES) \
|| defined(GENERATING_DOCUMENTATION)
template <typename T>
using as_default_on_t = typename T::template rebind_executor<
executor_with_default<typename T::executor_type> >::other;
#endif // defined(BOOST_ASIO_HAS_ALIAS_TEMPLATES)
// || defined(GENERATING_DOCUMENTATION)
/// 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>::type::template rebind_executor<
executor_with_default<typename decay<T>::type::executor_type>
>::other
as_default_on(BOOST_ASIO_MOVE_ARG(T) object)
{
return typename decay<T>::type::template rebind_executor<
executor_with_default<typename decay<T>::type::executor_type>
>::other(BOOST_ASIO_MOVE_CAST(T)(object));
}
//private:
CompletionToken token_;
};
/// Create a completion token to specify that the completion handler arguments
/// should be combined into a single argument.
template <typename CompletionToken>
BOOST_ASIO_NODISCARD inline
BOOST_ASIO_CONSTEXPR as_single_t<typename decay<CompletionToken>::type>
as_single(BOOST_ASIO_MOVE_ARG(CompletionToken) completion_token)
{
return as_single_t<typename decay<CompletionToken>::type>(
BOOST_ASIO_MOVE_CAST(CompletionToken)(completion_token));
}
} // namespace experimental
} // namespace asio
} // namespace boost
#include <boost/asio/detail/pop_options.hpp>
#include <boost/asio/experimental/impl/as_single.hpp>
#endif // BOOST_ASIO_EXPERIMENTAL_AS_SINGLE_HPP

View File

@@ -0,0 +1,134 @@
//
// experimental/as_tuple.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2022 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 BOOST_ASIO_EXPERIMENTAL_AS_TUPLE_HPP
#define BOOST_ASIO_EXPERIMENTAL_AS_TUPLE_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include <boost/asio/detail/config.hpp>
#include <boost/asio/detail/type_traits.hpp>
#include <boost/asio/detail/push_options.hpp>
namespace boost {
namespace asio {
namespace experimental {
/// Completion token type used to specify that the completion handler
/// arguments should be combined into a single tuple argument.
/**
* The as_tuple_t class is used to indicate that any arguments to the
* completion handler should be combined and passed as a single tuple 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_tuple_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.
*/
BOOST_ASIO_CONSTEXPR as_tuple_t(
default_constructor_tag = default_constructor_tag(),
CompletionToken token = CompletionToken())
: token_(BOOST_ASIO_MOVE_CAST(CompletionToken)(token))
{
}
/// Constructor.
template <typename T>
BOOST_ASIO_CONSTEXPR explicit as_tuple_t(
BOOST_ASIO_MOVE_ARG(T) completion_token)
: token_(BOOST_ASIO_MOVE_CAST(T)(completion_token))
{
}
/// Adapts an executor to add the @c as_tuple_t completion token as the
/// default.
template <typename InnerExecutor>
struct executor_with_default : InnerExecutor
{
/// Specify @c as_tuple_t as the default completion token type.
typedef as_tuple_t default_completion_token_type;
/// Construct the adapted executor from the inner executor type.
template <typename InnerExecutor1>
executor_with_default(const InnerExecutor1& ex,
typename constraint<
conditional<
!is_same<InnerExecutor1, executor_with_default>::value,
is_convertible<InnerExecutor1, InnerExecutor>,
false_type
>::type::value
>::type = 0) BOOST_ASIO_NOEXCEPT
: InnerExecutor(ex)
{
}
};
/// Type alias to adapt an I/O object to use @c as_tuple_t as its
/// default completion token type.
#if defined(BOOST_ASIO_HAS_ALIAS_TEMPLATES) \
|| defined(GENERATING_DOCUMENTATION)
template <typename T>
using as_default_on_t = typename T::template rebind_executor<
executor_with_default<typename T::executor_type> >::other;
#endif // defined(BOOST_ASIO_HAS_ALIAS_TEMPLATES)
// || defined(GENERATING_DOCUMENTATION)
/// Function helper to adapt an I/O object to use @c as_tuple_t as its
/// default completion token type.
template <typename T>
static typename decay<T>::type::template rebind_executor<
executor_with_default<typename decay<T>::type::executor_type>
>::other
as_default_on(BOOST_ASIO_MOVE_ARG(T) object)
{
return typename decay<T>::type::template rebind_executor<
executor_with_default<typename decay<T>::type::executor_type>
>::other(BOOST_ASIO_MOVE_CAST(T)(object));
}
//private:
CompletionToken token_;
};
/// Create a completion token to specify that the completion handler arguments
/// should be combined into a single tuple argument.
template <typename CompletionToken>
BOOST_ASIO_NODISCARD inline
BOOST_ASIO_CONSTEXPR as_tuple_t<typename decay<CompletionToken>::type>
as_tuple(BOOST_ASIO_MOVE_ARG(CompletionToken) completion_token)
{
return as_tuple_t<typename decay<CompletionToken>::type>(
BOOST_ASIO_MOVE_CAST(CompletionToken)(completion_token));
}
} // namespace experimental
} // namespace asio
} // namespace boost
#include <boost/asio/detail/pop_options.hpp>
#include <boost/asio/experimental/impl/as_tuple.hpp>
#endif // BOOST_ASIO_EXPERIMENTAL_AS_TUPLE_HPP

View File

@@ -0,0 +1,539 @@
//
// experimental/awaitable_operators.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2022 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 BOOST_ASIO_EXPERIMENTAL_AWAITABLE_OPERATORS_HPP
#define BOOST_ASIO_EXPERIMENTAL_AWAITABLE_OPERATORS_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include <boost/asio/detail/config.hpp>
#include <optional>
#include <stdexcept>
#include <tuple>
#include <variant>
#include <boost/asio/awaitable.hpp>
#include <boost/asio/co_spawn.hpp>
#include <boost/asio/detail/type_traits.hpp>
#include <boost/asio/experimental/deferred.hpp>
#include <boost/asio/experimental/parallel_group.hpp>
#include <boost/asio/multiple_exceptions.hpp>
#include <boost/asio/this_coro.hpp>
#include <boost/asio/use_awaitable.hpp>
#include <boost/asio/detail/push_options.hpp>
namespace boost {
namespace asio {
namespace experimental {
namespace awaitable_operators {
namespace detail {
template <typename T, typename Executor>
awaitable<T, Executor> awaitable_wrap(awaitable<T, Executor> a,
typename constraint<is_constructible<T>::value>::type* = 0)
{
return a;
}
template <typename T, typename Executor>
awaitable<std::optional<T>, Executor> awaitable_wrap(awaitable<T, Executor> a,
typename constraint<!is_constructible<T>::value>::type* = 0)
{
co_return std::optional<T>(co_await std::move(a));
}
template <typename T>
T& awaitable_unwrap(typename conditional<true, T, void>::type& r,
typename constraint<is_constructible<T>::value>::type* = 0)
{
return r;
}
template <typename T>
T& awaitable_unwrap(std::optional<typename conditional<true, T, void>::type>& r,
typename constraint<!is_constructible<T>::value>::type* = 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(),
use_awaitable_t<Executor>{}
);
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(),
use_awaitable_t<Executor>{}
);
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(),
use_awaitable_t<Executor>{}
);
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(),
use_awaitable_t<Executor>{}
);
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(),
use_awaitable_t<Executor>{}
);
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(),
use_awaitable_t<Executor>{}
);
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(),
use_awaitable_t<Executor>{}
);
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(),
use_awaitable_t<Executor>{}
);
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(),
use_awaitable_t<Executor>{}
);
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(),
use_awaitable_t<Executor>{}
);
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(),
use_awaitable_t<Executor>{}
);
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(),
use_awaitable_t<Executor>{}
);
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
} // namespace boost
#include <boost/asio/detail/pop_options.hpp>
#endif // BOOST_ASIO_EXPERIMENTAL_AWAITABLE_OPERATORS_HPP

View File

@@ -0,0 +1,432 @@
//
// experimental/basic_channel.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2022 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 BOOST_ASIO_EXPERIMENTAL_BASIC_CHANNEL_HPP
#define BOOST_ASIO_EXPERIMENTAL_BASIC_CHANNEL_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include <boost/asio/detail/config.hpp>
#include <boost/asio/detail/non_const_lvalue.hpp>
#include <boost/asio/detail/null_mutex.hpp>
#include <boost/asio/execution/executor.hpp>
#include <boost/asio/execution_context.hpp>
#include <boost/asio/experimental/detail/channel_send_functions.hpp>
#include <boost/asio/experimental/detail/channel_service.hpp>
#include <boost/asio/detail/push_options.hpp>
namespace boost {
namespace asio {
namespace experimental {
namespace detail {
} // namespace detail
/// A channel for messages.
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<boost::asio::detail::null_mutex> service_type;
typedef typename service_type::template implementation_type<
Traits, Signatures...>::payload_type payload_type;
template <typename... PayloadSignatures,
BOOST_ASIO_COMPLETION_TOKEN_FOR(PayloadSignatures...) CompletionToken>
auto do_async_receive(detail::channel_payload<PayloadSignatures...>*,
BOOST_ASIO_MOVE_ARG(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_(&boost::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,
typename constraint<
is_convertible<ExecutionContext&, execution_context&>::value,
defaulted_constraint
>::type = defaulted_constraint())
: service_(&boost::asio::use_service<service_type>(context)),
impl_(),
executor_(context.get_executor())
{
service_->construct(impl_, max_buffer_size);
}
#if defined(BOOST_ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
/// 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,
typename constraint<
is_convertible<Executor1, Executor>::value
>::type = 0)
: service_(other.service_),
executor_(other.executor_)
{
service_->move_construct(impl_, *other.service_, 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>
typename constraint<
is_convertible<Executor1, Executor>::value,
basic_channel&
>::type 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;
}
#endif // defined(BOOST_ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
/// Destructor.
~basic_channel()
{
service_->destroy(impl_);
}
/// Get the executor associated with the object.
executor_type get_executor() BOOST_ASIO_NOEXCEPT
{
return executor_;
}
/// Get the capacity of the channel's buffer.
std::size_t capacity() BOOST_ASIO_NOEXCEPT
{
return service_->capacity(impl_);
}
/// Determine whether the channel is open.
bool is_open() const BOOST_ASIO_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 boost::asio::experimental::error::channel_canceld. 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 BOOST_ASIO_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(BOOST_ASIO_MOVE_ARG(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, BOOST_ASIO_MOVE_ARG(Args)... args);
/// Asynchronously send a message.
/**
* @par Completion Signature
* @code void(boost::system::error_code) @endcode
*/
template <typename... Args,
BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code))
CompletionToken BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)>
auto async_send(BOOST_ASIO_MOVE_ARG(Args)... args,
BOOST_ASIO_MOVE_ARG(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(BOOST_ASIO_MOVE_ARG(Handler) handler)
{
return service_->try_receive(impl_, BOOST_ASIO_MOVE_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
BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)>
auto async_receive(
BOOST_ASIO_MOVE_ARG(CompletionToken) token
BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(Executor))
#if !defined(GENERATING_DOCUMENTATION)
-> decltype(
this->do_async_receive(static_cast<payload_type*>(0),
BOOST_ASIO_MOVE_CAST(CompletionToken)(token)))
#endif // !defined(GENERATING_DOCUMENTATION)
{
return this->do_async_receive(static_cast<payload_type*>(0),
BOOST_ASIO_MOVE_CAST(CompletionToken)(token));
}
private:
// Disallow copying and assignment.
basic_channel(const basic_channel&) BOOST_ASIO_DELETED;
basic_channel& operator=(const basic_channel&) BOOST_ASIO_DELETED;
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,
typename enable_if<execution::is_executor<T>::value>::type* = 0)
{
return boost::asio::query(t, execution::context);
}
// Helper function to get an executor's context.
template <typename T>
static execution_context& get_context(const T& t,
typename enable_if<!execution::is_executor<T>::value>::type* = 0)
{
return t.context();
}
class initiate_async_send
{
public:
typedef Executor executor_type;
explicit initiate_async_send(basic_channel* self)
: self_(self)
{
}
executor_type get_executor() const BOOST_ASIO_NOEXCEPT
{
return self_->get_executor();
}
template <typename SendHandler>
void operator()(BOOST_ASIO_MOVE_ARG(SendHandler) handler,
BOOST_ASIO_MOVE_ARG(payload_type) payload) const
{
boost::asio::detail::non_const_lvalue<SendHandler> handler2(handler);
self_->service_->async_send(self_->impl_,
BOOST_ASIO_MOVE_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)
{
}
executor_type get_executor() const BOOST_ASIO_NOEXCEPT
{
return self_->get_executor();
}
template <typename ReceiveHandler>
void operator()(BOOST_ASIO_MOVE_ARG(ReceiveHandler) handler) const
{
boost::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
} // namespace boost
#include <boost/asio/detail/pop_options.hpp>
#endif // BOOST_ASIO_EXPERIMENTAL_BASIC_CHANNEL_HPP

View File

@@ -0,0 +1,432 @@
//
// experimental/basic_concurrent_channel.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2022 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 BOOST_ASIO_EXPERIMENTAL_BASIC_CONCURRENT_CHANNEL_HPP
#define BOOST_ASIO_EXPERIMENTAL_BASIC_CONCURRENT_CHANNEL_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include <boost/asio/detail/config.hpp>
#include <boost/asio/detail/non_const_lvalue.hpp>
#include <boost/asio/detail/mutex.hpp>
#include <boost/asio/execution/executor.hpp>
#include <boost/asio/execution_context.hpp>
#include <boost/asio/experimental/detail/channel_send_functions.hpp>
#include <boost/asio/experimental/detail/channel_service.hpp>
#include <boost/asio/detail/push_options.hpp>
namespace boost {
namespace asio {
namespace experimental {
namespace detail {
} // namespace detail
/// A channel for messages.
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<boost::asio::detail::mutex> service_type;
typedef typename service_type::template implementation_type<
Traits, Signatures...>::payload_type payload_type;
template <typename... PayloadSignatures,
BOOST_ASIO_COMPLETION_TOKEN_FOR(PayloadSignatures...) CompletionToken>
auto do_async_receive(detail::channel_payload<PayloadSignatures...>*,
BOOST_ASIO_MOVE_ARG(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_(&boost::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,
typename constraint<
is_convertible<ExecutionContext&, execution_context&>::value,
defaulted_constraint
>::type = defaulted_constraint())
: service_(&boost::asio::use_service<service_type>(context)),
impl_(),
executor_(context.get_executor())
{
service_->construct(impl_, max_buffer_size);
}
#if defined(BOOST_ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
/// 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,
typename constraint<
is_convertible<Executor1, Executor>::value
>::type = 0)
: service_(other.service_),
executor_(other.executor_)
{
service_->move_construct(impl_, *other.service_, 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>
typename constraint<
is_convertible<Executor1, Executor>::value,
basic_concurrent_channel&
>::type 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;
}
#endif // defined(BOOST_ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
/// Destructor.
~basic_concurrent_channel()
{
service_->destroy(impl_);
}
/// Get the executor associated with the object.
executor_type get_executor() BOOST_ASIO_NOEXCEPT
{
return executor_;
}
/// Get the capacity of the channel's buffer.
std::size_t capacity() BOOST_ASIO_NOEXCEPT
{
return service_->capacity(impl_);
}
/// Determine whether the channel is open.
bool is_open() const BOOST_ASIO_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 boost::asio::experimental::error::channel_canceld. 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 BOOST_ASIO_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(BOOST_ASIO_MOVE_ARG(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, BOOST_ASIO_MOVE_ARG(Args)... args);
/// Asynchronously send a message.
template <typename... Args,
BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code))
CompletionToken BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)>
auto async_send(BOOST_ASIO_MOVE_ARG(Args)... args,
BOOST_ASIO_MOVE_ARG(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(BOOST_ASIO_MOVE_ARG(Handler) handler)
{
return service_->try_receive(impl_, BOOST_ASIO_MOVE_CAST(Handler)(handler));
}
/// Asynchronously receive a message.
template <typename CompletionToken
BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)>
auto async_receive(
BOOST_ASIO_MOVE_ARG(CompletionToken) token
BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(Executor))
#if !defined(GENERATING_DOCUMENTATION)
-> decltype(
this->do_async_receive(static_cast<payload_type*>(0),
BOOST_ASIO_MOVE_CAST(CompletionToken)(token)))
#endif // !defined(GENERATING_DOCUMENTATION)
{
return this->do_async_receive(static_cast<payload_type*>(0),
BOOST_ASIO_MOVE_CAST(CompletionToken)(token));
}
private:
// Disallow copying and assignment.
basic_concurrent_channel(
const basic_concurrent_channel&) BOOST_ASIO_DELETED;
basic_concurrent_channel& operator=(
const basic_concurrent_channel&) BOOST_ASIO_DELETED;
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,
typename enable_if<execution::is_executor<T>::value>::type* = 0)
{
return boost::asio::query(t, execution::context);
}
// Helper function to get an executor's context.
template <typename T>
static execution_context& get_context(const T& t,
typename enable_if<!execution::is_executor<T>::value>::type* = 0)
{
return t.context();
}
class initiate_async_send
{
public:
typedef Executor executor_type;
explicit initiate_async_send(basic_concurrent_channel* self)
: self_(self)
{
}
executor_type get_executor() const BOOST_ASIO_NOEXCEPT
{
return self_->get_executor();
}
template <typename SendHandler>
void operator()(BOOST_ASIO_MOVE_ARG(SendHandler) handler,
BOOST_ASIO_MOVE_ARG(payload_type) payload) const
{
boost::asio::detail::non_const_lvalue<SendHandler> handler2(handler);
self_->service_->async_send(self_->impl_,
BOOST_ASIO_MOVE_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)
{
}
executor_type get_executor() const BOOST_ASIO_NOEXCEPT
{
return self_->get_executor();
}
template <typename ReceiveHandler>
void operator()(BOOST_ASIO_MOVE_ARG(ReceiveHandler) handler) const
{
boost::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
} // namespace boost
#include <boost/asio/detail/pop_options.hpp>
#endif // BOOST_ASIO_EXPERIMENTAL_BASIC_CONCURRENT_CHANNEL_HPP

View File

@@ -0,0 +1,157 @@
//
// experimental/cancellation_condition.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2022 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 BOOST_ASIO_EXPERIMENTAL_CANCELLATION_CONDITION_HPP
#define BOOST_ASIO_EXPERIMENTAL_CANCELLATION_CONDITION_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include <boost/asio/detail/config.hpp>
#include <exception>
#include <boost/asio/cancellation_type.hpp>
#include <boost/system/error_code.hpp>
#include <boost/asio/detail/type_traits.hpp>
#include <boost/asio/detail/push_options.hpp>
namespace boost {
namespace asio {
namespace experimental {
/// Wait for all operations to complete.
class wait_for_all
{
public:
template <typename... Args>
BOOST_ASIO_CONSTEXPR cancellation_type_t operator()(
Args&&...) const BOOST_ASIO_NOEXCEPT
{
return cancellation_type::none;
}
};
/// Wait until an operation completes, then cancel the others.
class wait_for_one
{
public:
BOOST_ASIO_CONSTEXPR explicit wait_for_one(
cancellation_type_t cancel_type = cancellation_type::all)
: cancel_type_(cancel_type)
{
}
template <typename... Args>
BOOST_ASIO_CONSTEXPR cancellation_type_t operator()(
Args&&...) const BOOST_ASIO_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:
BOOST_ASIO_CONSTEXPR explicit wait_for_one_success(
cancellation_type_t cancel_type = cancellation_type::all)
: cancel_type_(cancel_type)
{
}
BOOST_ASIO_CONSTEXPR cancellation_type_t
operator()() const BOOST_ASIO_NOEXCEPT
{
return cancel_type_;
}
template <typename E, typename... Args>
BOOST_ASIO_CONSTEXPR typename constraint<
!is_same<typename decay<E>::type, boost::system::error_code>::value
&& !is_same<typename decay<E>::type, std::exception_ptr>::value,
cancellation_type_t
>::type operator()(const E&, Args&&...) const BOOST_ASIO_NOEXCEPT
{
return cancel_type_;
}
template <typename E, typename... Args>
BOOST_ASIO_CONSTEXPR typename constraint<
is_same<typename decay<E>::type, boost::system::error_code>::value
|| is_same<typename decay<E>::type, std::exception_ptr>::value,
cancellation_type_t
>::type operator()(const E& e, Args&&...) const BOOST_ASIO_NOEXCEPT
{
return !!e ? 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:
BOOST_ASIO_CONSTEXPR explicit wait_for_one_error(
cancellation_type_t cancel_type = cancellation_type::all)
: cancel_type_(cancel_type)
{
}
BOOST_ASIO_CONSTEXPR cancellation_type_t
operator()() const BOOST_ASIO_NOEXCEPT
{
return cancellation_type::none;
}
template <typename E, typename... Args>
BOOST_ASIO_CONSTEXPR typename constraint<
!is_same<typename decay<E>::type, boost::system::error_code>::value
&& !is_same<typename decay<E>::type, std::exception_ptr>::value,
cancellation_type_t
>::type operator()(const E&, Args&&...) const BOOST_ASIO_NOEXCEPT
{
return cancellation_type::none;
}
template <typename E, typename... Args>
BOOST_ASIO_CONSTEXPR typename constraint<
is_same<typename decay<E>::type, boost::system::error_code>::value
|| is_same<typename decay<E>::type, std::exception_ptr>::value,
cancellation_type_t
>::type operator()(const E& e, Args&&...) const BOOST_ASIO_NOEXCEPT
{
return !!e ? cancel_type_ : cancellation_type::none;
}
private:
cancellation_type_t cancel_type_;
};
} // namespace experimental
} // namespace asio
} // namespace boost
#include <boost/asio/detail/pop_options.hpp>
#endif // BOOST_ASIO_EXPERIMENTAL_CANCELLATION_CONDITION_HPP

View File

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

View File

@@ -0,0 +1,88 @@
//
// experimental/channel_error.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2022 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 BOOST_ASIO_EXPERIMENTAL_CHANNEL_ERROR_HPP
#define BOOST_ASIO_EXPERIMENTAL_CHANNEL_ERROR_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include <boost/asio/detail/config.hpp>
#include <boost/system/error_code.hpp>
#include <boost/asio/detail/push_options.hpp>
namespace boost {
namespace asio {
namespace experimental {
namespace error {
enum channel_errors
{
/// The channel was closed.
channel_closed = 1,
/// The channel was cancelled.
channel_cancelled = 2
};
extern BOOST_ASIO_DECL
const boost::system::error_category& get_channel_category();
static const boost::system::error_category&
channel_category BOOST_ASIO_UNUSED_VARIABLE
= boost::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 boost
namespace boost {
namespace system {
template<> struct is_error_code_enum<
boost::asio::experimental::error::channel_errors>
{
static const bool value = true;
};
} // namespace system
} // namespace boost
namespace boost {
namespace asio {
namespace experimental {
namespace error {
inline boost::system::error_code make_error_code(channel_errors e)
{
return boost::system::error_code(
static_cast<int>(e), get_channel_category());
}
} // namespace error
} // namespace experimental
} // namespace asio
} // namespace boost
#include <boost/asio/detail/pop_options.hpp>
#if defined(BOOST_ASIO_HEADER_ONLY)
# include <boost/asio/experimental/impl/channel_error.ipp>
#endif // defined(BOOST_ASIO_HEADER_ONLY)
#endif // BOOST_ASIO_EXPERIMENTAL_CHANNEL_ERROR_HPP

View File

@@ -0,0 +1,233 @@
//
// experimental/channel_traits.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2022 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 BOOST_ASIO_EXPERIMENTAL_CHANNEL_TRAITS_HPP
#define BOOST_ASIO_EXPERIMENTAL_CHANNEL_TRAITS_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include <boost/asio/detail/config.hpp>
#include <deque>
#include <boost/asio/detail/type_traits.hpp>
#include <boost/asio/error.hpp>
#include <boost/asio/experimental/channel_error.hpp>
#include <boost/asio/detail/push_options.hpp>
namespace boost {
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, typename... Args, typename... Signatures>
struct channel_traits<R(boost::system::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(boost::system::error_code, Args...);
template <typename F>
static void invoke_receive_cancelled(F f)
{
const boost::system::error_code e = error::channel_cancelled;
BOOST_ASIO_MOVE_OR_LVALUE(F)(f)(e, typename decay<Args>::type()...);
}
typedef R receive_closed_signature(boost::system::error_code, Args...);
template <typename F>
static void invoke_receive_closed(F f)
{
const boost::system::error_code e = error::channel_closed;
BOOST_ASIO_MOVE_OR_LVALUE(F)(f)(e, typename decay<Args>::type()...);
}
};
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 boost::system::error_code e = error::channel_cancelled;
BOOST_ASIO_MOVE_OR_LVALUE(F)(f)(
std::make_exception_ptr(boost::system::system_error(e)),
typename decay<Args>::type()...);
}
typedef R receive_closed_signature(std::exception_ptr, Args...);
template <typename F>
static void invoke_receive_closed(F f)
{
const boost::system::error_code e = error::channel_closed;
BOOST_ASIO_MOVE_OR_LVALUE(F)(f)(
std::make_exception_ptr(boost::system::system_error(e)),
typename decay<Args>::type()...);
}
};
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(boost::system::error_code);
template <typename F>
static void invoke_receive_cancelled(F f)
{
const boost::system::error_code e = error::channel_cancelled;
BOOST_ASIO_MOVE_OR_LVALUE(F)(f)(e);
}
typedef R receive_closed_signature(boost::system::error_code);
template <typename F>
static void invoke_receive_closed(F f)
{
const boost::system::error_code e = error::channel_closed;
BOOST_ASIO_MOVE_OR_LVALUE(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(boost::system::error_code);
template <typename F>
static void invoke_receive_cancelled(F f)
{
const boost::system::error_code e = error::channel_cancelled;
BOOST_ASIO_MOVE_OR_LVALUE(F)(f)(e);
}
typedef R receive_closed_signature(boost::system::error_code);
template <typename F>
static void invoke_receive_closed(F f)
{
const boost::system::error_code e = error::channel_closed;
BOOST_ASIO_MOVE_OR_LVALUE(F)(f)(e);
}
};
#endif // defined(GENERATING_DOCUMENTATION)
} // namespace experimental
} // namespace asio
} // namespace boost
#include <boost/asio/detail/pop_options.hpp>
#endif // BOOST_ASIO_EXPERIMENTAL_CHANNEL_TRAITS_HPP

View File

@@ -0,0 +1,189 @@
//
// experimental/co_spawn.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2021-2022 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 BOOST_ASIO_EXPERIMENTAL_CO_SPAWN_HPP
#define BOOST_ASIO_EXPERIMENTAL_CO_SPAWN_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include <boost/asio/detail/config.hpp>
#include <utility>
#include <boost/asio/compose.hpp>
#include <boost/asio/detail/type_traits.hpp>
#include <boost/asio/experimental/coro.hpp>
#include <boost/asio/experimental/deferred.hpp>
#include <boost/asio/experimental/prepend.hpp>
#include <boost/asio/redirect_error.hpp>
#include <boost/asio/detail/push_options.hpp>
namespace boost {
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>
BOOST_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>
BOOST_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>
BOOST_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>
BOOST_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>
BOOST_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>
BOOST_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
} // namespace boost
#include <boost/asio/detail/pop_options.hpp>
#endif //BOOST_ASIO_EXPERIMENTAL_CO_SPAWN_HPP

View File

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

View File

@@ -0,0 +1,265 @@
//
// experimental/coro.hpp
// ~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2021-2022 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 BOOST_ASIO_EXPERIMENTAL_CORO_HPP
#define BOOST_ASIO_EXPERIMENTAL_CORO_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include <boost/asio/detail/config.hpp>
#include <boost/asio/dispatch.hpp>
#include <boost/asio/error.hpp>
#include <boost/system/error_code.hpp>
#include <boost/asio/experimental/coro_traits.hpp>
#include <boost/asio/experimental/detail/coro_promise_allocator.hpp>
#include <boost/asio/experimental/detail/partial_promise.hpp>
#include <boost/asio/experimental/use_coro.hpp>
#include <boost/asio/post.hpp>
#include <boost/asio/detail/push_options.hpp>
namespace boost {
namespace asio {
namespace experimental {
namespace detail {
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>
struct coro
{
/// The traits of the coroutine. See boost::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>;
#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;
#if !defined(GENERATING_DOCUMENTATION)
friend struct detail::coro_promise<Yield, Return, Executor>;
#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)
boost::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");
}
/// 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};
};
} // namespace experimental
} // namespace asio
} // namespace boost
#include <boost/asio/detail/pop_options.hpp>
#include <boost/asio/experimental/impl/coro.hpp>
#endif // BOOST_ASIO_EXPERIMENTAL_CORO_HPP

View File

@@ -0,0 +1,230 @@
//
// experimental/detail/coro_traits.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2021-2022 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 BOOST_ASIO_EXPERIMENTAL_DETAIL_CORO_TRAITS_HPP
#define BOOST_ASIO_EXPERIMENTAL_DETAIL_CORO_TRAITS_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include <boost/asio/detail/config.hpp>
#include <optional>
#include <variant>
#include <boost/asio/any_io_executor.hpp>
namespace boost {
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
} // namespace boost
#endif // BOOST_ASIO_EXPERIMENTAL_DETAIL_CORO_TRAITS_HPP

View File

@@ -0,0 +1,607 @@
//
// experimental/deferred.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2022 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 BOOST_ASIO_EXPERIMENTAL_DEFERRED_HPP
#define BOOST_ASIO_EXPERIMENTAL_DEFERRED_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include <boost/asio/detail/config.hpp>
#include <tuple>
#include <boost/asio/associator.hpp>
#include <boost/asio/async_result.hpp>
#include <boost/asio/detail/type_traits.hpp>
#include <boost/asio/detail/push_options.hpp>
namespace boost {
namespace asio {
namespace experimental {
/// Trait for detecting objects that are usable as deferred operations.
template <typename T>
struct is_deferred : false_type
{
};
namespace detail {
// Helper trait for getting the completion signature from an async operation.
struct deferred_signature_probe {};
template <typename T>
struct deferred_signature_probe_result
{
typedef T type;
};
template <typename T>
struct deferred_signature
{
typedef typename decltype(
declval<T>()(declval<deferred_signature_probe>()))::type type;
};
// Helper trait for getting the completion signature of the tail in a sequence
// when invoked with the specified arguments.
template <typename HeadSignature, typename Tail>
struct deferred_sequence_signature;
template <typename R, typename... Args, typename Tail>
struct deferred_sequence_signature<R(Args...), Tail>
{
static_assert(
!is_same<decltype(declval<Tail>()(declval<Args>()...)), void>::value,
"deferred functions must produce a deferred return type");
typedef typename decltype(
declval<Tail>()(declval<Args>()...)(
declval<deferred_signature_probe>()))::type type;
};
// Completion handler for the head component of a deferred sequence.
template <typename Handler, typename Tail>
class deferred_sequence_handler
{
public:
template <typename H, typename T>
explicit deferred_sequence_handler(
BOOST_ASIO_MOVE_ARG(H) handler, BOOST_ASIO_MOVE_ARG(T) tail)
: handler_(BOOST_ASIO_MOVE_CAST(H)(handler)),
tail_(BOOST_ASIO_MOVE_CAST(T)(tail))
{
}
template <typename... Args>
void operator()(BOOST_ASIO_MOVE_ARG(Args)... args)
{
BOOST_ASIO_MOVE_OR_LVALUE(Tail)(tail_)(
BOOST_ASIO_MOVE_CAST(Args)(args)...)(
BOOST_ASIO_MOVE_OR_LVALUE(Handler)(handler_));
}
//private:
Handler handler_;
Tail tail_;
};
} // namespace detail
/// Used to represent an empty deferred action.
struct deferred_noop
{
/// No effect.
template <typename... Args>
void operator()(BOOST_ASIO_MOVE_ARG(Args)...) BOOST_ASIO_RVALUE_REF_QUAL
{
}
#if defined(BOOST_ASIO_HAS_REF_QUALIFIED_FUNCTIONS)
/// No effect.
template <typename... Args>
decltype(auto) operator()(BOOST_ASIO_MOVE_ARG(Args)...) const &
{
}
#endif // defined(BOOST_ASIO_HAS_REF_QUALIFIED_FUNCTIONS)
};
#if !defined(GENERATING_DOCUMENTATION)
template <>
struct is_deferred<deferred_noop> : true_type
{
};
#endif // !defined(GENERATING_DOCUMENTATION)
/// Tag type to disambiguate deferred constructors.
struct deferred_init_tag {};
/// Wraps a function object so that it may be used as an element in a deferred
/// composition.
template <typename Function>
class deferred_function
{
public:
/// Constructor.
template <typename F>
BOOST_ASIO_CONSTEXPR explicit deferred_function(
deferred_init_tag, BOOST_ASIO_MOVE_ARG(F) function)
: function_(BOOST_ASIO_MOVE_CAST(F)(function))
{
}
template <typename... Args>
decltype(auto) operator()(
BOOST_ASIO_MOVE_ARG(Args)... args) BOOST_ASIO_RVALUE_REF_QUAL
{
return BOOST_ASIO_MOVE_CAST(Function)(function_)(
BOOST_ASIO_MOVE_CAST(Args)(args)...);
}
#if defined(BOOST_ASIO_HAS_REF_QUALIFIED_FUNCTIONS)
template <typename... Args>
decltype(auto) operator()(
BOOST_ASIO_MOVE_ARG(Args)... args) const &
{
return deferred_function(*this)(
BOOST_ASIO_MOVE_CAST(Args)(args)...);
}
#endif // defined(BOOST_ASIO_HAS_REF_QUALIFIED_FUNCTIONS)
//private:
Function function_;
};
#if !defined(GENERATING_DOCUMENTATION)
template <typename Function>
struct is_deferred<deferred_function<Function> > : true_type
{
};
#endif // !defined(GENERATING_DOCUMENTATION)
/// Encapsulates deferred values.
template <typename... Values>
class BOOST_ASIO_NODISCARD deferred_values
{
private:
std::tuple<Values...> values_;
struct initiate
{
template <typename Handler, typename... V>
void operator()(Handler handler, BOOST_ASIO_MOVE_ARG(V)... values)
{
BOOST_ASIO_MOVE_OR_LVALUE(Handler)(handler)(
BOOST_ASIO_MOVE_CAST(V)(values)...);
}
};
template <typename CompletionToken, std::size_t... I>
decltype(auto) invoke_helper(
BOOST_ASIO_MOVE_ARG(CompletionToken) token,
std::index_sequence<I...>)
{
return boost::asio::async_initiate<CompletionToken, void(Values...)>(
initiate(), token,
std::get<I>(BOOST_ASIO_MOVE_CAST(std::tuple<Values...>)(values_))...);
}
public:
/// Construct a deferred asynchronous operation from the arguments to an
/// initiation function object.
template <typename... V>
BOOST_ASIO_CONSTEXPR explicit deferred_values(
deferred_init_tag, BOOST_ASIO_MOVE_ARG(V)... values)
: values_(BOOST_ASIO_MOVE_CAST(V)(values)...)
{
}
/// Initiate the deferred operation using the supplied completion token.
template <BOOST_ASIO_COMPLETION_TOKEN_FOR(void(Values...)) CompletionToken>
decltype(auto) operator()(
BOOST_ASIO_MOVE_ARG(CompletionToken) token) BOOST_ASIO_RVALUE_REF_QUAL
{
return this->invoke_helper(
BOOST_ASIO_MOVE_CAST(CompletionToken)(token),
std::index_sequence_for<Values...>());
}
#if defined(BOOST_ASIO_HAS_REF_QUALIFIED_FUNCTIONS)
template <BOOST_ASIO_COMPLETION_TOKEN_FOR(void(Values...)) CompletionToken>
decltype(auto) operator()(
BOOST_ASIO_MOVE_ARG(CompletionToken) token) const &
{
return deferred_values(*this)(
BOOST_ASIO_MOVE_CAST(CompletionToken)(token));
}
#endif // defined(BOOST_ASIO_HAS_REF_QUALIFIED_FUNCTIONS)
};
#if !defined(GENERATING_DOCUMENTATION)
template <typename... Values>
struct is_deferred<deferred_values<Values...> > : true_type
{
};
#endif // !defined(GENERATING_DOCUMENTATION)
/// Encapsulates a deferred asynchronous operation.
template <typename Signature, typename Initiation, typename... InitArgs>
class BOOST_ASIO_NODISCARD deferred_async_operation
{
private:
typename decay<Initiation>::type initiation_;
typedef std::tuple<typename decay<InitArgs>::type...> init_args_t;
init_args_t init_args_;
template <typename CompletionToken, std::size_t... I>
decltype(auto) invoke_helper(
BOOST_ASIO_MOVE_ARG(CompletionToken) token,
std::index_sequence<I...>)
{
return boost::asio::async_initiate<CompletionToken, Signature>(
BOOST_ASIO_MOVE_CAST(typename decay<Initiation>::type)(initiation_),
token, std::get<I>(BOOST_ASIO_MOVE_CAST(init_args_t)(init_args_))...);
}
public:
/// Construct a deferred asynchronous operation from the arguments to an
/// initiation function object.
template <typename I, typename... A>
BOOST_ASIO_CONSTEXPR explicit deferred_async_operation(
deferred_init_tag, BOOST_ASIO_MOVE_ARG(I) initiation,
BOOST_ASIO_MOVE_ARG(A)... init_args)
: initiation_(BOOST_ASIO_MOVE_CAST(I)(initiation)),
init_args_(BOOST_ASIO_MOVE_CAST(A)(init_args)...)
{
}
/// Initiate the asynchronous operation using the supplied completion token.
template <BOOST_ASIO_COMPLETION_TOKEN_FOR(Signature) CompletionToken>
decltype(auto) operator()(
BOOST_ASIO_MOVE_ARG(CompletionToken) token) BOOST_ASIO_RVALUE_REF_QUAL
{
return this->invoke_helper(
BOOST_ASIO_MOVE_CAST(CompletionToken)(token),
std::index_sequence_for<InitArgs...>());
}
#if defined(BOOST_ASIO_HAS_REF_QUALIFIED_FUNCTIONS)
template <BOOST_ASIO_COMPLETION_TOKEN_FOR(Signature) CompletionToken>
decltype(auto) operator()(
BOOST_ASIO_MOVE_ARG(CompletionToken) token) const &
{
return deferred_async_operation(*this)(
BOOST_ASIO_MOVE_CAST(CompletionToken)(token));
}
#endif // defined(BOOST_ASIO_HAS_REF_QUALIFIED_FUNCTIONS)
};
#if !defined(GENERATING_DOCUMENTATION)
template <typename Signature, typename Initiation, typename... InitArgs>
struct is_deferred<
deferred_async_operation<Signature, Initiation, InitArgs...> > : true_type
{
};
#endif // !defined(GENERATING_DOCUMENTATION)
/// Defines a link between two consecutive operations in a sequence.
template <typename Head, typename Tail>
class BOOST_ASIO_NODISCARD deferred_sequence
{
private:
typedef typename detail::deferred_sequence_signature<
typename detail::deferred_signature<Head>::type, Tail>::type
signature;
public:
template <typename H, typename T>
BOOST_ASIO_CONSTEXPR explicit deferred_sequence(deferred_init_tag,
BOOST_ASIO_MOVE_ARG(H) head, BOOST_ASIO_MOVE_ARG(T) tail)
: head_(BOOST_ASIO_MOVE_CAST(H)(head)),
tail_(BOOST_ASIO_MOVE_CAST(T)(tail))
{
}
template <BOOST_ASIO_COMPLETION_TOKEN_FOR(signature) CompletionToken>
decltype(auto) operator()(
BOOST_ASIO_MOVE_ARG(CompletionToken) token) BOOST_ASIO_RVALUE_REF_QUAL
{
return boost::asio::async_initiate<CompletionToken, signature>(
initiate(), token, BOOST_ASIO_MOVE_OR_LVALUE(Head)(head_),
BOOST_ASIO_MOVE_OR_LVALUE(Tail)(tail_));
}
#if defined(BOOST_ASIO_HAS_REF_QUALIFIED_FUNCTIONS)
template <BOOST_ASIO_COMPLETION_TOKEN_FOR(signature) CompletionToken>
decltype(auto) operator()(
BOOST_ASIO_MOVE_ARG(CompletionToken) token) const &
{
return deferred_sequence(*this)(
BOOST_ASIO_MOVE_CAST(CompletionToken)(token));
}
#endif // defined(BOOST_ASIO_HAS_REF_QUALIFIED_FUNCTIONS)
private:
struct initiate
{
template <typename Handler>
void operator()(BOOST_ASIO_MOVE_ARG(Handler) handler,
Head head, BOOST_ASIO_MOVE_ARG(Tail) tail)
{
BOOST_ASIO_MOVE_OR_LVALUE(Head)(head)(
detail::deferred_sequence_handler<
typename decay<Handler>::type,
typename decay<Tail>::type>(
BOOST_ASIO_MOVE_CAST(Handler)(handler),
BOOST_ASIO_MOVE_CAST(Tail)(tail)));
}
};
Head head_;
Tail tail_;
};
#if !defined(GENERATING_DOCUMENTATION)
template <typename Head, typename Tail>
struct is_deferred<deferred_sequence<Head, Tail> > : true_type
{
};
#endif // !defined(GENERATING_DOCUMENTATION)
/// Used to represent a deferred conditional branch.
template <typename OnTrue = deferred_noop,
typename OnFalse = deferred_noop>
class BOOST_ASIO_NODISCARD deferred_conditional
{
public:
/// Construct a deferred conditional with the value to determine which branch
/// will be executed.
BOOST_ASIO_CONSTEXPR explicit deferred_conditional(bool b)
: on_true_(),
on_false_(),
bool_(b)
{
}
/// Invoke the conditional branch bsaed on the stored alue.
template <typename... Args>
auto operator()(BOOST_ASIO_MOVE_ARG(Args)... args) BOOST_ASIO_RVALUE_REF_QUAL
{
if (bool_)
{
return BOOST_ASIO_MOVE_OR_LVALUE(OnTrue)(on_true_)(
BOOST_ASIO_MOVE_CAST(Args)(args)...);
}
else
{
return BOOST_ASIO_MOVE_OR_LVALUE(OnFalse)(on_false_)(
BOOST_ASIO_MOVE_CAST(Args)(args)...);
}
}
#if defined(BOOST_ASIO_HAS_REF_QUALIFIED_FUNCTIONS)
template <typename... Args>
auto operator()(BOOST_ASIO_MOVE_ARG(Args)... args) const &
{
return deferred_conditional(*this)(
BOOST_ASIO_MOVE_CAST(Args)(args)...);
}
#endif // defined(BOOST_ASIO_HAS_REF_QUALIFIED_FUNCTIONS)
/// Set the true branch of the conditional.
template <typename T>
deferred_conditional<T, OnFalse> then(T on_true,
typename constraint<
is_deferred<T>::value
>::type* = 0,
typename constraint<
is_same<
typename conditional<true, OnTrue, T>::type,
deferred_noop
>::value
>::type* = 0) BOOST_ASIO_RVALUE_REF_QUAL
{
return deferred_conditional<T, OnFalse>(
bool_, BOOST_ASIO_MOVE_CAST(T)(on_true),
BOOST_ASIO_MOVE_CAST(OnFalse)(on_false_));
}
/// Set the false branch of the conditional.
template <typename T>
deferred_conditional<OnTrue, T> otherwise(T on_false,
typename constraint<
is_deferred<T>::value
>::type* = 0,
typename constraint<
!is_same<
typename conditional<true, OnTrue, T>::type,
deferred_noop
>::value
>::type* = 0,
typename constraint<
is_same<
typename conditional<true, OnFalse, T>::type,
deferred_noop
>::value
>::type* = 0) BOOST_ASIO_RVALUE_REF_QUAL
{
return deferred_conditional<OnTrue, T>(
bool_, BOOST_ASIO_MOVE_CAST(OnTrue)(on_true_),
BOOST_ASIO_MOVE_CAST(T)(on_false));
}
private:
template <typename T, typename F> friend class deferred_conditional;
// Helper constructor.
template <typename T, typename F>
explicit deferred_conditional(bool b, BOOST_ASIO_MOVE_ARG(T) on_true,
BOOST_ASIO_MOVE_ARG(F) on_false)
: on_true_(BOOST_ASIO_MOVE_CAST(T)(on_true)),
on_false_(BOOST_ASIO_MOVE_CAST(F)(on_false)),
bool_(b)
{
}
OnTrue on_true_;
OnFalse on_false_;
bool bool_;
};
#if !defined(GENERATING_DOCUMENTATION)
template <typename OnTrue, typename OnFalse>
struct is_deferred<deferred_conditional<OnTrue, OnFalse> > : true_type
{
};
#endif // !defined(GENERATING_DOCUMENTATION)
/// Class used to specify that an asynchronous operation should return a
/// function object to lazily launch the operation.
/**
* The deferred_t class is used to indicate that an asynchronous operation
* should return a function object which is itself an initiation function. A
* deferred_t object may be passed as a completion token to an asynchronous
* operation, typically using the special value @c boost::asio::deferred. For
* example:
*
* @code auto my_sender
* = my_socket.async_read_some(my_buffer,
* boost::asio::experimental::deferred); @endcode
*
* The initiating function (async_read_some in the above example) returns a
* function object that will lazily initiate the operation.
*/
class deferred_t
{
public:
/// Default constructor.
BOOST_ASIO_CONSTEXPR deferred_t()
{
}
/// Adapts an executor to add the @c deferred_t completion token as the
/// default.
template <typename InnerExecutor>
struct executor_with_default : InnerExecutor
{
/// Specify @c deferred_t as the default completion token type.
typedef deferred_t default_completion_token_type;
/// Construct the adapted executor from the inner executor type.
template <typename InnerExecutor1>
executor_with_default(const InnerExecutor1& ex,
typename constraint<
conditional<
!is_same<InnerExecutor1, executor_with_default>::value,
is_convertible<InnerExecutor1, InnerExecutor>,
false_type
>::type::value
>::type = 0) BOOST_ASIO_NOEXCEPT
: InnerExecutor(ex)
{
}
};
/// Type alias to adapt an I/O object to use @c deferred_t as its
/// default completion token type.
#if defined(BOOST_ASIO_HAS_ALIAS_TEMPLATES) \
|| defined(GENERATING_DOCUMENTATION)
template <typename T>
using as_default_on_t = typename T::template rebind_executor<
executor_with_default<typename T::executor_type> >::other;
#endif // defined(BOOST_ASIO_HAS_ALIAS_TEMPLATES)
// || defined(GENERATING_DOCUMENTATION)
/// Function helper to adapt an I/O object to use @c deferred_t as its
/// default completion token type.
template <typename T>
static typename decay<T>::type::template rebind_executor<
executor_with_default<typename decay<T>::type::executor_type>
>::other
as_default_on(BOOST_ASIO_MOVE_ARG(T) object)
{
return typename decay<T>::type::template rebind_executor<
executor_with_default<typename decay<T>::type::executor_type>
>::other(BOOST_ASIO_MOVE_CAST(T)(object));
}
/// Creates a new deferred from a function.
template <typename Function>
typename constraint<
!is_deferred<typename decay<Function>::type>::value,
deferred_function<typename decay<Function>::type>
>::type operator()(BOOST_ASIO_MOVE_ARG(Function) function) const
{
return deferred_function<typename decay<Function>::type>(
deferred_init_tag{}, BOOST_ASIO_MOVE_CAST(Function)(function));
}
/// Passes through anything that is already deferred.
template <typename T>
typename constraint<
is_deferred<typename decay<T>::type>::value,
typename decay<T>::type
>::type operator()(BOOST_ASIO_MOVE_ARG(T) t) const
{
return BOOST_ASIO_MOVE_CAST(T)(t);
}
/// Returns a deferred operation that returns the provided values.
template <typename... Args>
static BOOST_ASIO_CONSTEXPR deferred_values<typename decay<Args>::type...>
values(BOOST_ASIO_MOVE_ARG(Args)... args)
{
return deferred_values<typename decay<Args>::type...>(
deferred_init_tag{}, BOOST_ASIO_MOVE_CAST(Args)(args)...);
}
/// Creates a conditional object for branching deferred operations.
static BOOST_ASIO_CONSTEXPR deferred_conditional<> when(bool b)
{
return deferred_conditional<>(b);
}
};
/// Pipe operator used to chain deferred operations.
template <typename Head, typename Tail>
inline auto operator|(Head head, BOOST_ASIO_MOVE_ARG(Tail) tail)
-> typename constraint<
is_deferred<Head>::value,
decltype(BOOST_ASIO_MOVE_OR_LVALUE(Head)(head)(
BOOST_ASIO_MOVE_CAST(Tail)(tail)))
>::type
{
return BOOST_ASIO_MOVE_OR_LVALUE(Head)(head)(
BOOST_ASIO_MOVE_CAST(Tail)(tail));
}
/// A special value, similar to std::nothrow.
/**
* See the documentation for boost::asio::experimental::deferred_t for a usage
* example.
*/
#if defined(BOOST_ASIO_HAS_CONSTEXPR) || defined(GENERATING_DOCUMENTATION)
constexpr deferred_t deferred;
#elif defined(BOOST_ASIO_MSVC)
__declspec(selectany) deferred_t deferred;
#endif
} // namespace experimental
} // namespace asio
} // namespace boost
#include <boost/asio/detail/pop_options.hpp>
#include <boost/asio/experimental/impl/deferred.hpp>
#endif // BOOST_ASIO_EXPERIMENTAL_DEFERRED_HPP

View File

@@ -0,0 +1,72 @@
//
// experimental/detail/channel_handler.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2022 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 BOOST_ASIO_EXPERIMENTAL_DETAIL_CHANNEL_HANDLER_HPP
#define BOOST_ASIO_EXPERIMENTAL_DETAIL_CHANNEL_HANDLER_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include <boost/asio/detail/config.hpp>
#include <boost/asio/associator.hpp>
#include <boost/asio/experimental/detail/channel_payload.hpp>
#include <boost/asio/detail/push_options.hpp>
namespace boost {
namespace asio {
namespace experimental {
namespace detail {
template <typename Payload, typename Handler>
class channel_handler
{
public:
channel_handler(BOOST_ASIO_MOVE_ARG(Payload) p, Handler& h)
: payload_(BOOST_ASIO_MOVE_CAST(Payload)(p)),
handler_(BOOST_ASIO_MOVE_CAST(Handler)(h))
{
}
void operator()()
{
payload_.receive(handler_);
}
//private:
Payload payload_;
Handler handler_;
};
} // namespace detail
} // namespace experimental
template <template <typename, typename> class Associator,
typename Payload, typename Handler, typename DefaultCandidate>
struct associator<Associator,
experimental::detail::channel_handler<Payload, Handler>,
DefaultCandidate>
: Associator<Handler, DefaultCandidate>
{
static typename Associator<Handler, DefaultCandidate>::type get(
const experimental::detail::channel_handler<Payload, Handler>& h,
const DefaultCandidate& c = DefaultCandidate()) BOOST_ASIO_NOEXCEPT
{
return Associator<Handler, DefaultCandidate>::get(h.handler_, c);
}
};
} // namespace asio
} // namespace boost
#include <boost/asio/detail/pop_options.hpp>
#endif // BOOST_ASIO_EXPERIMENTAL_DETAIL_CHANNEL_HANDLER_HPP

View File

@@ -0,0 +1,124 @@
//
// experimental/detail/channel_message.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2022 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 BOOST_ASIO_EXPERIMENTAL_DETAIL_CHANNEL_MESSAGE_HPP
#define BOOST_ASIO_EXPERIMENTAL_DETAIL_CHANNEL_MESSAGE_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include <boost/asio/detail/config.hpp>
#include <tuple>
#include <boost/asio/detail/type_traits.hpp>
#include <boost/asio/detail/push_options.hpp>
namespace boost {
namespace asio {
namespace experimental {
namespace detail {
template <typename Signature>
class channel_message;
template <typename R>
class channel_message<R()>
{
public:
channel_message(int)
{
}
template <typename Handler>
void receive(Handler& handler)
{
BOOST_ASIO_MOVE_OR_LVALUE(Handler)(handler)();
}
};
template <typename R, typename Arg0>
class channel_message<R(Arg0)>
{
public:
template <typename T0>
channel_message(int, BOOST_ASIO_MOVE_ARG(T0) t0)
: arg0_(BOOST_ASIO_MOVE_CAST(T0)(t0))
{
}
template <typename Handler>
void receive(Handler& handler)
{
BOOST_ASIO_MOVE_OR_LVALUE(Handler)(handler)(
BOOST_ASIO_MOVE_CAST(arg0_type)(arg0_));
}
private:
typedef typename decay<Arg0>::type arg0_type;
arg0_type arg0_;
};
template <typename R, typename Arg0, typename Arg1>
class channel_message<R(Arg0, Arg1)>
{
public:
template <typename T0, typename T1>
channel_message(int, BOOST_ASIO_MOVE_ARG(T0) t0, BOOST_ASIO_MOVE_ARG(T1) t1)
: arg0_(BOOST_ASIO_MOVE_CAST(T0)(t0)),
arg1_(BOOST_ASIO_MOVE_CAST(T1)(t1))
{
}
template <typename Handler>
void receive(Handler& handler)
{
BOOST_ASIO_MOVE_OR_LVALUE(Handler)(handler)(
BOOST_ASIO_MOVE_CAST(arg0_type)(arg0_),
BOOST_ASIO_MOVE_CAST(arg1_type)(arg1_));
}
private:
typedef typename decay<Arg0>::type arg0_type;
arg0_type arg0_;
typedef typename decay<Arg1>::type arg1_type;
arg1_type arg1_;
};
template <typename R, typename... Args>
class channel_message<R(Args...)>
{
public:
template <typename... T>
channel_message(int, BOOST_ASIO_MOVE_ARG(T)... t)
: args_(BOOST_ASIO_MOVE_CAST(T)(t)...)
{
}
template <typename Handler>
void receive(Handler& h)
{
std::apply(BOOST_ASIO_MOVE_OR_LVALUE(Handler)(h),
BOOST_ASIO_MOVE_CAST(args_type)(args_));
}
private:
typedef std::tuple<typename decay<Args>::type...> args_type;
args_type args_;
};
} // namespace detail
} // namespace experimental
} // namespace asio
} // namespace boost
#include <boost/asio/detail/pop_options.hpp>
#endif // BOOST_ASIO_EXPERIMENTAL_DETAIL_CHANNEL_MESSAGE_HPP

View File

@@ -0,0 +1,201 @@
//
// experimental/detail/channel_operation.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2022 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 BOOST_ASIO_EXPERIMENTAL_DETAIL_CHANNEL_OPERATION_HPP
#define BOOST_ASIO_EXPERIMENTAL_DETAIL_CHANNEL_OPERATION_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include <boost/asio/detail/config.hpp>
#include <boost/asio/associated_allocator.hpp>
#include <boost/asio/associated_executor.hpp>
#include <boost/asio/detail/op_queue.hpp>
#include <boost/asio/execution/executor.hpp>
#include <boost/asio/execution/outstanding_work.hpp>
#include <boost/asio/executor_work_guard.hpp>
#include <boost/asio/prefer.hpp>
#include <boost/asio/detail/push_options.hpp>
namespace boost {
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 BOOST_ASIO_INHERIT_TRACKED_HANDLER
{
public:
template <typename Executor, 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,
complete_op = 1,
cancel_op = 2,
close_op = 3
};
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 boost::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>
class channel_operation::handler_work_base
{
public:
handler_work_base(int, const Executor& ex)
: executor_(boost::asio::prefer(ex, execution::outstanding_work.tracked))
{
}
template <typename Function, typename Handler>
void post(Function& function, Handler& handler)
{
typename associated_allocator<Handler>::type allocator =
(get_associated_allocator)(handler);
execution::execute(
boost::asio::prefer(
boost::asio::require(executor_, execution::blocking.never),
execution::allocator(allocator)),
BOOST_ASIO_MOVE_CAST(Function)(function));
}
private:
typename decay<
typename prefer_result<Executor,
execution::outstanding_work_t::tracked_t
>::type
>::type executor_;
};
#if !defined(BOOST_ASIO_NO_TS_EXECUTORS)
template <typename Executor>
class channel_operation::handler_work_base<Executor,
typename enable_if<
!execution::is_executor<Executor>::value
>::type>
{
public:
handler_work_base(int, const Executor& ex)
: work_(ex)
{
}
template <typename Function, typename Handler>
void post(Function& function, Handler& handler)
{
typename associated_allocator<Handler>::type allocator =
(get_associated_allocator)(handler);
work_.get_executor().post(
BOOST_ASIO_MOVE_CAST(Function)(function), allocator);
}
private:
executor_work_guard<Executor> work_;
};
#endif // !defined(BOOST_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<
typename associated_executor<Handler, IoExecutor>::type, IoExecutor>
{
public:
typedef channel_operation::handler_work_base<IoExecutor> base1_type;
typedef channel_operation::handler_work_base<
typename associated_executor<Handler, IoExecutor>::type, IoExecutor>
base2_type;
handler_work(Handler& handler, const IoExecutor& io_ex) BOOST_ASIO_NOEXCEPT
: base1_type(0, io_ex),
base2_type(0, (get_associated_executor)(handler, io_ex))
{
}
template <typename Function>
void complete(Function& function, Handler& handler)
{
base2_type::post(function, handler);
}
};
template <typename Handler, typename IoExecutor>
class channel_operation::handler_work<
Handler, IoExecutor,
typename enable_if<
is_same<
typename associated_executor<Handler,
IoExecutor>::asio_associated_executor_is_unspecialised,
void
>::value
>::type> : handler_work_base<IoExecutor>
{
public:
typedef channel_operation::handler_work_base<IoExecutor> base1_type;
handler_work(Handler&, const IoExecutor& io_ex) BOOST_ASIO_NOEXCEPT
: base1_type(0, io_ex)
{
}
template <typename Function>
void complete(Function& function, Handler& handler)
{
base1_type::post(function, handler);
}
};
} // namespace detail
} // namespace experimental
} // namespace asio
} // namespace boost
#include <boost/asio/detail/pop_options.hpp>
#endif // BOOST_ASIO_EXPERIMENTAL_DETAIL_CHANNEL_OPERATION_HPP

View File

@@ -0,0 +1,95 @@
//
// experimental/detail/channel_payload.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2022 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 BOOST_ASIO_EXPERIMENTAL_DETAIL_CHANNEL_PAYLOAD_HPP
#define BOOST_ASIO_EXPERIMENTAL_DETAIL_CHANNEL_PAYLOAD_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include <boost/asio/detail/config.hpp>
#include <variant>
#include <boost/asio/detail/type_traits.hpp>
#include <boost/asio/experimental/detail/channel_message.hpp>
#include <boost/asio/detail/push_options.hpp>
namespace boost {
namespace asio {
namespace experimental {
namespace detail {
template <typename... Signatures>
class channel_payload
{
public:
template <typename Signature>
channel_payload(BOOST_ASIO_MOVE_ARG(channel_message<Signature>) m)
: message_(BOOST_ASIO_MOVE_CAST(channel_message<Signature>)(m))
{
}
template <typename Handler>
void receive(Handler& handler)
{
std::visit(
[&](auto& message)
{
message.receive(handler);
}, message_);
}
private:
std::variant<channel_message<Signatures>...> message_;
};
template <typename R>
class channel_payload<R()>
{
public:
explicit channel_payload(channel_message<R()>)
{
}
template <typename Handler>
void receive(Handler& handler)
{
BOOST_ASIO_MOVE_OR_LVALUE(Handler)(handler)();
}
};
template <typename Signature>
class channel_payload<Signature>
{
public:
channel_payload(BOOST_ASIO_MOVE_ARG(channel_message<Signature>) m)
: message_(BOOST_ASIO_MOVE_CAST(channel_message<Signature>)(m))
{
}
template <typename Handler>
void receive(Handler& handler)
{
message_.receive(handler);
}
private:
channel_message<Signature> message_;
};
} // namespace detail
} // namespace experimental
} // namespace asio
} // namespace boost
#include <boost/asio/detail/pop_options.hpp>
#endif // BOOST_ASIO_EXPERIMENTAL_DETAIL_CHANNEL_PAYLOAD_HPP

View File

@@ -0,0 +1,114 @@
//
// experimental/detail/channel_receive_op.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2022 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 BOOST_ASIO_EXPERIMENTAL_DETAIL_CHANNEL_RECEIVE_OP_HPP
#define BOOST_ASIO_EXPERIMENTAL_DETAIL_CHANNEL_RECEIVE_OP_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include <boost/asio/detail/config.hpp>
#include <boost/asio/detail/bind_handler.hpp>
#include <boost/asio/detail/handler_alloc_helpers.hpp>
#include <boost/asio/error.hpp>
#include <boost/asio/experimental/detail/channel_handler.hpp>
#include <boost/asio/experimental/detail/channel_operation.hpp>
#include <boost/asio/experimental/detail/channel_payload.hpp>
#include <boost/asio/detail/push_options.hpp>
namespace boost {
namespace asio {
namespace experimental {
namespace detail {
template <typename Payload>
class channel_receive : public channel_operation
{
public:
void complete(Payload payload)
{
func_(this, complete_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:
BOOST_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_(BOOST_ASIO_MOVE_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 = { boost::asio::detail::addressof(o->handler_), o, o };
BOOST_ASIO_HANDLER_COMPLETION((*o));
// Take ownership of the operation's outstanding work.
channel_operation::handler_work<Handler, IoExecutor> w(
BOOST_ASIO_MOVE_CAST2(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::complete_op)
{
Payload* payload = static_cast<Payload*>(v);
channel_handler<Payload, Handler> handler(
BOOST_ASIO_MOVE_CAST(Payload)(*payload), o->handler_);
p.h = boost::asio::detail::addressof(handler.handler_);
p.reset();
BOOST_ASIO_HANDLER_INVOCATION_BEGIN(());
w.complete(handler, handler.handler_);
BOOST_ASIO_HANDLER_INVOCATION_END;
}
else
{
boost::asio::detail::binder0<Handler> handler(o->handler_);
p.h = boost::asio::detail::addressof(handler.handler_);
p.reset();
}
}
private:
Handler handler_;
channel_operation::handler_work<Handler, IoExecutor> work_;
};
} // namespace detail
} // namespace experimental
} // namespace asio
} // namespace boost
#include <boost/asio/detail/pop_options.hpp>
#endif // BOOST_ASIO_EXPERIMENTAL_DETAIL_CHANNEL_RECEIVE_OP_HPP

View File

@@ -0,0 +1,134 @@
//
// experimental/detail/channel_send_functions.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2022 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 BOOST_ASIO_EXPERIMENTAL_DETAIL_CHANNEL_SEND_FUNCTIONS_HPP
#define BOOST_ASIO_EXPERIMENTAL_DETAIL_CHANNEL_SEND_FUNCTIONS_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include <boost/asio/detail/config.hpp>
#include <boost/asio/async_result.hpp>
#include <boost/asio/detail/type_traits.hpp>
#include <boost/system/error_code.hpp>
#include <boost/asio/experimental/detail/channel_message.hpp>
#include <boost/asio/detail/push_options.hpp>
namespace boost {
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>
typename enable_if<
is_constructible<detail::channel_message<R(Args...)>, int, Args2...>::value,
bool
>::type try_send(BOOST_ASIO_MOVE_ARG(Args2)... args)
{
typedef typename detail::channel_message<R(Args...)> message_type;
Derived* self = static_cast<Derived*>(this);
return self->service_->template try_send<message_type>(
self->impl_, BOOST_ASIO_MOVE_CAST(Args2)(args)...);
}
template <typename... Args2>
typename enable_if<
is_constructible<detail::channel_message<R(Args...)>, int, Args2...>::value,
std::size_t
>::type try_send_n(std::size_t count, BOOST_ASIO_MOVE_ARG(Args2)... args)
{
typedef typename detail::channel_message<R(Args...)> message_type;
Derived* self = static_cast<Derived*>(this);
return self->service_->template try_send_n<message_type>(
self->impl_, count, BOOST_ASIO_MOVE_CAST(Args2)(args)...);
}
template <
BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code))
CompletionToken BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(Executor)>
auto async_send(Args... args,
BOOST_ASIO_MOVE_ARG(CompletionToken) token
BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(Executor))
{
typedef typename Derived::payload_type payload_type;
typedef typename detail::channel_message<R(Args...)> message_type;
Derived* self = static_cast<Derived*>(this);
return async_initiate<CompletionToken, void (boost::system::error_code)>(
typename Derived::initiate_async_send(self), token,
payload_type(message_type(0, BOOST_ASIO_MOVE_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>
typename enable_if<
is_constructible<detail::channel_message<R(Args...)>, int, Args2...>::value,
bool
>::type try_send(BOOST_ASIO_MOVE_ARG(Args2)... args)
{
typedef typename detail::channel_message<R(Args...)> message_type;
Derived* self = static_cast<Derived*>(this);
return self->service_->template try_send<message_type>(
self->impl_, BOOST_ASIO_MOVE_CAST(Args2)(args)...);
}
template <typename... Args2>
typename enable_if<
is_constructible<detail::channel_message<R(Args...)>, int, Args2...>::value,
std::size_t
>::type try_send_n(std::size_t count, BOOST_ASIO_MOVE_ARG(Args2)... args)
{
typedef typename detail::channel_message<R(Args...)> message_type;
Derived* self = static_cast<Derived*>(this);
return self->service_->template try_send_n<message_type>(
self->impl_, count, BOOST_ASIO_MOVE_CAST(Args2)(args)...);
}
template <
BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code))
CompletionToken BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(Executor)>
auto async_send(Args... args,
BOOST_ASIO_MOVE_ARG(CompletionToken) token
BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(Executor))
{
typedef typename Derived::payload_type payload_type;
typedef typename detail::channel_message<R(Args...)> message_type;
Derived* self = static_cast<Derived*>(this);
return async_initiate<CompletionToken, void (boost::system::error_code)>(
typename Derived::initiate_async_send(self), token,
payload_type(message_type(0, BOOST_ASIO_MOVE_CAST(Args)(args)...)));
}
};
} // namespace detail
} // namespace experimental
} // namespace asio
} // namespace boost
#include <boost/asio/detail/pop_options.hpp>
#endif // BOOST_ASIO_EXPERIMENTAL_DETAIL_CHANNEL_SEND_FUNCTIONS_HPP

View File

@@ -0,0 +1,142 @@
//
// experimental/detail/channel_send_op.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2022 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 BOOST_ASIO_EXPERIMENTAL_DETAIL_CHANNEL_SEND_OP_HPP
#define BOOST_ASIO_EXPERIMENTAL_DETAIL_CHANNEL_SEND_OP_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include <boost/asio/detail/config.hpp>
#include <boost/asio/detail/bind_handler.hpp>
#include <boost/asio/detail/handler_alloc_helpers.hpp>
#include <boost/asio/error.hpp>
#include <boost/asio/experimental/channel_error.hpp>
#include <boost/asio/experimental/detail/channel_operation.hpp>
#include <boost/asio/experimental/detail/channel_payload.hpp>
#include <boost/asio/detail/push_options.hpp>
namespace boost {
namespace asio {
namespace experimental {
namespace detail {
template <typename Payload>
class channel_send : public channel_operation
{
public:
Payload get_payload()
{
return BOOST_ASIO_MOVE_CAST(Payload)(payload_);
}
void complete()
{
func_(this, complete_op, 0);
}
void cancel()
{
func_(this, cancel_op, 0);
}
void close()
{
func_(this, close_op, 0);
}
protected:
channel_send(func_type func, BOOST_ASIO_MOVE_ARG(Payload) payload)
: channel_operation(func),
payload_(BOOST_ASIO_MOVE_CAST(Payload)(payload))
{
}
private:
Payload payload_;
};
template <typename Payload, typename Handler, typename IoExecutor>
class channel_send_op : public channel_send<Payload>
{
public:
BOOST_ASIO_DEFINE_HANDLER_PTR(channel_send_op);
channel_send_op(BOOST_ASIO_MOVE_ARG(Payload) payload,
Handler& handler, const IoExecutor& io_ex)
: channel_send<Payload>(&channel_send_op::do_action,
BOOST_ASIO_MOVE_CAST(Payload)(payload)),
handler_(BOOST_ASIO_MOVE_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 = { boost::asio::detail::addressof(o->handler_), o, o };
BOOST_ASIO_HANDLER_COMPLETION((*o));
// Take ownership of the operation's outstanding work.
channel_operation::handler_work<Handler, IoExecutor> w(
BOOST_ASIO_MOVE_CAST2(channel_operation::handler_work<
Handler, IoExecutor>)(o->work_));
boost::system::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.
boost::asio::detail::binder1<Handler, boost::system::error_code>
handler(o->handler_, ec);
p.h = boost::asio::detail::addressof(handler.handler_);
p.reset();
// Post the completion if required.
if (a != channel_operation::destroy_op)
{
BOOST_ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_));
w.complete(handler, handler.handler_);
BOOST_ASIO_HANDLER_INVOCATION_END;
}
}
private:
Handler handler_;
channel_operation::handler_work<Handler, IoExecutor> work_;
};
} // namespace detail
} // namespace experimental
} // namespace asio
} // namespace boost
#include <boost/asio/detail/pop_options.hpp>
#endif // BOOST_ASIO_EXPERIMENTAL_DETAIL_CHANNEL_SEND_OP_HPP

View File

@@ -0,0 +1,499 @@
//
// experimental/detail/channel_service.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2022 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 BOOST_ASIO_EXPERIMENTAL_DETAIL_CHANNEL_SERVICE_HPP
#define BOOST_ASIO_EXPERIMENTAL_DETAIL_CHANNEL_SERVICE_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include <boost/asio/detail/config.hpp>
#include <boost/asio/associated_cancellation_slot.hpp>
#include <boost/asio/cancellation_type.hpp>
#include <boost/asio/detail/mutex.hpp>
#include <boost/asio/detail/op_queue.hpp>
#include <boost/asio/execution_context.hpp>
#include <boost/asio/experimental/detail/channel_message.hpp>
#include <boost/asio/experimental/detail/channel_receive_op.hpp>
#include <boost/asio/experimental/detail/channel_send_op.hpp>
#include <boost/asio/experimental/detail/has_signature.hpp>
#include <boost/asio/detail/push_options.hpp>
namespace boost {
namespace asio {
namespace experimental {
namespace detail {
template <typename Mutex>
class channel_service
: public boost::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.
boost::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(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 BOOST_ASIO_NOEXCEPT;
// Determine whether the channel is open.
bool is_open(const base_implementation_type& impl) const BOOST_ASIO_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 BOOST_ASIO_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,
BOOST_ASIO_MOVE_ARG(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, BOOST_ASIO_MOVE_ARG(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,
BOOST_ASIO_MOVE_ARG2(typename implementation_type<
Traits, Signatures...>::payload_type) payload,
Handler& handler, const IoExecutor& io_ex)
{
typename associated_cancellation_slot<Handler>::type slot
= boost::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 = { boost::asio::detail::addressof(handler),
op::ptr::allocate(handler), 0 };
p.p = new (p.v) op(BOOST_ASIO_MOVE_CAST2(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);
}
BOOST_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,
BOOST_ASIO_MOVE_ARG(Handler) handler);
// Asynchronously send a new value into the channel.
// 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)
{
typename associated_cancellation_slot<Handler>::type slot
= boost::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 = { boost::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);
}
BOOST_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 complete_receive
{
explicit complete_receive(channel_receive<Payload>* op)
: op_(op)
{
}
template <typename... Args>
void operator()(BOOST_ASIO_MOVE_ARG(Args)... args)
{
op_->complete(
channel_message<Signature>(0,
BOOST_ASIO_MOVE_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.
boost::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 typename conditional<
has_signature<
typename traits_type::receive_cancelled_signature,
Signatures...
>::value,
typename conditional<
has_signature<
typename traits_type::receive_closed_signature,
Signatures...
>::value,
channel_payload<Signatures...>,
channel_payload<
Signatures...,
typename traits_type::receive_closed_signature
>
>::type,
typename conditional<
has_signature<
typename traits_type::receive_closed_signature,
Signatures...,
typename traits_type::receive_cancelled_signature
>::value,
channel_payload<
Signatures...,
typename traits_type::receive_cancelled_signature
>,
channel_payload<
Signatures...,
typename traits_type::receive_cancelled_signature,
typename traits_type::receive_closed_signature
>
>::type
>::type payload_type;
// Move from another buffer.
void buffer_move_from(implementation_type& other)
{
buffer_ = BOOST_ASIO_MOVE_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(BOOST_ASIO_MOVE_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 BOOST_ASIO_MOVE_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 typename conditional<
has_signature<
typename traits_type::receive_cancelled_signature,
R()
>::value,
typename conditional<
has_signature<
typename traits_type::receive_closed_signature,
R()
>::value,
channel_payload<R()>,
channel_payload<
R(),
typename traits_type::receive_closed_signature
>
>::type,
typename conditional<
has_signature<
typename traits_type::receive_closed_signature,
R(),
typename traits_type::receive_cancelled_signature
>::value,
channel_payload<
R(),
typename traits_type::receive_cancelled_signature
>,
channel_payload<
R(),
typename traits_type::receive_cancelled_signature,
typename traits_type::receive_closed_signature
>
>::type
>::type 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(channel_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_;
};
} // namespace detail
} // namespace experimental
} // namespace asio
} // namespace boost
#include <boost/asio/detail/pop_options.hpp>
#include <boost/asio/experimental/detail/impl/channel_service.hpp>
#endif // BOOST_ASIO_EXPERIMENTAL_DETAIL_CHANNEL_SERVICE_HPP

View File

@@ -0,0 +1,196 @@
//
// experimental/detail/completion_handler_erasure.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2021-2022 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 BOOST_ASIO_EXPERIMENTAL_DETAIL_COMPLETION_HANDLER_ERASURE_HPP
#define BOOST_ASIO_EXPERIMENTAL_DETAIL_COMPLETION_HANDLER_ERASURE_HPP
#include <new>
#include <boost/asio/associated_allocator.hpp>
#include <boost/asio/dispatch.hpp>
namespace boost {
namespace asio {
class any_io_executor;
namespace experimental {
namespace detail {
template<typename Signature, typename Executor>
struct completion_handler_erasure_base;
template<typename Func, typename Signature, typename Executor>
struct completion_handler_erasure_impl;
template<typename Return, typename... Args, typename Executor>
struct completion_handler_erasure_base<Return(Args...), Executor>
{
Executor executor;
completion_handler_erasure_base(Executor&& executor)
: executor(std::move(executor))
{
}
virtual Return call(Args... args) = 0;
virtual void destroy() = 0;
virtual ~completion_handler_erasure_base() = default;
};
template<typename Func, typename Return, typename... Args, typename Executor>
struct completion_handler_erasure_impl<Func, Return(Args...), Executor> final
: completion_handler_erasure_base<Return(Args...), Executor>
{
using allocator_base = typename associated_allocator<Func>::type;
using allocator_type =
typename std::allocator_traits<allocator_base>::template rebind_alloc<
completion_handler_erasure_impl>;
completion_handler_erasure_impl(Executor&& exec, Func&& func)
: completion_handler_erasure_base<Return(Args...), Executor>(
std::move(exec)), func(std::move(func))
{
}
struct uninit_deleter_t
{
allocator_type allocator;
uninit_deleter_t(const Func& func)
: allocator(get_associated_allocator(func))
{
}
void operator()(completion_handler_erasure_impl* p)
{
std::allocator_traits<allocator_type>::deallocate(allocator, p, 1);
}
};
static completion_handler_erasure_impl* make(Executor exec, Func&& func)
{
uninit_deleter_t deleter(func);
std::unique_ptr<completion_handler_erasure_impl, uninit_deleter_t>
uninit_ptr(std::allocator_traits<allocator_type>::allocate(
deleter.allocator, 1), deleter);
completion_handler_erasure_impl* ptr =
new (uninit_ptr.get()) completion_handler_erasure_impl(
std::move(exec), std::move(func));
uninit_ptr.release();
return ptr;
}
struct deleter_t
{
allocator_type allocator;
deleter_t(const Func& func)
: allocator(get_associated_allocator(func))
{
}
void operator()(completion_handler_erasure_impl* p)
{
std::allocator_traits<allocator_type>::destroy(allocator, p);
std::allocator_traits<allocator_type>::deallocate(allocator, p, 1);
}
};
virtual Return call(Args... args) override
{
std::unique_ptr<completion_handler_erasure_impl,
deleter_t> p(this, deleter_t(func));
Func f(std::move(func));
p.reset();
std::move(f)(std::move(args)...);
}
virtual void destroy() override
{
std::unique_ptr<completion_handler_erasure_impl,
deleter_t>(this, deleter_t(func));
}
Func func;
};
template<typename Signature, typename Executor = any_io_executor>
struct completion_handler_erasure;
template<typename Return, typename... Args, typename Executor>
struct completion_handler_erasure<Return(Args...), Executor>
{
struct deleter_t
{
void operator()(
completion_handler_erasure_base<Return(Args...), Executor>* p)
{
p->destroy();
}
};
completion_handler_erasure(const completion_handler_erasure&) = delete;
completion_handler_erasure(completion_handler_erasure&&) = default;
completion_handler_erasure& operator=(
const completion_handler_erasure&) = delete;
completion_handler_erasure& operator=(
completion_handler_erasure&&) = default;
constexpr completion_handler_erasure() = default;
constexpr completion_handler_erasure(nullptr_t)
: completion_handler_erasure()
{
}
template<typename Func>
completion_handler_erasure(Executor exec, Func&& func)
: impl_(completion_handler_erasure_impl<
std::decay_t<Func>, Return(Args...), Executor>::make(
std::move(exec), std::forward<Func>(func)))
{
}
~completion_handler_erasure()
{
if (impl_)
{
Executor executor(impl_->executor);
boost::asio::dispatch(executor,
[impl = std::move(impl_)]() mutable
{
impl.reset();
});
}
}
Return operator()(Args... args)
{
if (impl_)
impl_.release()->call(std::move(args)...);
}
constexpr bool operator==(nullptr_t) const noexcept {return impl_ == nullptr;}
constexpr bool operator!=(nullptr_t) const noexcept {return impl_ != nullptr;}
constexpr bool operator!() const noexcept {return impl_ == nullptr;}
private:
std::unique_ptr<
completion_handler_erasure_base<Return(Args...), Executor>, deleter_t>
impl_;
};
} // namespace detail
} // namespace experimental
} // namespace asio
} // namespace boost
#endif // BOOST_ASIO_EXPERIMENTAL_DETAIL_COMPLETION_HANDLER_ERASURE_HPP

View File

@@ -0,0 +1,120 @@
//
// experimental/detail/coro_promise_allocator.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2021-2022 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 BOOST_ASIO_EXPERIMENTAL_DETAIL_CORO_PROMISE_ALLOCATOR_HPP
#define BOOST_ASIO_EXPERIMENTAL_DETAIL_CORO_PROMISE_ALLOCATOR_HPP
#include <boost/asio/detail/config.hpp>
#include <boost/asio/experimental/coro_traits.hpp>
namespace boost {
namespace asio {
namespace experimental {
namespace detail {
template <typename Coroutine,
typename Executor = typename Coroutine::executor_type,
typename Allocator = typename std::allocator_traits<
associated_allocator_t<Executor>>:: template rebind_alloc<std::byte>,
bool Noexcept = noexcept(std::declval<Allocator>().allocate(0u))>
struct coro_promise_allocator
{
using allocator_type = Allocator;
template <typename... Args>
void* operator new(const std::size_t size, Executor executor, Args&&...)
{
static_assert(std::is_nothrow_move_constructible_v<allocator_type>);
allocator_type alloc{get_associated_allocator(executor)};
const auto csize = size + sizeof(alloc);
const auto raw =
std::allocator_traits<allocator_type>::allocate(alloc, csize);
new (raw) allocator_type(std::move(alloc));
return static_cast<std::byte*>(raw) + sizeof(alloc);
}
template <execution_context Context, typename... Args>
void* operator new(const std::size_t size, Context&& ctx, Args&&... args)
{
return coro_promise_allocator::operator new(size,
ctx.get_executor(), std::forward<Args>(args)...);
}
void operator delete(void* raw, std::size_t size )
{
auto * alloc_p = static_cast<allocator_type*>(raw);
auto alloc = std::move(*alloc_p);
alloc_p->~allocator_type();
std::allocator_traits<allocator_type>::deallocate(alloc,
static_cast<std::byte*>(raw), size + sizeof(allocator_type));
}
};
template <typename Coroutine, typename Executor>
struct coro_promise_allocator<Coroutine,
Executor, std::allocator<std::byte>, false>
{
};
template <typename Coroutine, typename Executor, typename Allocator>
struct coro_promise_allocator<Coroutine, Executor, Allocator, true>
{
using allocator_type = Allocator;
template <typename... Args>
void* operator new(const std::size_t size,
Executor executor, Args&&...) noexcept
{
static_assert(std::is_nothrow_move_constructible_v<allocator_type>);
allocator_type alloc{get_associated_allocator(executor)};
const auto csize = size + sizeof(alloc);
const auto raw =
std::allocator_traits<allocator_type>::allocate(alloc, csize);
if (raw == nullptr)
return nullptr;
new (raw) allocator_type(std::move(alloc));
return static_cast<std::byte*>(raw) + sizeof(alloc);
}
template <execution_context Context, typename... Args>
void* operator new(const std::size_t size,
Context&& ctx, Args&&... args) noexcept
{
return coro_promise_allocator::operator new(size,
ctx.get_executor(), std::forward<Args>(args)...);
}
void operator delete(void* raw, std::size_t size) noexcept
{
auto * alloc_p = static_cast<allocator_type*>(raw);
auto alloc = std::move(*alloc_p);
alloc_p->~allocator_type();
const auto csize = size + sizeof(allocator_type);
std::allocator_traits<allocator_type>::deallocate(alloc,
static_cast<std::byte*>(raw) - sizeof(allocator_type), csize);
}
static auto get_return_object_on_allocation_failure() noexcept -> Coroutine
{
return Coroutine{};
}
};
} // namespace detail
} // namespace experimental
} // namespace asio
} // namespace boost
#endif // BOOST_ASIO_EXPERIMENTAL_DETAIL_CORO_PROMISE_ALLOCATOR_HPP

View File

@@ -0,0 +1,56 @@
//
// experimental/detail/has_signature.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2022 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 BOOST_ASIO_EXPERIMENTAL_DETAIL_HAS_SIGNATURE_HPP
#define BOOST_ASIO_EXPERIMENTAL_DETAIL_HAS_SIGNATURE_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include <boost/asio/detail/config.hpp>
#include <boost/asio/detail/type_traits.hpp>
#include <boost/asio/detail/push_options.hpp>
namespace boost {
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
} // namespace boost
#include <boost/asio/detail/pop_options.hpp>
#endif // BOOST_ASIO_EXPERIMENTAL_DETAIL_HAS_SIGNATURE_HPP

View File

@@ -0,0 +1,613 @@
//
// experimental/detail/impl/channel_service.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2022 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 BOOST_ASIO_EXPERIMENTAL_DETAIL_IMPL_CHANNEL_SERVICE_HPP
#define BOOST_ASIO_EXPERIMENTAL_DETAIL_IMPL_CHANNEL_SERVICE_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include <boost/asio/detail/push_options.hpp>
namespace boost {
namespace asio {
namespace experimental {
namespace detail {
template <typename Mutex>
inline channel_service<Mutex>::channel_service(execution_context& ctx)
: boost::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.
boost::asio::detail::op_queue<channel_operation> ops;
boost::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.
boost::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.
boost::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.
boost::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.
boost::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.
boost::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 BOOST_ASIO_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 BOOST_ASIO_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_);
if (impl.receive_state_ == closed)
impl.receive_state_ = block;
if (impl.send_state_ == closed)
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(
complete_receive<payload_type,
typename traits_type::receive_closed_signature>(
static_cast<channel_receive<payload_type>*>(op)));
}
}
impl.send_state_ = closed;
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(
complete_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_ = 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_);
boost::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(
complete_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_ = block;
}
}
template <typename Mutex>
inline bool channel_service<Mutex>::ready(
const channel_service<Mutex>::base_implementation_type& impl)
const BOOST_ASIO_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,
BOOST_ASIO_MOVE_ARG(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, BOOST_ASIO_MOVE_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, BOOST_ASIO_MOVE_CAST(Args)(args)...));
channel_receive<payload_type>* receive_op =
static_cast<channel_receive<payload_type>*>(impl.waiters_.front());
impl.waiters_.pop();
receive_op->complete(BOOST_ASIO_MOVE_CAST(payload_type)(payload));
if (impl.waiters_.empty())
impl.send_state_ = impl.max_buffer_size_ ? buffer : block;
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, BOOST_ASIO_MOVE_ARG(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, BOOST_ASIO_MOVE_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,
BOOST_ASIO_MOVE_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();
receive_op->complete(payload);
if (impl.waiters_.empty())
impl.send_state_ = impl.max_buffer_size_ ? buffer : block;
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->complete();
break;
}
case waiter:
{
channel_receive<payload_type>* receive_op =
static_cast<channel_receive<payload_type>*>(impl.waiters_.front());
impl.waiters_.pop();
receive_op->complete(send_op->get_payload());
if (impl.waiters_.empty())
impl.send_state_ = impl.max_buffer_size_ ? buffer : block;
send_op->complete();
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,
BOOST_ASIO_MOVE_ARG(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->complete();
}
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();
boost::asio::detail::non_const_lvalue<Handler> handler2(handler);
channel_handler<payload_type, typename decay<Handler>::type>(
BOOST_ASIO_MOVE_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();
send_op->complete();
if (impl.waiters_.front() == 0)
impl.receive_state_ = (impl.send_state_ == closed) ? closed : block;
lock.unlock();
boost::asio::detail::non_const_lvalue<Handler> handler2(handler);
channel_handler<payload_type, typename decay<Handler>::type>(
BOOST_ASIO_MOVE_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:
{
receive_op->complete(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->complete();
}
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;
}
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();
send_op->complete();
receive_op->complete(BOOST_ASIO_MOVE_CAST(payload_type)(payload));
if (impl.waiters_.front() == 0)
impl.receive_state_ = (impl.send_state_ == closed) ? closed : block;
break;
}
case closed:
default:
{
traits_type::invoke_receive_closed(
complete_receive<payload_type,
typename traits_type::receive_closed_signature>(receive_op));
break;
}
}
}
} // namespace detail
} // namespace experimental
} // namespace asio
} // namespace boost
#include <boost/asio/detail/pop_options.hpp>
#endif // BOOST_ASIO_EXPERIMENTAL_DETAIL_IMPL_CHANNEL_SERVICE_HPP

View File

@@ -0,0 +1,180 @@
//
// experimental/detail/partial_promise.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2021-2022 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 BOOST_ASIO_EXPERIMENTAL_DETAIL_PARTIAL_PROMISE_HPP
#define BOOST_ASIO_EXPERIMENTAL_DETAIL_PARTIAL_PROMISE_HPP
#include <boost/asio/detail/config.hpp>
#include <boost/asio/awaitable.hpp>
#include <boost/asio/experimental/coro_traits.hpp>
#if defined(BOOST_ASIO_HAS_STD_COROUTINE)
# include <coroutine>
#else // defined(BOOST_ASIO_HAS_STD_COROUTINE)
# include <experimental/coroutine>
#endif // defined(BOOST_ASIO_HAS_STD_COROUTINE)
namespace boost {
namespace asio {
namespace experimental {
namespace detail {
#if defined(BOOST_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(BOOST_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(BOOST_ASIO_HAS_STD_COROUTINE)
struct partial_promise
{
auto initial_suspend() noexcept
{
return boost::asio::detail::suspend_always{};
}
auto final_suspend() noexcept
{
struct awaitable_t
{
partial_promise *p;
constexpr bool await_ready() noexcept { return true; }
auto await_suspend(boost::asio::detail::coroutine_handle<>) noexcept
{
p->get_return_object().destroy();
}
constexpr void await_resume() noexcept {}
};
return awaitable_t{this};
}
void return_void() {}
coroutine_handle<partial_promise> get_return_object()
{
return coroutine_handle<partial_promise>::from_promise(*this);
}
void unhandled_exception()
{
assert(false);
}
};
} // namespace detail
} // namespace experimental
} // namespace asio
} // namespace boost
#if defined(BOOST_ASIO_HAS_STD_COROUTINE)
namespace std {
template <typename ... Args>
struct coroutine_traits<
coroutine_handle<boost::asio::experimental::detail::partial_promise>,
Args...>
{
using promise_type = boost::asio::experimental::detail::partial_promise;
};
} // namespace std
#else // defined(BOOST_ASIO_HAS_STD_COROUTINE)
namespace std { namespace experimental {
template <typename... Args>
struct coroutine_traits<
coroutine_handle<boost::asio::experimental::detail::partial_promise>,
Args...>
{
using promise_type = boost::asio::experimental::detail::partial_promise;
};
}} // namespace std::experimental
#endif // defined(BOOST_ASIO_HAS_STD_COROUTINE)
namespace boost {
namespace asio {
namespace experimental {
namespace detail {
template <typename CompletionToken>
auto post_coroutine(CompletionToken token) noexcept
-> coroutine_handle<partial_promise>
{
post(std::move(token));
co_return;
}
template <execution::executor Executor, typename CompletionToken>
auto post_coroutine(Executor exec, CompletionToken token) noexcept
-> coroutine_handle<partial_promise>
{
post(exec, std::move(token));
co_return;
}
template <detail::execution_context Context, typename CompletionToken>
auto post_coroutine(Context &ctx, CompletionToken token) noexcept
-> coroutine_handle<partial_promise>
{
post(ctx, std::move(token));
co_return;
}
template <typename CompletionToken>
auto dispatch_coroutine(CompletionToken token) noexcept
-> coroutine_handle<partial_promise>
{
dispatch(std::move(token));
co_return;
}
template <execution::executor Executor, typename CompletionToken>
auto dispatch_coroutine(Executor exec, CompletionToken token) noexcept
-> coroutine_handle<partial_promise>
{
dispatch(exec, std::move(token));
co_return;
}
template <detail::execution_context Context, typename CompletionToken>
auto dispatch_coroutine(Context &ctx, CompletionToken token) noexcept
-> coroutine_handle<partial_promise>
{
dispatch(ctx, std::move(token));
co_return;
}
} // namespace detail
} // namespace experimental
} // namespace asio
} // namespace boost
#endif // BOOST_ASIO_EXPERIMENTAL_DETAIL_PARTIAL_PROMISE_HPP

View File

@@ -0,0 +1,219 @@
//
// experimental/impl/append.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2022 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 BOOST_ASIO_IMPL_EXPERIMENTAL_APPEND_HPP
#define BOOST_ASIO_IMPL_EXPERIMENTAL_APPEND_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include <boost/asio/detail/config.hpp>
#include <boost/asio/associator.hpp>
#include <boost/asio/async_result.hpp>
#include <boost/asio/detail/handler_alloc_helpers.hpp>
#include <boost/asio/detail/handler_cont_helpers.hpp>
#include <boost/asio/detail/handler_invoke_helpers.hpp>
#include <boost/asio/detail/type_traits.hpp>
#include <boost/asio/detail/variadic_templates.hpp>
#include <boost/asio/detail/push_options.hpp>
namespace boost {
namespace asio {
namespace experimental {
namespace detail {
// Class to adapt a append_t as a completion handler.
template <typename Handler, typename... Values>
class append_handler
{
public:
typedef void result_type;
template <typename H>
append_handler(BOOST_ASIO_MOVE_ARG(H) handler, std::tuple<Values...> values)
: handler_(BOOST_ASIO_MOVE_CAST(H)(handler)),
values_(BOOST_ASIO_MOVE_CAST(std::tuple<Values...>)(values))
{
}
template <typename... Args>
void operator()(BOOST_ASIO_MOVE_ARG(Args)... args)
{
this->invoke(
std::make_index_sequence<sizeof...(Values)>{},
BOOST_ASIO_MOVE_CAST(Args)(args)...);
}
template <std::size_t... I, typename... Args>
void invoke(std::index_sequence<I...>, BOOST_ASIO_MOVE_ARG(Args)... args)
{
BOOST_ASIO_MOVE_OR_LVALUE(Handler)(handler_)(
BOOST_ASIO_MOVE_CAST(Args)(args)...,
BOOST_ASIO_MOVE_CAST(Values)(std::get<I>(values_))...);
}
//private:
Handler handler_;
std::tuple<Values...> values_;
};
template <typename Handler>
inline asio_handler_allocate_is_deprecated
asio_handler_allocate(std::size_t size,
append_handler<Handler>* this_handler)
{
#if defined(BOOST_ASIO_NO_DEPRECATED)
boost_asio_handler_alloc_helpers::allocate(size, this_handler->handler_);
return asio_handler_allocate_is_no_longer_used();
#else // defined(BOOST_ASIO_NO_DEPRECATED)
return boost_asio_handler_alloc_helpers::allocate(
size, this_handler->handler_);
#endif // defined(BOOST_ASIO_NO_DEPRECATED)
}
template <typename Handler>
inline asio_handler_deallocate_is_deprecated
asio_handler_deallocate(void* pointer, std::size_t size,
append_handler<Handler>* this_handler)
{
boost_asio_handler_alloc_helpers::deallocate(
pointer, size, this_handler->handler_);
#if defined(BOOST_ASIO_NO_DEPRECATED)
return asio_handler_deallocate_is_no_longer_used();
#endif // defined(BOOST_ASIO_NO_DEPRECATED)
}
template <typename Handler>
inline bool asio_handler_is_continuation(
append_handler<Handler>* this_handler)
{
return boost_asio_handler_cont_helpers::is_continuation(
this_handler->handler_);
}
template <typename Function, typename Handler>
inline asio_handler_invoke_is_deprecated
asio_handler_invoke(Function& function,
append_handler<Handler>* this_handler)
{
boost_asio_handler_invoke_helpers::invoke(
function, this_handler->handler_);
#if defined(BOOST_ASIO_NO_DEPRECATED)
return asio_handler_invoke_is_no_longer_used();
#endif // defined(BOOST_ASIO_NO_DEPRECATED)
}
template <typename Function, typename Handler>
inline asio_handler_invoke_is_deprecated
asio_handler_invoke(const Function& function,
append_handler<Handler>* this_handler)
{
boost_asio_handler_invoke_helpers::invoke(
function, this_handler->handler_);
#if defined(BOOST_ASIO_NO_DEPRECATED)
return asio_handler_invoke_is_no_longer_used();
#endif // defined(BOOST_ASIO_NO_DEPRECATED)
}
template <typename Signature, typename... Values>
struct append_signature;
template <typename R, typename... Args, typename... Values>
struct append_signature<R(Args...), Values...>
{
typedef R type(typename decay<Args>::type..., Values...);
};
} // namespace detail
} // namespace experimental
#if !defined(GENERATING_DOCUMENTATION)
template <typename CompletionToken, typename... Values, typename Signature>
struct async_result<
experimental::append_t<CompletionToken, Values...>, Signature>
: async_result<CompletionToken,
typename experimental::detail::append_signature<
Signature, Values...>::type>
{
typedef typename experimental::detail::append_signature<
Signature, Values...>::type signature;
template <typename Initiation>
struct init_wrapper
{
init_wrapper(Initiation init)
: initiation_(BOOST_ASIO_MOVE_CAST(Initiation)(init))
{
}
template <typename Handler, typename... Args>
void operator()(
BOOST_ASIO_MOVE_ARG(Handler) handler,
std::tuple<Values...> values,
BOOST_ASIO_MOVE_ARG(Args)... args)
{
BOOST_ASIO_MOVE_CAST(Initiation)(initiation_)(
experimental::detail::append_handler<
typename decay<Handler>::type, Values...>(
BOOST_ASIO_MOVE_CAST(Handler)(handler),
BOOST_ASIO_MOVE_CAST(std::tuple<Values...>)(values)),
BOOST_ASIO_MOVE_CAST(Args)(args)...);
}
Initiation initiation_;
};
template <typename Initiation, typename RawCompletionToken, typename... Args>
static BOOST_ASIO_INITFN_DEDUCED_RESULT_TYPE(CompletionToken, signature,
(async_initiate<CompletionToken, signature>(
declval<init_wrapper<typename decay<Initiation>::type> >(),
declval<CompletionToken&>(),
declval<std::tuple<Values...> >(),
declval<BOOST_ASIO_MOVE_ARG(Args)>()...)))
initiate(
BOOST_ASIO_MOVE_ARG(Initiation) initiation,
BOOST_ASIO_MOVE_ARG(RawCompletionToken) token,
BOOST_ASIO_MOVE_ARG(Args)... args)
{
return async_initiate<CompletionToken, signature>(
init_wrapper<typename decay<Initiation>::type>(
BOOST_ASIO_MOVE_CAST(Initiation)(initiation)),
token.token_,
BOOST_ASIO_MOVE_CAST(std::tuple<Values...>)(token.values_),
BOOST_ASIO_MOVE_CAST(Args)(args)...);
}
};
template <template <typename, typename> class Associator,
typename Handler, typename... Values, typename DefaultCandidate>
struct associator<Associator,
experimental::detail::append_handler<Handler, Values...>, DefaultCandidate>
: Associator<Handler, DefaultCandidate>
{
static typename Associator<Handler, DefaultCandidate>::type get(
const experimental::detail::append_handler<Handler, Values...>& h,
const DefaultCandidate& c = DefaultCandidate()) BOOST_ASIO_NOEXCEPT
{
return Associator<Handler, DefaultCandidate>::get(h.handler_, c);
}
};
#endif // !defined(GENERATING_DOCUMENTATION)
} // namespace asio
} // namespace boost
#include <boost/asio/detail/pop_options.hpp>
#endif // BOOST_ASIO_IMPL_EXPERIMENTAL_APPEND_HPP

View File

@@ -0,0 +1,231 @@
//
// experimental/impl/as_single.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2022 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 BOOST_ASIO_IMPL_EXPERIMENTAL_AS_SINGLE_HPP
#define BOOST_ASIO_IMPL_EXPERIMENTAL_AS_SINGLE_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include <boost/asio/detail/config.hpp>
#include <tuple>
#include <boost/asio/associator.hpp>
#include <boost/asio/async_result.hpp>
#include <boost/asio/detail/handler_alloc_helpers.hpp>
#include <boost/asio/detail/handler_cont_helpers.hpp>
#include <boost/asio/detail/handler_invoke_helpers.hpp>
#include <boost/asio/detail/type_traits.hpp>
#include <boost/asio/detail/variadic_templates.hpp>
#include <boost/asio/detail/push_options.hpp>
namespace boost {
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_(BOOST_ASIO_MOVE_CAST(CompletionToken)(e.token_))
{
}
template <typename RedirectedHandler>
as_single_handler(BOOST_ASIO_MOVE_ARG(RedirectedHandler) h)
: handler_(BOOST_ASIO_MOVE_CAST(RedirectedHandler)(h))
{
}
void operator()()
{
BOOST_ASIO_MOVE_OR_LVALUE(Handler)(handler_)();
}
template <typename Arg>
void operator()(BOOST_ASIO_MOVE_ARG(Arg) arg)
{
BOOST_ASIO_MOVE_OR_LVALUE(Handler)(handler_)(
BOOST_ASIO_MOVE_CAST(Arg)(arg));
}
template <typename... Args>
void operator()(BOOST_ASIO_MOVE_ARG(Args)... args)
{
BOOST_ASIO_MOVE_OR_LVALUE(Handler)(handler_)(
std::make_tuple(BOOST_ASIO_MOVE_CAST(Args)(args)...));
}
//private:
Handler handler_;
};
template <typename Handler>
inline asio_handler_allocate_is_deprecated
asio_handler_allocate(std::size_t size,
as_single_handler<Handler>* this_handler)
{
#if defined(BOOST_ASIO_NO_DEPRECATED)
boost_asio_handler_alloc_helpers::allocate(size, this_handler->handler_);
return asio_handler_allocate_is_no_longer_used();
#else // defined(BOOST_ASIO_NO_DEPRECATED)
return boost_asio_handler_alloc_helpers::allocate(
size, this_handler->handler_);
#endif // defined(BOOST_ASIO_NO_DEPRECATED)
}
template <typename Handler>
inline asio_handler_deallocate_is_deprecated
asio_handler_deallocate(void* pointer, std::size_t size,
as_single_handler<Handler>* this_handler)
{
boost_asio_handler_alloc_helpers::deallocate(
pointer, size, this_handler->handler_);
#if defined(BOOST_ASIO_NO_DEPRECATED)
return asio_handler_deallocate_is_no_longer_used();
#endif // defined(BOOST_ASIO_NO_DEPRECATED)
}
template <typename Handler>
inline bool asio_handler_is_continuation(
as_single_handler<Handler>* this_handler)
{
return boost_asio_handler_cont_helpers::is_continuation(
this_handler->handler_);
}
template <typename Function, typename Handler>
inline asio_handler_invoke_is_deprecated
asio_handler_invoke(Function& function,
as_single_handler<Handler>* this_handler)
{
boost_asio_handler_invoke_helpers::invoke(
function, this_handler->handler_);
#if defined(BOOST_ASIO_NO_DEPRECATED)
return asio_handler_invoke_is_no_longer_used();
#endif // defined(BOOST_ASIO_NO_DEPRECATED)
}
template <typename Function, typename Handler>
inline asio_handler_invoke_is_deprecated
asio_handler_invoke(const Function& function,
as_single_handler<Handler>* this_handler)
{
boost_asio_handler_invoke_helpers::invoke(
function, this_handler->handler_);
#if defined(BOOST_ASIO_NO_DEPRECATED)
return asio_handler_invoke_is_no_longer_used();
#endif // defined(BOOST_ASIO_NO_DEPRECATED)
}
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<typename decay<Args>::type...>);
};
} // namespace detail
} // namespace experimental
#if !defined(GENERATING_DOCUMENTATION)
template <typename CompletionToken, typename Signature>
struct async_result<experimental::as_single_t<CompletionToken>, Signature>
{
typedef typename async_result<CompletionToken,
typename experimental::detail::as_single_signature<Signature>::type>
::return_type return_type;
template <typename Initiation>
struct init_wrapper
{
init_wrapper(Initiation init)
: initiation_(BOOST_ASIO_MOVE_CAST(Initiation)(init))
{
}
template <typename Handler, typename... Args>
void operator()(
BOOST_ASIO_MOVE_ARG(Handler) handler,
BOOST_ASIO_MOVE_ARG(Args)... args)
{
BOOST_ASIO_MOVE_CAST(Initiation)(initiation_)(
experimental::detail::as_single_handler<
typename decay<Handler>::type>(
BOOST_ASIO_MOVE_CAST(Handler)(handler)),
BOOST_ASIO_MOVE_CAST(Args)(args)...);
}
Initiation initiation_;
};
template <typename Initiation, typename RawCompletionToken, typename... Args>
static return_type initiate(
BOOST_ASIO_MOVE_ARG(Initiation) initiation,
BOOST_ASIO_MOVE_ARG(RawCompletionToken) token,
BOOST_ASIO_MOVE_ARG(Args)... args)
{
return async_initiate<CompletionToken,
typename experimental::detail::as_single_signature<Signature>::type>(
init_wrapper<typename decay<Initiation>::type>(
BOOST_ASIO_MOVE_CAST(Initiation)(initiation)),
token.token_, BOOST_ASIO_MOVE_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,
const DefaultCandidate& c = DefaultCandidate()) BOOST_ASIO_NOEXCEPT
{
return Associator<Handler, DefaultCandidate>::get(h.handler_, c);
}
};
#endif // !defined(GENERATING_DOCUMENTATION)
} // namespace asio
} // namespace boost
#include <boost/asio/detail/pop_options.hpp>
#endif // BOOST_ASIO_IMPL_EXPERIMENTAL_AS_SINGLE_HPP

View File

@@ -0,0 +1,248 @@
//
// experimental/impl/as_tuple.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2022 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 BOOST_ASIO_IMPL_EXPERIMENTAL_AS_TUPLE_HPP
#define BOOST_ASIO_IMPL_EXPERIMENTAL_AS_TUPLE_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include <boost/asio/detail/config.hpp>
#include <tuple>
#include <boost/asio/associator.hpp>
#include <boost/asio/async_result.hpp>
#include <boost/asio/detail/handler_alloc_helpers.hpp>
#include <boost/asio/detail/handler_cont_helpers.hpp>
#include <boost/asio/detail/handler_invoke_helpers.hpp>
#include <boost/asio/detail/type_traits.hpp>
#include <boost/asio/detail/variadic_templates.hpp>
#include <boost/asio/detail/push_options.hpp>
namespace boost {
namespace asio {
namespace experimental {
namespace detail {
// Class to adapt a as_tuple_t as a completion handler.
template <typename Handler>
class as_tuple_handler
{
public:
typedef void result_type;
template <typename CompletionToken>
as_tuple_handler(as_tuple_t<CompletionToken> e)
: handler_(BOOST_ASIO_MOVE_CAST(CompletionToken)(e.token_))
{
}
template <typename RedirectedHandler>
as_tuple_handler(BOOST_ASIO_MOVE_ARG(RedirectedHandler) h)
: handler_(BOOST_ASIO_MOVE_CAST(RedirectedHandler)(h))
{
}
template <typename... Args>
void operator()(BOOST_ASIO_MOVE_ARG(Args)... args)
{
BOOST_ASIO_MOVE_OR_LVALUE(Handler)(handler_)(
std::make_tuple(BOOST_ASIO_MOVE_CAST(Args)(args)...));
}
//private:
Handler handler_;
};
template <typename Handler>
inline asio_handler_allocate_is_deprecated
asio_handler_allocate(std::size_t size,
as_tuple_handler<Handler>* this_handler)
{
#if defined(BOOST_ASIO_NO_DEPRECATED)
boost_asio_handler_alloc_helpers::allocate(size, this_handler->handler_);
return asio_handler_allocate_is_no_longer_used();
#else // defined(BOOST_ASIO_NO_DEPRECATED)
return boost_asio_handler_alloc_helpers::allocate(
size, this_handler->handler_);
#endif // defined(BOOST_ASIO_NO_DEPRECATED)
}
template <typename Handler>
inline asio_handler_deallocate_is_deprecated
asio_handler_deallocate(void* pointer, std::size_t size,
as_tuple_handler<Handler>* this_handler)
{
boost_asio_handler_alloc_helpers::deallocate(
pointer, size, this_handler->handler_);
#if defined(BOOST_ASIO_NO_DEPRECATED)
return asio_handler_deallocate_is_no_longer_used();
#endif // defined(BOOST_ASIO_NO_DEPRECATED)
}
template <typename Handler>
inline bool asio_handler_is_continuation(
as_tuple_handler<Handler>* this_handler)
{
return boost_asio_handler_cont_helpers::is_continuation(
this_handler->handler_);
}
template <typename Function, typename Handler>
inline asio_handler_invoke_is_deprecated
asio_handler_invoke(Function& function,
as_tuple_handler<Handler>* this_handler)
{
boost_asio_handler_invoke_helpers::invoke(
function, this_handler->handler_);
#if defined(BOOST_ASIO_NO_DEPRECATED)
return asio_handler_invoke_is_no_longer_used();
#endif // defined(BOOST_ASIO_NO_DEPRECATED)
}
template <typename Function, typename Handler>
inline asio_handler_invoke_is_deprecated
asio_handler_invoke(const Function& function,
as_tuple_handler<Handler>* this_handler)
{
boost_asio_handler_invoke_helpers::invoke(
function, this_handler->handler_);
#if defined(BOOST_ASIO_NO_DEPRECATED)
return asio_handler_invoke_is_no_longer_used();
#endif // defined(BOOST_ASIO_NO_DEPRECATED)
}
template <typename Signature>
struct as_tuple_signature;
template <typename R, typename... Args>
struct as_tuple_signature<R(Args...)>
{
typedef R type(std::tuple<typename decay<Args>::type...>);
};
#if defined(BOOST_ASIO_HAS_REF_QUALIFIED_FUNCTIONS)
template <typename R, typename... Args>
struct as_tuple_signature<R(Args...) &>
{
typedef R type(std::tuple<typename decay<Args>::type...>) &;
};
template <typename R, typename... Args>
struct as_tuple_signature<R(Args...) &&>
{
typedef R type(std::tuple<typename decay<Args>::type...>) &&;
};
# if defined(BOOST_ASIO_HAS_NOEXCEPT_FUNCTION_TYPE)
template <typename R, typename... Args>
struct as_tuple_signature<R(Args...) noexcept>
{
typedef R type(std::tuple<typename decay<Args>::type...>) noexcept;
};
template <typename R, typename... Args>
struct as_tuple_signature<R(Args...) & noexcept>
{
typedef R type(std::tuple<typename decay<Args>::type...>) & noexcept;
};
template <typename R, typename... Args>
struct as_tuple_signature<R(Args...) && noexcept>
{
typedef R type(std::tuple<typename decay<Args>::type...>) && noexcept;
};
# endif // defined(BOOST_ASIO_HAS_NOEXCEPT_FUNCTION_TYPE)
#endif // defined(BOOST_ASIO_HAS_REF_QUALIFIED_FUNCTIONS)
} // namespace detail
} // namespace experimental
#if !defined(GENERATING_DOCUMENTATION)
template <typename CompletionToken, typename... Signatures>
struct async_result<experimental::as_tuple_t<CompletionToken>, Signatures...>
: async_result<CompletionToken,
typename experimental::detail::as_tuple_signature<Signatures>::type...>
{
typedef async_result<CompletionToken,
typename experimental::detail::as_tuple_signature<Signatures>::type...>
base_async_result;
template <typename Initiation>
struct init_wrapper
{
init_wrapper(Initiation init)
: initiation_(BOOST_ASIO_MOVE_CAST(Initiation)(init))
{
}
template <typename Handler, typename... Args>
void operator()(
BOOST_ASIO_MOVE_ARG(Handler) handler,
BOOST_ASIO_MOVE_ARG(Args)... args)
{
BOOST_ASIO_MOVE_CAST(Initiation)(initiation_)(
experimental::detail::as_tuple_handler<
typename decay<Handler>::type>(
BOOST_ASIO_MOVE_CAST(Handler)(handler)),
BOOST_ASIO_MOVE_CAST(Args)(args)...);
}
Initiation initiation_;
};
template <typename Initiation, typename RawCompletionToken, typename... Args>
static BOOST_ASIO_INITFN_DEDUCED_RESULT_TYPE(CompletionToken,
typename experimental::detail::as_tuple_signature<Signatures>::type...,
(base_async_result::initiate(
declval<init_wrapper<typename decay<Initiation>::type> >(),
declval<CompletionToken>(),
declval<BOOST_ASIO_MOVE_ARG(Args)>()...)))
initiate(
BOOST_ASIO_MOVE_ARG(Initiation) initiation,
BOOST_ASIO_MOVE_ARG(RawCompletionToken) token,
BOOST_ASIO_MOVE_ARG(Args)... args)
{
return base_async_result::initiate(
init_wrapper<typename decay<Initiation>::type>(
BOOST_ASIO_MOVE_CAST(Initiation)(initiation)),
token.token_, BOOST_ASIO_MOVE_CAST(Args)(args)...);
}
};
template <template <typename, typename> class Associator,
typename Handler, typename DefaultCandidate>
struct associator<Associator,
experimental::detail::as_tuple_handler<Handler>, DefaultCandidate>
: Associator<Handler, DefaultCandidate>
{
static typename Associator<Handler, DefaultCandidate>::type get(
const experimental::detail::as_tuple_handler<Handler>& h,
const DefaultCandidate& c = DefaultCandidate()) BOOST_ASIO_NOEXCEPT
{
return Associator<Handler, DefaultCandidate>::get(h.handler_, c);
}
};
#endif // !defined(GENERATING_DOCUMENTATION)
} // namespace asio
} // namespace boost
#include <boost/asio/detail/pop_options.hpp>
#endif // BOOST_ASIO_IMPL_EXPERIMENTAL_AS_TUPLE_HPP

View File

@@ -0,0 +1,63 @@
//
// experimental/impl/channel_error.ipp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2022 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 BOOST_ASIO_EXPERIMENTAL_IMPL_CHANNEL_ERROR_IPP
#define BOOST_ASIO_EXPERIMENTAL_IMPL_CHANNEL_ERROR_IPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include <boost/asio/detail/config.hpp>
#include <boost/asio/experimental/channel_error.hpp>
#include <boost/asio/detail/push_options.hpp>
namespace boost {
namespace asio {
namespace experimental {
namespace error {
namespace detail {
class channel_category : public boost::system::error_category
{
public:
const char* name() const BOOST_ASIO_ERROR_CATEGORY_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 boost::system::error_category& get_channel_category()
{
static detail::channel_category instance;
return instance;
}
} // namespace error
} // namespace experimental
} // namespace asio
} // namespace boost
#include <boost/asio/detail/pop_options.hpp>
#endif // BOOST_ASIO_EXPERIMENTAL_IMPL_CHANNEL_ERROR_IPP

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,106 @@
//
// experimental/impl/deferred.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2022 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 BOOST_ASIO_EXPERIMENTAL_IMPL_DEFERRED_HPP
#define BOOST_ASIO_EXPERIMENTAL_IMPL_DEFERRED_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include <boost/asio/detail/push_options.hpp>
namespace boost {
namespace asio {
#if !defined(GENERATING_DOCUMENTATION)
template <typename R, typename... Args>
class async_result<
experimental::detail::deferred_signature_probe, R(Args...)>
{
public:
typedef experimental::detail::deferred_signature_probe_result<void(Args...)>
return_type;
template <typename Initiation, typename... InitArgs>
static return_type initiate(
BOOST_ASIO_MOVE_ARG(Initiation),
experimental::detail::deferred_signature_probe,
BOOST_ASIO_MOVE_ARG(InitArgs)...)
{
return return_type{};
}
};
template <typename Signature>
class async_result<experimental::deferred_t, Signature>
{
public:
template <typename Initiation, typename... InitArgs>
static experimental::deferred_async_operation<
Signature, Initiation, InitArgs...>
initiate(BOOST_ASIO_MOVE_ARG(Initiation) initiation,
experimental::deferred_t, BOOST_ASIO_MOVE_ARG(InitArgs)... args)
{
return experimental::deferred_async_operation<
Signature, Initiation, InitArgs...>(
experimental::deferred_init_tag{},
BOOST_ASIO_MOVE_CAST(Initiation)(initiation),
BOOST_ASIO_MOVE_CAST(InitArgs)(args)...);
}
};
template <typename Function, typename R, typename... Args>
class async_result<
experimental::deferred_function<Function>, R(Args...)>
{
public:
template <typename Initiation, typename... InitArgs>
static auto initiate(BOOST_ASIO_MOVE_ARG(Initiation) initiation,
experimental::deferred_function<Function> token,
BOOST_ASIO_MOVE_ARG(InitArgs)... init_args)
{
return experimental::deferred_sequence<
experimental::deferred_async_operation<
R(Args...), Initiation, InitArgs...>,
Function>(experimental::deferred_init_tag{},
experimental::deferred_async_operation<
R(Args...), Initiation, InitArgs...>(
experimental::deferred_init_tag{},
BOOST_ASIO_MOVE_CAST(Initiation)(initiation),
BOOST_ASIO_MOVE_CAST(InitArgs)(init_args)...),
BOOST_ASIO_MOVE_CAST(Function)(token.function_));
}
};
template <template <typename, typename> class Associator,
typename Handler, typename Tail, typename DefaultCandidate>
struct associator<Associator,
experimental::detail::deferred_sequence_handler<Handler, Tail>,
DefaultCandidate>
: Associator<Handler, DefaultCandidate>
{
static typename Associator<Handler, DefaultCandidate>::type get(
const experimental::detail::deferred_sequence_handler<Handler, Tail>& h,
const DefaultCandidate& c = DefaultCandidate()) BOOST_ASIO_NOEXCEPT
{
return Associator<Handler, DefaultCandidate>::get(h.handler_, c);
}
};
#endif // !defined(GENERATING_DOCUMENTATION)
} // namespace asio
} // namespace boost
#include <boost/asio/detail/pop_options.hpp>
#endif // BOOST_ASIO_EXPERIMENTAL_IMPL_DEFERRED_HPP

View File

@@ -0,0 +1,435 @@
//
// experimental/impl/parallel_group.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2022 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 BOOST_ASIO_IMPL_EXPERIMENTAL_PARALLEL_GROUP_HPP
#define BOOST_ASIO_IMPL_EXPERIMENTAL_PARALLEL_GROUP_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include <boost/asio/detail/config.hpp>
#include <atomic>
#include <memory>
#include <new>
#include <tuple>
#include <boost/asio/associated_cancellation_slot.hpp>
#include <boost/asio/detail/recycling_allocator.hpp>
#include <boost/asio/detail/type_traits.hpp>
#include <boost/asio/dispatch.hpp>
#include <boost/asio/detail/push_options.hpp>
namespace boost {
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 operatations. 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 typename decay<
typename prefer_result<
typename associated_executor<Handler>::type,
execution::outstanding_work_t::tracked_t
>::type
>::type executor_type;
parallel_group_completion_handler(Handler&& h)
: handler_(std::move(h)),
executor_(
boost::asio::prefer(
boost::asio::get_associated_executor(handler_),
execution::outstanding_work.tracked))
{
}
executor_type get_executor() const noexcept
{
return executor_;
}
void operator()()
{
this->invoke(std::make_index_sequence<sizeof...(Ops)>());
}
template <std::size_t... I>
void invoke(std::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), std::make_index_sequence<sizeof...(Args)>());
}
template <typename... Args, std::size_t... I>
void invoke(std::tuple<Args...>&& args, std::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<
typename parallel_op_signature<Ops>::type
>::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.
boost::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 boost::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)
boost::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 boost::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())
{
boost::asio::cancellation_signal* sig = &signal_;
boost::asio::dispatch(executor_,
[state, sig, type]{ sig->emit(type); });
}
}
std::weak_ptr<parallel_group_state<Condition, Handler, Ops...> > state_;
boost::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 typename associated_executor<Op>::type ex_type;
ex_type ex = boost::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,
typename enable_if<
is_same<
typename associated_executor<
Op>::asio_associated_executor_is_unspecialised,
void
>::value
>::type>
{
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, std::index_sequence<I...>)
{
// Get the user's completion handler's cancellation slot, so that we can allow
// cancellation of the entire group.
typename associated_cancellation_slot<Handler>::type slot
= boost::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>(
boost::asio::detail::recycling_allocator<state_type,
boost::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);
}
} // namespace detail
} // namespace experimental
template <typename R, typename... Args>
class async_result<
experimental::detail::parallel_op_signature_probe,
R(Args...)>
{
public:
typedef experimental::detail::parallel_op_signature_probe_result<
void(Args...)> return_type;
template <typename Initiation, typename... InitArgs>
static return_type initiate(Initiation&&,
experimental::detail::parallel_op_signature_probe, InitArgs&&...)
{
return return_type{};
}
};
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,
const DefaultCandidate& c = DefaultCandidate()) BOOST_ASIO_NOEXCEPT
{
return Associator<Handler, DefaultCandidate>::get(h.handler_, c);
}
};
} // namespace asio
} // namespace boost
#include <boost/asio/detail/pop_options.hpp>
#endif // BOOST_ASIO_IMPL_EXPERIMENTAL_PARALLEL_GROUP_HPP

View File

@@ -0,0 +1,219 @@
//
// experimental/impl/prepend.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2022 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 BOOST_ASIO_IMPL_EXPERIMENTAL_PREPEND_HPP
#define BOOST_ASIO_IMPL_EXPERIMENTAL_PREPEND_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include <boost/asio/detail/config.hpp>
#include <boost/asio/associator.hpp>
#include <boost/asio/async_result.hpp>
#include <boost/asio/detail/handler_alloc_helpers.hpp>
#include <boost/asio/detail/handler_cont_helpers.hpp>
#include <boost/asio/detail/handler_invoke_helpers.hpp>
#include <boost/asio/detail/type_traits.hpp>
#include <boost/asio/detail/variadic_templates.hpp>
#include <boost/asio/detail/push_options.hpp>
namespace boost {
namespace asio {
namespace experimental {
namespace detail {
// Class to adapt a prepend_t as a completion handler.
template <typename Handler, typename... Values>
class prepend_handler
{
public:
typedef void result_type;
template <typename H>
prepend_handler(BOOST_ASIO_MOVE_ARG(H) handler, std::tuple<Values...> values)
: handler_(BOOST_ASIO_MOVE_CAST(H)(handler)),
values_(BOOST_ASIO_MOVE_CAST(std::tuple<Values...>)(values))
{
}
template <typename... Args>
void operator()(BOOST_ASIO_MOVE_ARG(Args)... args)
{
this->invoke(
std::make_index_sequence<sizeof...(Values)>{},
BOOST_ASIO_MOVE_CAST(Args)(args)...);
}
template <std::size_t... I, typename... Args>
void invoke(std::index_sequence<I...>, BOOST_ASIO_MOVE_ARG(Args)... args)
{
BOOST_ASIO_MOVE_OR_LVALUE(Handler)(handler_)(
BOOST_ASIO_MOVE_CAST(Values)(std::get<I>(values_))...,
BOOST_ASIO_MOVE_CAST(Args)(args)...);
}
//private:
Handler handler_;
std::tuple<Values...> values_;
};
template <typename Handler>
inline asio_handler_allocate_is_deprecated
asio_handler_allocate(std::size_t size,
prepend_handler<Handler>* this_handler)
{
#if defined(BOOST_ASIO_NO_DEPRECATED)
boost_asio_handler_alloc_helpers::allocate(size, this_handler->handler_);
return asio_handler_allocate_is_no_longer_used();
#else // defined(BOOST_ASIO_NO_DEPRECATED)
return boost_asio_handler_alloc_helpers::allocate(
size, this_handler->handler_);
#endif // defined(BOOST_ASIO_NO_DEPRECATED)
}
template <typename Handler>
inline asio_handler_deallocate_is_deprecated
asio_handler_deallocate(void* pointer, std::size_t size,
prepend_handler<Handler>* this_handler)
{
boost_asio_handler_alloc_helpers::deallocate(
pointer, size, this_handler->handler_);
#if defined(BOOST_ASIO_NO_DEPRECATED)
return asio_handler_deallocate_is_no_longer_used();
#endif // defined(BOOST_ASIO_NO_DEPRECATED)
}
template <typename Handler>
inline bool asio_handler_is_continuation(
prepend_handler<Handler>* this_handler)
{
return boost_asio_handler_cont_helpers::is_continuation(
this_handler->handler_);
}
template <typename Function, typename Handler>
inline asio_handler_invoke_is_deprecated
asio_handler_invoke(Function& function,
prepend_handler<Handler>* this_handler)
{
boost_asio_handler_invoke_helpers::invoke(
function, this_handler->handler_);
#if defined(BOOST_ASIO_NO_DEPRECATED)
return asio_handler_invoke_is_no_longer_used();
#endif // defined(BOOST_ASIO_NO_DEPRECATED)
}
template <typename Function, typename Handler>
inline asio_handler_invoke_is_deprecated
asio_handler_invoke(const Function& function,
prepend_handler<Handler>* this_handler)
{
boost_asio_handler_invoke_helpers::invoke(
function, this_handler->handler_);
#if defined(BOOST_ASIO_NO_DEPRECATED)
return asio_handler_invoke_is_no_longer_used();
#endif // defined(BOOST_ASIO_NO_DEPRECATED)
}
template <typename Signature, typename... Values>
struct prepend_signature;
template <typename R, typename... Args, typename... Values>
struct prepend_signature<R(Args...), Values...>
{
typedef R type(Values..., typename decay<Args>::type...);
};
} // namespace detail
} // namespace experimental
#if !defined(GENERATING_DOCUMENTATION)
template <typename CompletionToken, typename... Values, typename Signature>
struct async_result<
experimental::prepend_t<CompletionToken, Values...>, Signature>
: async_result<CompletionToken,
typename experimental::detail::prepend_signature<
Signature, Values...>::type>
{
typedef typename experimental::detail::prepend_signature<
Signature, Values...>::type signature;
template <typename Initiation>
struct init_wrapper
{
init_wrapper(Initiation init)
: initiation_(BOOST_ASIO_MOVE_CAST(Initiation)(init))
{
}
template <typename Handler, typename... Args>
void operator()(
BOOST_ASIO_MOVE_ARG(Handler) handler,
std::tuple<Values...> values,
BOOST_ASIO_MOVE_ARG(Args)... args)
{
BOOST_ASIO_MOVE_CAST(Initiation)(initiation_)(
experimental::detail::prepend_handler<
typename decay<Handler>::type, Values...>(
BOOST_ASIO_MOVE_CAST(Handler)(handler),
BOOST_ASIO_MOVE_CAST(std::tuple<Values...>)(values)),
BOOST_ASIO_MOVE_CAST(Args)(args)...);
}
Initiation initiation_;
};
template <typename Initiation, typename RawCompletionToken, typename... Args>
static BOOST_ASIO_INITFN_DEDUCED_RESULT_TYPE(CompletionToken, signature,
(async_initiate<CompletionToken, signature>(
declval<init_wrapper<typename decay<Initiation>::type> >(),
declval<CompletionToken&>(),
declval<std::tuple<Values...> >(),
declval<BOOST_ASIO_MOVE_ARG(Args)>()...)))
initiate(
BOOST_ASIO_MOVE_ARG(Initiation) initiation,
BOOST_ASIO_MOVE_ARG(RawCompletionToken) token,
BOOST_ASIO_MOVE_ARG(Args)... args)
{
return async_initiate<CompletionToken, signature>(
init_wrapper<typename decay<Initiation>::type>(
BOOST_ASIO_MOVE_CAST(Initiation)(initiation)),
token.token_,
BOOST_ASIO_MOVE_CAST(std::tuple<Values...>)(token.values_),
BOOST_ASIO_MOVE_CAST(Args)(args)...);
}
};
template <template <typename, typename> class Associator,
typename Handler, typename... Values, typename DefaultCandidate>
struct associator<Associator,
experimental::detail::prepend_handler<Handler, Values...>, DefaultCandidate>
: Associator<Handler, DefaultCandidate>
{
static typename Associator<Handler, DefaultCandidate>::type get(
const experimental::detail::prepend_handler<Handler, Values...>& h,
const DefaultCandidate& c = DefaultCandidate()) BOOST_ASIO_NOEXCEPT
{
return Associator<Handler, DefaultCandidate>::get(h.handler_, c);
}
};
#endif // !defined(GENERATING_DOCUMENTATION)
} // namespace asio
} // namespace boost
#include <boost/asio/detail/pop_options.hpp>
#endif // BOOST_ASIO_IMPL_EXPERIMENTAL_PREPEND_HPP

View File

@@ -0,0 +1,104 @@
//
// experimental/impl/promise.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2021-2022 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 BOOST_ASIO_EXPERIMENTAL_IMPL_PROMISE_HPP
#define BOOST_ASIO_EXPERIMENTAL_IMPL_PROMISE_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include <boost/asio/cancellation_signal.hpp>
#include <boost/asio/experimental/detail/completion_handler_erasure.hpp>
#include <tuple>
#include <optional>
#include <boost/asio/detail/push_options.hpp>
namespace boost {
namespace asio {
namespace experimental {
template<typename Signature = void(), typename Executor = any_io_executor>
struct promise;
namespace detail {
template<typename Signature, typename Executor>
struct promise_impl;
template<typename ... Ts, typename Executor>
struct promise_impl<void(Ts...), Executor>
{
using result_type = std::tuple<Ts...>;
promise_impl(Executor executor = {})
: executor(std::move(executor))
{
}
std::optional<result_type> result;
bool done{false};
detail::completion_handler_erasure<void(Ts...), Executor> completion;
cancellation_signal cancel;
Executor executor;
};
template<typename Signature = void(), typename Executor = any_io_executor>
struct promise_handler;
template<typename Signature, typename Executor>
struct promise_handler;
template<typename ... Ts, typename Executor>
struct promise_handler<void(Ts...), Executor>
{
using promise_type = promise<void(Ts...), Executor>;
promise_handler(Executor executor) // get_associated_allocator(exec)
: impl_{
std::allocate_shared<promise_impl<void(Ts...), Executor>>(
get_associated_allocator(executor))}
{
impl_->executor = std::move(executor);
}
std::shared_ptr<promise_impl<void(Ts...), Executor>> impl_;
using cancellation_slot_type = cancellation_slot;
cancellation_slot_type get_cancellation_slot() const noexcept
{
return impl_->cancel.slot();
}
auto make_promise() -> promise<void(Ts...), Executor>
{
return {impl_};
}
void operator()(std::remove_reference_t<Ts>... ts)
{
assert(impl_);
impl_->result.emplace(std::move(ts)...);
impl_->done = true;
if (auto f = std::exchange(impl_->completion, nullptr); f != nullptr)
std::apply(std::move(f), std::move(*impl_->result));
}
};
} // namespace detail
} // namespace experimental
} // namespace asio
} // namespace boost
#include <boost/asio/detail/pop_options.hpp>
#endif // BOOST_ASIO_EXPERIMENTAL_IMPL_PROMISE_HPP

View File

@@ -0,0 +1,270 @@
//
// experimental/impl/use_coro.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2021-2022 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 BOOST_ASIO_EXPERIMENTAL_IMPL_USE_CORO_HPP
#define BOOST_ASIO_EXPERIMENTAL_IMPL_USE_CORO_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include <boost/asio/detail/push_options.hpp>
namespace boost {
namespace asio {
namespace experimental {
template <typename Yield, typename Return, typename Executor>
struct coro;
namespace detail {
template <typename Yield, typename Return, typename Executor>
struct coro_promise;
template <typename Executor, typename... Ts>
struct coro_init_handler
{
struct handler_t
{
};
constexpr static handler_t handler{};
struct init_helper;
struct promise_type
{
auto initial_suspend() noexcept { return suspend_always{}; }
auto final_suspend() noexcept { return suspend_always(); }
void return_void() {}
void unhandled_exception() { assert(false); }
auto await_transform(handler_t)
{
assert(executor);
assert(h);
return init_helper{this};
}
std::optional<Executor> executor;
std::optional<std::tuple<Ts...>> result;
coroutine_handle<> h;
coro_init_handler get_return_object() { return coro_init_handler{this}; }
cancellation_slot cancel_slot;
};
struct init_helper
{
promise_type *self_;
constexpr static bool await_ready() noexcept { return true; }
constexpr static void await_suspend(coroutine_handle<>) noexcept {}
auto await_resume() const noexcept
{
assert(self_);
return bind_cancellation_slot(self_->cancel_slot,
bind_executor(*self_->executor, [self = self_](Ts... ts)
{
self->cancel_slot.clear();
self->result.emplace(std::move(ts)...);
self->h.resume();
}));
}
};
promise_type* promise;
void unhandled_exception() noexcept
{
throw;
}
struct noexcept_version
{
promise_type *promise;
constexpr static bool await_ready() noexcept { return false; }
template <typename Yield, typename Return,
convertible_to<Executor> Executor1>
auto await_suspend(
coroutine_handle<coro_promise<Yield, Return, Executor1> > h) noexcept
{
promise->executor = h.promise().get_executor();
promise->h = h;
return coroutine_handle<promise_type>::from_promise(*promise);
}
template <typename... Args>
static auto resume_impl(std::tuple<Args...>&& tup)
{
return std::move(tup);
}
template <typename Arg>
static auto resume_impl(std::tuple<Arg>&& tup)
{
return get<0>(std::move(tup));
}
static void resume_impl(std::tuple<>&&) {}
auto await_resume() const noexcept
{
auto res = std::move(promise->result.value());
coroutine_handle<promise_type>::from_promise(*promise).destroy();
return resume_impl(std::move(res));
}
};
struct throwing_version
{
promise_type *promise;
constexpr static bool await_ready() noexcept { return false; }
template <typename Yield, typename Return,
convertible_to<Executor> Executor1>
auto await_suspend(
coroutine_handle<coro_promise<Yield, Return, Executor1> > h) noexcept
{
promise->executor = h.promise().get_executor();
promise->h = h;
return coroutine_handle<promise_type>::from_promise(*promise);
}
template <typename... Args>
static auto resume_impl(std::tuple<Args...>&& tup)
{
return std::move(tup);
}
static void resume_impl(std::tuple<>&&) {}
template <typename Arg>
static auto resume_impl(std::tuple<Arg>&& tup)
{
return get<0>(std::move(tup));
}
template <typename... Args>
static auto resume_impl(std::tuple<std::exception_ptr, Args...>&& tup)
{
auto ex = get<0>(std::move(tup));
if (ex)
std::rethrow_exception(ex);
if constexpr (sizeof...(Args) == 0u)
return;
else if constexpr (sizeof...(Args) == 1u)
return get<1>(std::move(tup));
else
{
return
[&]<std::size_t... Idx>(std::index_sequence<Idx...>)
{
return std::make_tuple(std::get<Idx + 1>(std::move(tup))...);
}(std::make_index_sequence<sizeof...(Args) - 1>{});
}
}
template <typename... Args>
static auto resume_impl(
std::tuple<boost::system::error_code, Args...>&& tup)
{
auto ec = get<0>(std::move(tup));
if (ec)
boost::asio::detail::throw_exception(
boost::system::system_error(ec, "error_code in use_coro"));
if constexpr (sizeof...(Args) == 0u)
return;
else if constexpr (sizeof...(Args) == 1u)
return get<1>(std::move(tup));
else
return
[&]<std::size_t... Idx>(std::index_sequence<Idx...>)
{
return std::make_tuple(std::get<Idx + 1>(std::move(tup))...);
}(std::make_index_sequence<sizeof...(Args) - 1>{});
}
static auto resume_impl(std::tuple<std::exception_ptr>&& tup)
{
auto ex = get<0>(std::move(tup));
if (ex)
std::rethrow_exception(ex);
}
static auto resume_impl(
std::tuple<boost::system::error_code>&& tup)
{
auto ec = get<0>(std::move(tup));
if (ec)
boost::asio::detail::throw_error(ec, "error_code in use_coro");
}
auto await_resume() const
{
auto res = std::move(promise->result.value());
coroutine_handle<promise_type>::from_promise(*promise).destroy();
return resume_impl(std::move(res));
}
};
auto as_noexcept(cancellation_slot&& sl) && noexcept
{
promise->cancel_slot = std::move(sl);
return noexcept_version{promise};
}
auto as_throwing(cancellation_slot&& sl) && noexcept
{
promise->cancel_slot = std::move(sl);
return throwing_version{promise};
}
};
} // namespace detail
} // namespace experimental
#if !defined(GENERATING_DOCUMENTATION)
template <typename Executor, typename R, typename... Args>
struct async_result<experimental::use_coro_t<Executor>, R(Args...)>
{
using return_type = experimental::detail::coro_init_handler<
Executor, typename decay<Args>::type...>;
template <typename Initiation, typename... InitArgs>
static return_type initiate(Initiation initiation,
experimental::use_coro_t<Executor>, InitArgs... args)
{
std::move(initiation)(co_await return_type::handler, std::move(args)...);
}
};
#endif // !defined(GENERATING_DOCUMENTATION)
} // namespace asio
} // namespace boost
#include <boost/asio/detail/pop_options.hpp>
#endif // BOOST_ASIO_EXPERIMENTAL_IMPL_USE_CORO_HPP

View File

@@ -0,0 +1,218 @@
//
// experimental/parallel_group.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2022 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 BOOST_ASIO_EXPERIMENTAL_PARALLEL_GROUP_HPP
#define BOOST_ASIO_EXPERIMENTAL_PARALLEL_GROUP_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include <boost/asio/detail/config.hpp>
#include <utility>
#include <boost/asio/detail/array.hpp>
#include <boost/asio/experimental/cancellation_condition.hpp>
#include <boost/asio/detail/push_options.hpp>
namespace boost {
namespace asio {
namespace experimental {
namespace detail {
// Helper trait for getting the completion signature from an async operation.
struct parallel_op_signature_probe {};
template <typename T>
struct parallel_op_signature_probe_result
{
typedef T type;
};
template <typename Op>
struct parallel_op_signature
{
typedef typename decltype(declval<Op>()(
declval<parallel_op_signature_probe>()))::type type;
};
// 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<typename decay<Args>::type...> 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 boost::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 boost::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 boost::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, std::index_sequence<I...>);
} // namespace detail
/// A group of asynchronous operations that may be launched in parallel.
/**
* See the documentation for boost::asio::experimental::make_parallel_group for
* a usage example.
*/
template <typename... Ops>
class parallel_group
{
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),
typename detail::parallel_op_signature<Ops>::type...>::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 boost::asio::cancellation_type value other
* than <tt>boost::asio::cancellation_type::none</tt>.
*
* @param token A 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 boost::asio::experimental::wait_for_all
* @li boost::asio::experimental::wait_for_one
* @li boost::asio::experimental::wait_for_one_error
* @li boost::asio::experimental::wait_for_one_success
*/
template <typename CancellationCondition,
BOOST_ASIO_COMPLETION_TOKEN_FOR(signature) CompletionToken>
auto async_wait(CancellationCondition cancellation_condition,
CompletionToken&& token)
{
return boost::asio::async_initiate<CompletionToken, signature>(
initiate_async_wait(), token,
std::move(cancellation_condition), std::move(ops_));
}
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::move(c), std::move(h),
ops, std::make_index_sequence<sizeof...(Ops)>());
}
};
std::tuple<Ops...> ops_;
};
/// Create a group of operations that may be launched in parallel.
/**
* For example:
* @code boost::asio::experimental::make_parallel_group(
* [&](auto token)
* {
* return in.async_read_some(boost::asio::buffer(data), token);
* },
* [&](auto token)
* {
* return timer.async_wait(token);
* }
* ).async_wait(
* boost::asio::experimental::wait_for_all(),
* [](
* std::array<std::size_t, 2> completion_order,
* boost::system::error_code ec1, std::size_t n1,
* boost::system::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>
BOOST_ASIO_NODISCARD inline parallel_group<Ops...>
make_parallel_group(Ops... ops)
{
return parallel_group<Ops...>(std::move(ops)...);
}
} // namespace experimental
} // namespace asio
} // namespace boost
#include <boost/asio/detail/pop_options.hpp>
#include <boost/asio/experimental/impl/parallel_group.hpp>
#endif // BOOST_ASIO_EXPERIMENTAL_PARALLEL_GROUP_HPP

View File

@@ -0,0 +1,73 @@
//
// experimental/prepend.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2022 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 BOOST_ASIO_EXPERIMENTAL_PREPEND_HPP
#define BOOST_ASIO_EXPERIMENTAL_PREPEND_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include <boost/asio/detail/config.hpp>
#include <tuple>
#include <boost/asio/detail/type_traits.hpp>
#include <boost/asio/detail/push_options.hpp>
namespace boost {
namespace asio {
namespace experimental {
/// Completion token type used to specify that the completion handler
/// arguments should be passed additional values before the results of the
/// operation.
template <typename CompletionToken, typename... Values>
class prepend_t
{
public:
/// Constructor.
template <typename T, typename... V>
BOOST_ASIO_CONSTEXPR explicit prepend_t(
BOOST_ASIO_MOVE_ARG(T) completion_token,
BOOST_ASIO_MOVE_ARG(V)... values)
: token_(BOOST_ASIO_MOVE_CAST(T)(completion_token)),
values_(BOOST_ASIO_MOVE_CAST(V)(values)...)
{
}
//private:
CompletionToken token_;
std::tuple<Values...> values_;
};
/// Completion token type used to specify that the completion handler
/// arguments should be passed additional values before the results of the
/// operation.
template <typename CompletionToken, typename... Values>
BOOST_ASIO_NODISCARD inline BOOST_ASIO_CONSTEXPR prepend_t<
typename decay<CompletionToken>::type, typename decay<Values>::type...>
prepend(BOOST_ASIO_MOVE_ARG(CompletionToken) completion_token,
BOOST_ASIO_MOVE_ARG(Values)... values)
{
return prepend_t<
typename decay<CompletionToken>::type, typename decay<Values>::type...>(
BOOST_ASIO_MOVE_CAST(CompletionToken)(completion_token),
BOOST_ASIO_MOVE_CAST(Values)(values)...);
}
} // namespace experimental
} // namespace asio
} // namespace boost
#include <boost/asio/detail/pop_options.hpp>
#include <boost/asio/experimental/impl/prepend.hpp>
#endif // BOOST_ASIO_EXPERIMENTAL_PREPEND_HPP

View File

@@ -0,0 +1,613 @@
//
// experimental/promise.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2021-2022 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 BOOST_ASIO_EXPERIMENTAL_PROMISE_HPP
#define BOOST_ASIO_EXPERIMENTAL_PROMISE_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include <boost/asio/detail/config.hpp>
#include <boost/asio/detail/type_traits.hpp>
#include <boost/asio/any_io_executor.hpp>
#include <boost/asio/associated_cancellation_slot.hpp>
#include <boost/asio/bind_executor.hpp>
#include <boost/asio/cancellation_signal.hpp>
#include <boost/asio/experimental/detail/completion_handler_erasure.hpp>
#include <boost/asio/experimental/impl/promise.hpp>
#include <boost/asio/post.hpp>
#include <algorithm>
#include <variant>
#include <boost/asio/detail/push_options.hpp>
namespace boost {
namespace asio {
namespace experimental {
template <typename Executor = any_io_executor>
struct use_promise_t {};
constexpr use_promise_t<> use_promise;
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 T>
concept is_promise_c = is_promise_v<std::remove_reference_t<T>>;
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::monostate;
};
#if defined(GENERATING_DOCUMENTATION)
/// The primary template is not defined.
template<typename Signature = void(), typename Executor = any_io_executor>
struct promise
{
};
#endif // defined(GENERATING_DOCUMENTATION)
template <typename ... Ts, typename Executor>
struct promise<void(Ts...), Executor>
{
using value_type = typename promise_value_type<Ts...>::type;
using tuple_type = std::tuple<Ts...>;
using executor_type = Executor;
executor_type get_executor() const
{
if (impl_)
return impl_->executor;
else
return {};
}
void cancel(cancellation_type level = cancellation_type::all)
{
if (impl_ && !impl_->done)
{
boost::asio::dispatch(impl_->executor,
[level, impl = impl_]{impl->cancel.emit(level);});
}
}
bool complete() const noexcept
{
return impl_ && impl_->done;
}
template <typename CompletionToken>
auto async_wait(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;
~promise() { cancel(); }
template <execution::executor Executor1, is_promise_c ... Ps>
static auto race(Executor1 exec, Ps ... ps)
-> promise<void(std::variant<typename Ps::value_type...>), Executor1>
{
using var_t = std::variant<typename Ps::value_type...>;
using pi = detail::promise_impl<void(var_t), Executor1>;
struct impl_t : pi
{
impl_t(Executor1 exec, Ps&& ... ps)
: pi(std::move(exec)),
tup(std::move(ps)...)
{
this->slot.template emplace<cancel_handler>(this);
}
struct cancel_handler
{
impl_t* self;
cancel_handler(impl_t* self)
: self(self)
{
}
void operator()(cancellation_type ct)
{
[ct, s=self]<std::size_t... Idx>(std::index_sequence<Idx...>)
{
(std::get<Idx>(s->tup).cancel(ct), ... );
}(std::make_index_sequence<sizeof...(Ps)>{});
}
};
std::tuple<std::remove_reference_t<Ps>...> tup;
cancellation_slot slot{this->cancel.slot()};
};
auto impl = std::allocate_shared<impl_t>(
get_associated_allocator(exec), exec, std::move(ps)...);
impl->executor = exec;
[impl, exec]<std::size_t... Idx>(std::index_sequence<Idx...>)
{
auto step =
[&]<std::size_t I>(std::integral_constant<std::size_t, I>)
{
return [impl]<typename... Args > (Args&& ... args)
{
if (impl->done)
return;
impl->result = var_t(std::in_place_index<I>,
std::forward<Args>(args)...);
impl->done = true;
if (auto f = std::exchange(impl->completion, nullptr); !!f)
std::apply(std::move(f), std::move(*impl->result));
auto cancel =
[&]<std::size_t Id>(std::integral_constant<std::size_t, Id>)
{
if constexpr (I != Id)
get<I>(impl->tup).cancel();
};
(cancel(std::integral_constant<std::size_t, Idx>{}), ...);
};
};
(
std::get<Idx>(impl->tup).async_wait(
bind_executor(exec,
step(std::integral_constant<std::size_t, Idx>{}))),
...
);
}(std::make_index_sequence<sizeof...(Ps)>{});
return {impl};
}
template <execution::executor Executor1, is_promise_c ... Ps>
static auto all(Executor1 exec, Ps ... ps)
-> promise<void(typename Ps::value_type...), Executor1>
{
using pi = detail::promise_impl<
void(typename Ps::value_type...), Executor1>;
struct impl_t : pi
{
impl_t(Executor1 exec, Ps&& ... ps)
: pi(std::move(exec)),
tup(std::move(ps)...)
{
this->slot.template emplace<cancel_handler>(this);
}
struct cancel_handler
{
impl_t* self;
cancel_handler(impl_t* self)
: self(self)
{
}
void operator()(cancellation_type level)
{
[level, s=self]<std::size_t... Idx>(std::index_sequence<Idx...>)
{
(std::get<Idx>(s->tup).cancel(level), ... );
}(std::make_index_sequence<sizeof...(Ps)>{});
}
};
std::tuple<std::remove_reference_t<Ps>...> tup;
std::tuple<std::optional<typename Ps::value_type>...> partial_result;
cancellation_slot slot{this->cancel.slot()};
};
auto impl = std::allocate_shared<impl_t>(
get_associated_allocator(exec), exec, std::move(ps)...);
impl->executor = exec;
[impl, exec]<std::size_t... Idx>(std::index_sequence<Idx...>)
{
auto step =
[&]<std::size_t I>(std::integral_constant<std::size_t, I>)
{
return [impl]<typename... Args>(Args&& ... args)
{
std::get<I>(impl->partial_result).emplace(
std::forward<Args>(args)...);
if ((std::get<Idx>(impl->partial_result) && ...)) // we're done.
{
impl->result = {*std::get<Idx>(impl->partial_result)...};
impl->done = true;
if (auto f = std::exchange(impl->completion, nullptr); !!f)
std::apply(std::move(f), std::move(*impl->result));
}
};
};
(
std::get<Idx>(impl->tup).async_wait(
bind_executor(exec,
step(std::integral_constant<std::size_t, Idx>{}))),
...
);
}(std::make_index_sequence<sizeof...(Ps)>{});
return {impl};
}
template <is_promise_c ... Ps>
static auto race(Ps ... ps)
{
auto exec = get<0>(std::tie(ps...)).get_executor();
return race(std::move(exec), std::move(ps)...);
}
template <is_promise_c ... Ps>
static auto all(Ps ... ps)
{
auto exec = get<0>(std::tie(ps...)).get_executor();
return all(std::move(exec), std::move(ps)...);
}
template <execution::executor Executor1, typename Range>
#if !defined(GENERATING_DOCUMENTATION)
requires requires (Range r)
{
{*std::begin(r)} -> is_promise_c;
{*std:: end(r)} -> is_promise_c;
}
#endif // !defined(GENERATING_DOCUMENTATION)
static auto race(Executor1 exec, Range range)
{
using var_t = typename std::decay_t<
decltype(*std::begin(range))>::value_type;
using signature_type = std::conditional_t<
std::is_same_v<var_t, std::monostate>,
void(std::size_t),
void(std::size_t, var_t)>;
using pi = detail::promise_impl<signature_type, Executor1>;
using promise_t = promise<signature_type, Executor1>;
struct impl_t : pi
{
impl_t(Executor1 exec, Range&& range)
: pi(std::move(exec)),
range(std::move(range))
{
this->slot.template emplace<cancel_handler>(this);
}
struct cancel_handler
{
impl_t* self;
cancel_handler(impl_t* self)
: self(self)
{
}
void operator()(boost::asio::cancellation_type ct)
{
for (auto& r : self->range)
r.cancel(ct);
}
};
Range range;
cancellation_slot slot{this->cancel.slot()};
};
const auto size = std::distance(std::begin(range), std::end(range));
auto impl = std::allocate_shared<impl_t>(
get_associated_allocator(exec), exec, std::move(range));
impl->executor = exec;
if (size == 0u)
{
if constexpr (std::is_same_v<var_t, std::monostate>)
impl->result = {-1};
else
impl->result = {-1, var_t{}};
impl->done = true;
if (auto f = std::exchange(impl->completion, nullptr); !!f)
{
boost::asio::post(exec,
[impl, f = std::move(f)]() mutable
{
std::apply(std::move(f), std::move(*impl->result));
});
}
return promise_t{impl};
}
auto idx = 0u;
for (auto& val : impl->range)
{
val.async_wait(
bind_executor(exec,
[idx, impl]<typename... Args>(Args&&... args)
{
if (impl->done)
return;
if constexpr (std::is_same_v<var_t, std::monostate>)
impl->result = idx;
else
impl->result = std::make_tuple(idx,
var_t(std::forward<Args>(args)...));
impl->done = true;
if (auto f = std::exchange(impl->completion, nullptr); !!f)
std::apply(std::move(f), std::move(*impl->result));
auto jdx = 0u;
for (auto &tc : impl->range)
if (jdx++ != idx)
tc.cancel();
}));
idx++;
}
return promise_t{impl};
}
template <execution::executor Executor1, typename Range>
#if !defined(GENERATING_DOCUMENTATION)
requires requires (Range r)
{
{*std::begin(r)} -> is_promise_c;
{*std:: end(r)} -> is_promise_c;
}
#endif // !defined(GENERATING_DOCUMENTATION)
static auto all(Executor1 exec, Range range)
-> promise<
void(
std::vector<
typename std::decay_t<
decltype(*std::begin(range))
>::value_type
>
), Executor1>
{
using var_t = typename std::decay_t<
decltype(*std::begin(range))>::value_type;
using pi = detail::promise_impl<void(std::vector<var_t>), Executor1>;
struct impl_t : pi
{
impl_t(Executor1 exec, Range&& range)
: pi(std::move(exec)),
range(std::move(range))
{
this->slot.template emplace<cancel_handler>(this);
}
struct cancel_handler
{
impl_t* self;
cancel_handler(impl_t* self)
: self(self)
{
}
void operator()(cancellation_type ct)
{
for (auto& r : self->range)
r.cancel(ct);
}
};
Range range;
std::vector<std::optional<var_t>> partial_result;
cancellation_slot slot{this->cancel.slot()};
};
const auto size = std::distance(std::begin(range), std::end(range));
auto impl = std::allocate_shared<impl_t>(
get_associated_allocator(exec), exec, std::move(range));
impl->executor = exec;
impl->partial_result.resize(size);
if (size == 0u)
{
impl->result.emplace();
impl->done = true;
if (auto f = std::exchange(impl->completion, nullptr); !!f)
boost::asio::post(exec, [impl, f = std::move(f)]() mutable
{
std::apply(std::move(f), std::move(*impl->result));
});
return {impl};
}
auto idx = 0u;
for (auto& val : impl->range) {
val.async_wait(bind_executor(
exec,
[idx, impl]<typename... Args>(Args&&... args) {
impl->partial_result[idx].emplace(std::forward<Args>(args)...);
if (std::all_of(impl->partial_result.begin(),
impl->partial_result.end(),
[](auto &opt) {return opt.has_value();}))
{
impl->result.emplace();
get<0>(*impl->result).reserve(impl->partial_result.size());
for (auto& p : impl->partial_result)
get<0>(*impl->result).push_back(std::move(*p));
impl->done = true;
if (auto f = std::exchange(impl->completion, nullptr); !!f)
std::apply(std::move(f), std::move(*impl->result));
}
}));
idx++;
}
return {impl};
}
template <typename Range>
#if !defined(GENERATING_DOCUMENTATION)
requires requires (Range r)
{
{*std::begin(r)} -> is_promise_c;
{*std:: end(r)} -> is_promise_c;
}
#endif // !defined(GENERATING_DOCUMENTATION)
static auto race(Range range)
{
if (std::begin(range) == std::end(range))
throw std::logic_error(
"Can't use race on an empty range with deduced executor");
else
return race(std::begin(range)->get_executor(), std::move(range));
}
template <typename Range>
#if !defined(GENERATING_DOCUMENTATION)
requires requires (Range&& r)
{
{*std::begin(r)} -> is_promise_c;
{*std:: end(r)} -> is_promise_c;
}
#endif // !defined(GENERATING_DOCUMENTATION)
static auto all(Range range)
{
if (std::begin(range) == std::end(range))
throw std::logic_error(
"Can't use all on an empty range with deduced executor");
else
return all(std::begin(range)->get_executor(), std::move(range));
}
private:
#if !defined(GENERATING_DOCUMENTATION)
template <typename, typename> friend struct promise;
friend struct detail::promise_handler<void(Ts...)>;
#endif // !defined(GENERATING_DOCUMENTATION)
std::shared_ptr<detail::promise_impl<void(Ts...), Executor>> impl_;
promise(std::shared_ptr<detail::promise_impl<void(Ts...), Executor>> impl)
: impl_(impl)
{
}
struct initiate_async_wait
{
std::shared_ptr<detail::promise_impl<void(Ts...), Executor>> self_;
template <typename WaitHandler>
void operator()(WaitHandler&& handler) const
{
const auto exec = get_associated_executor(handler, self_->executor);
auto cancel = get_associated_cancellation_slot(handler);
if (self_->done)
{
boost::asio::post(exec,
[self = self_, h = std::forward<WaitHandler>(handler)]() mutable
{
std::apply(std::forward<WaitHandler>(h),
std::move(*self->result));
});
}
else
{
if (cancel.is_connected())
{
struct cancel_handler
{
std::weak_ptr<detail::promise_impl<void(Ts...), Executor>> self;
cancel_handler(
std::weak_ptr<detail::promise_impl<void(Ts...), Executor>> self)
: self(std::move(self))
{
}
void operator()(cancellation_type level) const
{
if (auto p = self.lock(); p != nullptr)
p->cancel.emit(level);
}
};
cancel.template emplace<cancel_handler>(self_);
}
self_->completion = {exec, std::forward<WaitHandler>(handler)};
}
}
};
};
} // namespace experimental
#if !defined(GENERATING_DOCUMENTATION)
template <typename Executor, typename R, typename... Args>
struct async_result<experimental::use_promise_t<Executor>, R(Args...)>
{
using handler_type = experimental::detail::promise_handler<
void(typename decay<Args>::type...), Executor>;
template <typename Initiation, typename... InitArgs>
static auto initiate(Initiation initiation,
experimental::use_promise_t<Executor>, InitArgs... args)
-> typename handler_type::promise_type
{
handler_type ht{get_associated_executor(initiation)};
std::move(initiation)(ht, std::move(args)...);
return ht.make_promise();
}
};
#endif // !defined(GENERATING_DOCUMENTATION)
} // namespace asio
} // namespace boost
#include <boost/asio/detail/pop_options.hpp>
#endif // BOOST_ASIO_EXPERIMENTAL_PROMISE_HPP

View File

@@ -0,0 +1,171 @@
//
// experimental/use_coro.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2021-2022 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 BOOST_ASIO_EXPERIMENTAL_USE_CORO_HPP
#define BOOST_ASIO_EXPERIMENTAL_USE_CORO_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include <boost/asio/detail/config.hpp>
#include <optional>
#include <boost/asio/bind_cancellation_slot.hpp>
#include <boost/asio/bind_executor.hpp>
#include <boost/system/error_code.hpp>
#include <boost/asio/experimental/detail/partial_promise.hpp>
#include <boost/asio/detail/push_options.hpp>
namespace boost {
namespace asio {
class any_io_executor;
namespace experimental {
/// A completion token that represents the currently executing resumable
/// coroutine.
/**
* 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.
*/
template <typename Executor = any_io_executor>
struct use_coro_t
{
/// Default constructor.
BOOST_ASIO_CONSTEXPR use_coro_t(
#if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
# if defined(BOOST_ASIO_HAS_SOURCE_LOCATION)
boost::asio::detail::source_location location =
boost::asio::detail::source_location::current()
# endif // defined(BOOST_ASIO_HAS_SOURCE_LOCATION)
#endif // defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
)
#if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
# if defined(BOOST_ASIO_HAS_SOURCE_LOCATION)
: file_name_(location.file_name()),
line_(location.line()),
function_name_(location.function_name())
# else // defined(BOOST_ASIO_HAS_SOURCE_LOCATION)
: file_name_(0),
line_(0),
function_name_(0)
# endif // defined(BOOST_ASIO_HAS_SOURCE_LOCATION)
#endif // defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
{
}
/// Constructor used to specify file name, line, and function name.
BOOST_ASIO_CONSTEXPR use_coro_t(const char* file_name,
int line, const char* function_name)
#if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
: file_name_(file_name),
line_(line),
function_name_(function_name)
#endif // defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
{
#if !defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
(void)file_name;
(void)line;
(void)function_name;
#endif // !defined(BOOST_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,
typename constraint<
conditional<
!is_same<InnerExecutor1, executor_with_default>::value,
is_convertible<InnerExecutor1, InnerExecutor>,
false_type
>::type::value
>::type = 0) BOOST_ASIO_NOEXCEPT
: InnerExecutor(ex)
{
}
};
/// Type alias to adapt an I/O object to use @c use_coro_t as its
/// default completion token type.
#if defined(BOOST_ASIO_HAS_ALIAS_TEMPLATES) \
|| defined(GENERATING_DOCUMENTATION)
template <typename T>
using as_default_on_t = typename T::template rebind_executor<
executor_with_default<typename T::executor_type> >::other;
#endif // defined(BOOST_ASIO_HAS_ALIAS_TEMPLATES)
// || defined(GENERATING_DOCUMENTATION)
/// 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>::type::template rebind_executor<
executor_with_default<typename decay<T>::type::executor_type>
>::other
as_default_on(BOOST_ASIO_MOVE_ARG(T) object)
{
return typename decay<T>::type::template rebind_executor<
executor_with_default<typename decay<T>::type::executor_type>
>::other(BOOST_ASIO_MOVE_CAST(T)(object));
}
#if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
const char* file_name_;
int line_;
const char* function_name_;
#endif // defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
};
/// A completion token object that represents the currently executing resumable
/// coroutine.
/**
* See the documentation for boost::asio::use_coro_t for a usage example.
*/
#if defined(GENERATING_DOCUMENTATION)
constexpr use_coro_t<> use_coro;
#elif defined(BOOST_ASIO_HAS_CONSTEXPR)
constexpr use_coro_t<> use_coro(0, 0, 0);
#elif defined(BOOST_ASIO_MSVC)
__declspec(selectany) use_coro_t<> use_coro(0, 0, 0);
#endif
} // namespace experimental
} // namespace asio
} // namespace boost
#include <boost/asio/detail/pop_options.hpp>
#include <boost/asio/experimental/impl/use_coro.hpp>
#include <boost/asio/experimental/coro.hpp>
#endif // BOOST_ASIO_EXPERIMENTAL_USE_CORO_HPP