diff options
Diffstat (limited to '3rdparty/asio/include/asio/experimental/awaitable_operators.hpp')
-rw-r--r-- | 3rdparty/asio/include/asio/experimental/awaitable_operators.hpp | 536 |
1 files changed, 536 insertions, 0 deletions
diff --git a/3rdparty/asio/include/asio/experimental/awaitable_operators.hpp b/3rdparty/asio/include/asio/experimental/awaitable_operators.hpp new file mode 100644 index 00000000000..45799daaa5f --- /dev/null +++ b/3rdparty/asio/include/asio/experimental/awaitable_operators.hpp @@ -0,0 +1,536 @@ +// +// experimental/awaitable_operators.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_EXPERIMENTAL_AWAITABLE_OPERATORS_HPP +#define ASIO_EXPERIMENTAL_AWAITABLE_OPERATORS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include <optional> +#include <stdexcept> +#include <tuple> +#include <variant> +#include "asio/awaitable.hpp" +#include "asio/co_spawn.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/experimental/deferred.hpp" +#include "asio/experimental/parallel_group.hpp" +#include "asio/multiple_exceptions.hpp" +#include "asio/this_coro.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace experimental { +namespace awaitable_operators { +namespace detail { + +template <typename T, typename Executor> +awaitable<T, Executor> awaitable_wrap(awaitable<T, Executor> a, + constraint_t<is_constructible<T>::value>* = 0) +{ + return a; +} + +template <typename T, typename Executor> +awaitable<std::optional<T>, Executor> awaitable_wrap(awaitable<T, Executor> a, + constraint_t<!is_constructible<T>::value>* = 0) +{ + co_return std::optional<T>(co_await std::move(a)); +} + +template <typename T> +T& awaitable_unwrap(conditional_t<true, T, void>& r, + constraint_t<is_constructible<T>::value>* = 0) +{ + return r; +} + +template <typename T> +T& awaitable_unwrap(std::optional<conditional_t<true, T, void>>& r, + constraint_t<!is_constructible<T>::value>* = 0) +{ + return *r; +} + +} // namespace detail + +/// Wait for both operations to succeed. +/** + * If one operations fails, the other is cancelled as the AND-condition can no + * longer be satisfied. + */ +template <typename Executor> +awaitable<void, Executor> operator&&( + awaitable<void, Executor> t, awaitable<void, Executor> u) +{ + auto ex = co_await this_coro::executor; + + auto [order, ex0, ex1] = + co_await make_parallel_group( + co_spawn(ex, std::move(t), deferred), + co_spawn(ex, std::move(u), deferred) + ).async_wait( + wait_for_one_error(), + deferred + ); + + if (ex0 && ex1) + throw multiple_exceptions(ex0); + if (ex0) + std::rethrow_exception(ex0); + if (ex1) + std::rethrow_exception(ex1); + co_return; +} + +/// Wait for both operations to succeed. +/** + * If one operations fails, the other is cancelled as the AND-condition can no + * longer be satisfied. + */ +template <typename U, typename Executor> +awaitable<U, Executor> operator&&( + awaitable<void, Executor> t, awaitable<U, Executor> u) +{ + auto ex = co_await this_coro::executor; + + auto [order, ex0, ex1, r1] = + co_await make_parallel_group( + co_spawn(ex, std::move(t), deferred), + co_spawn(ex, detail::awaitable_wrap(std::move(u)), deferred) + ).async_wait( + wait_for_one_error(), + deferred + ); + + if (ex0 && ex1) + throw multiple_exceptions(ex0); + if (ex0) + std::rethrow_exception(ex0); + if (ex1) + std::rethrow_exception(ex1); + co_return std::move(detail::awaitable_unwrap<U>(r1)); +} + +/// Wait for both operations to succeed. +/** + * If one operations fails, the other is cancelled as the AND-condition can no + * longer be satisfied. + */ +template <typename T, typename Executor> +awaitable<T, Executor> operator&&( + awaitable<T, Executor> t, awaitable<void, Executor> u) +{ + auto ex = co_await this_coro::executor; + + auto [order, ex0, r0, ex1] = + co_await make_parallel_group( + co_spawn(ex, detail::awaitable_wrap(std::move(t)), deferred), + co_spawn(ex, std::move(u), deferred) + ).async_wait( + wait_for_one_error(), + deferred + ); + + if (ex0 && ex1) + throw multiple_exceptions(ex0); + if (ex0) + std::rethrow_exception(ex0); + if (ex1) + std::rethrow_exception(ex1); + co_return std::move(detail::awaitable_unwrap<T>(r0)); +} + +/// Wait for both operations to succeed. +/** + * If one operations fails, the other is cancelled as the AND-condition can no + * longer be satisfied. + */ +template <typename T, typename U, typename Executor> +awaitable<std::tuple<T, U>, Executor> operator&&( + awaitable<T, Executor> t, awaitable<U, Executor> u) +{ + auto ex = co_await this_coro::executor; + + auto [order, ex0, r0, ex1, r1] = + co_await make_parallel_group( + co_spawn(ex, detail::awaitable_wrap(std::move(t)), deferred), + co_spawn(ex, detail::awaitable_wrap(std::move(u)), deferred) + ).async_wait( + wait_for_one_error(), + deferred + ); + + if (ex0 && ex1) + throw multiple_exceptions(ex0); + if (ex0) + std::rethrow_exception(ex0); + if (ex1) + std::rethrow_exception(ex1); + co_return std::make_tuple( + std::move(detail::awaitable_unwrap<T>(r0)), + std::move(detail::awaitable_unwrap<U>(r1))); +} + +/// Wait for both operations to succeed. +/** + * If one operations fails, the other is cancelled as the AND-condition can no + * longer be satisfied. + */ +template <typename... T, typename Executor> +awaitable<std::tuple<T..., std::monostate>, Executor> operator&&( + awaitable<std::tuple<T...>, Executor> t, awaitable<void, Executor> u) +{ + auto ex = co_await this_coro::executor; + + auto [order, ex0, r0, ex1, r1] = + co_await make_parallel_group( + co_spawn(ex, detail::awaitable_wrap(std::move(t)), deferred), + co_spawn(ex, std::move(u), deferred) + ).async_wait( + wait_for_one_error(), + deferred + ); + + if (ex0 && ex1) + throw multiple_exceptions(ex0); + if (ex0) + std::rethrow_exception(ex0); + if (ex1) + std::rethrow_exception(ex1); + co_return std::move(detail::awaitable_unwrap<std::tuple<T...>>(r0)); +} + +/// Wait for both operations to succeed. +/** + * If one operations fails, the other is cancelled as the AND-condition can no + * longer be satisfied. + */ +template <typename... T, typename U, typename Executor> +awaitable<std::tuple<T..., U>, Executor> operator&&( + awaitable<std::tuple<T...>, Executor> t, awaitable<U, Executor> u) +{ + auto ex = co_await this_coro::executor; + + auto [order, ex0, r0, ex1, r1] = + co_await make_parallel_group( + co_spawn(ex, detail::awaitable_wrap(std::move(t)), deferred), + co_spawn(ex, detail::awaitable_wrap(std::move(u)), deferred) + ).async_wait( + wait_for_one_error(), + deferred + ); + + if (ex0 && ex1) + throw multiple_exceptions(ex0); + if (ex0) + std::rethrow_exception(ex0); + if (ex1) + std::rethrow_exception(ex1); + co_return std::tuple_cat( + std::move(detail::awaitable_unwrap<std::tuple<T...>>(r0)), + std::make_tuple(std::move(detail::awaitable_unwrap<U>(r1)))); +} + +/// Wait for one operation to succeed. +/** + * If one operations succeeds, the other is cancelled as the OR-condition is + * already satisfied. + */ +template <typename Executor> +awaitable<std::variant<std::monostate, std::monostate>, Executor> operator||( + awaitable<void, Executor> t, awaitable<void, Executor> u) +{ + auto ex = co_await this_coro::executor; + + auto [order, ex0, ex1] = + co_await make_parallel_group( + co_spawn(ex, std::move(t), deferred), + co_spawn(ex, std::move(u), deferred) + ).async_wait( + wait_for_one_success(), + deferred + ); + + if (order[0] == 0) + { + if (!ex0) + co_return std::variant<std::monostate, std::monostate>{ + std::in_place_index<0>}; + if (!ex1) + co_return std::variant<std::monostate, std::monostate>{ + std::in_place_index<1>}; + throw multiple_exceptions(ex0); + } + else + { + if (!ex1) + co_return std::variant<std::monostate, std::monostate>{ + std::in_place_index<1>}; + if (!ex0) + co_return std::variant<std::monostate, std::monostate>{ + std::in_place_index<0>}; + throw multiple_exceptions(ex1); + } +} + +/// Wait for one operation to succeed. +/** + * If one operations succeeds, the other is cancelled as the OR-condition is + * already satisfied. + */ +template <typename U, typename Executor> +awaitable<std::variant<std::monostate, U>, Executor> operator||( + awaitable<void, Executor> t, awaitable<U, Executor> u) +{ + auto ex = co_await this_coro::executor; + + auto [order, ex0, ex1, r1] = + co_await make_parallel_group( + co_spawn(ex, std::move(t), deferred), + co_spawn(ex, detail::awaitable_wrap(std::move(u)), deferred) + ).async_wait( + wait_for_one_success(), + deferred + ); + + if (order[0] == 0) + { + if (!ex0) + co_return std::variant<std::monostate, U>{ + std::in_place_index<0>}; + if (!ex1) + co_return std::variant<std::monostate, U>{ + std::in_place_index<1>, + std::move(detail::awaitable_unwrap<U>(r1))}; + throw multiple_exceptions(ex0); + } + else + { + if (!ex1) + co_return std::variant<std::monostate, U>{ + std::in_place_index<1>, + std::move(detail::awaitable_unwrap<U>(r1))}; + if (!ex0) + co_return std::variant<std::monostate, U>{ + std::in_place_index<0>}; + throw multiple_exceptions(ex1); + } +} + +/// Wait for one operation to succeed. +/** + * If one operations succeeds, the other is cancelled as the OR-condition is + * already satisfied. + */ +template <typename T, typename Executor> +awaitable<std::variant<T, std::monostate>, Executor> operator||( + awaitable<T, Executor> t, awaitable<void, Executor> u) +{ + auto ex = co_await this_coro::executor; + + auto [order, ex0, r0, ex1] = + co_await make_parallel_group( + co_spawn(ex, detail::awaitable_wrap(std::move(t)), deferred), + co_spawn(ex, std::move(u), deferred) + ).async_wait( + wait_for_one_success(), + deferred + ); + + if (order[0] == 0) + { + if (!ex0) + co_return std::variant<T, std::monostate>{ + std::in_place_index<0>, + std::move(detail::awaitable_unwrap<T>(r0))}; + if (!ex1) + co_return std::variant<T, std::monostate>{ + std::in_place_index<1>}; + throw multiple_exceptions(ex0); + } + else + { + if (!ex1) + co_return std::variant<T, std::monostate>{ + std::in_place_index<1>}; + if (!ex0) + co_return std::variant<T, std::monostate>{ + std::in_place_index<0>, + std::move(detail::awaitable_unwrap<T>(r0))}; + throw multiple_exceptions(ex1); + } +} + +/// Wait for one operation to succeed. +/** + * If one operations succeeds, the other is cancelled as the OR-condition is + * already satisfied. + */ +template <typename T, typename U, typename Executor> +awaitable<std::variant<T, U>, Executor> operator||( + awaitable<T, Executor> t, awaitable<U, Executor> u) +{ + auto ex = co_await this_coro::executor; + + auto [order, ex0, r0, ex1, r1] = + co_await make_parallel_group( + co_spawn(ex, detail::awaitable_wrap(std::move(t)), deferred), + co_spawn(ex, detail::awaitable_wrap(std::move(u)), deferred) + ).async_wait( + wait_for_one_success(), + deferred + ); + + if (order[0] == 0) + { + if (!ex0) + co_return std::variant<T, U>{ + std::in_place_index<0>, + std::move(detail::awaitable_unwrap<T>(r0))}; + if (!ex1) + co_return std::variant<T, U>{ + std::in_place_index<1>, + std::move(detail::awaitable_unwrap<U>(r1))}; + throw multiple_exceptions(ex0); + } + else + { + if (!ex1) + co_return std::variant<T, U>{ + std::in_place_index<1>, + std::move(detail::awaitable_unwrap<U>(r1))}; + if (!ex0) + co_return std::variant<T, U>{ + std::in_place_index<0>, + std::move(detail::awaitable_unwrap<T>(r0))}; + throw multiple_exceptions(ex1); + } +} + +namespace detail { + +template <typename... T> +struct widen_variant +{ + template <std::size_t I, typename SourceVariant> + static std::variant<T...> call(SourceVariant& source) + { + if (source.index() == I) + return std::variant<T...>{ + std::in_place_index<I>, std::move(std::get<I>(source))}; + else if constexpr (I + 1 < std::variant_size_v<SourceVariant>) + return call<I + 1>(source); + else + throw std::logic_error("empty variant"); + } +}; + +} // namespace detail + +/// Wait for one operation to succeed. +/** + * If one operations succeeds, the other is cancelled as the OR-condition is + * already satisfied. + */ +template <typename... T, typename Executor> +awaitable<std::variant<T..., std::monostate>, Executor> operator||( + awaitable<std::variant<T...>, Executor> t, awaitable<void, Executor> u) +{ + auto ex = co_await this_coro::executor; + + auto [order, ex0, r0, ex1] = + co_await make_parallel_group( + co_spawn(ex, detail::awaitable_wrap(std::move(t)), deferred), + co_spawn(ex, std::move(u), deferred) + ).async_wait( + wait_for_one_success(), + deferred + ); + + using widen = detail::widen_variant<T..., std::monostate>; + if (order[0] == 0) + { + if (!ex0) + co_return widen::template call<0>( + detail::awaitable_unwrap<std::variant<T...>>(r0)); + if (!ex1) + co_return std::variant<T..., std::monostate>{ + std::in_place_index<sizeof...(T)>}; + throw multiple_exceptions(ex0); + } + else + { + if (!ex1) + co_return std::variant<T..., std::monostate>{ + std::in_place_index<sizeof...(T)>}; + if (!ex0) + co_return widen::template call<0>( + detail::awaitable_unwrap<std::variant<T...>>(r0)); + throw multiple_exceptions(ex1); + } +} + +/// Wait for one operation to succeed. +/** + * If one operations succeeds, the other is cancelled as the OR-condition is + * already satisfied. + */ +template <typename... T, typename U, typename Executor> +awaitable<std::variant<T..., U>, Executor> operator||( + awaitable<std::variant<T...>, Executor> t, awaitable<U, Executor> u) +{ + auto ex = co_await this_coro::executor; + + auto [order, ex0, r0, ex1, r1] = + co_await make_parallel_group( + co_spawn(ex, detail::awaitable_wrap(std::move(t)), deferred), + co_spawn(ex, detail::awaitable_wrap(std::move(u)), deferred) + ).async_wait( + wait_for_one_success(), + deferred + ); + + using widen = detail::widen_variant<T..., U>; + if (order[0] == 0) + { + if (!ex0) + co_return widen::template call<0>( + detail::awaitable_unwrap<std::variant<T...>>(r0)); + if (!ex1) + co_return std::variant<T..., U>{ + std::in_place_index<sizeof...(T)>, + std::move(detail::awaitable_unwrap<U>(r1))}; + throw multiple_exceptions(ex0); + } + else + { + if (!ex1) + co_return std::variant<T..., U>{ + std::in_place_index<sizeof...(T)>, + std::move(detail::awaitable_unwrap<U>(r1))}; + if (!ex0) + co_return widen::template call<0>( + detail::awaitable_unwrap<std::variant<T...>>(r0)); + throw multiple_exceptions(ex1); + } +} + +} // namespace awaitable_operators +} // namespace experimental +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_EXPERIMENTAL_AWAITABLE_OPERATORS_HPP |