// // 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 ASIO_EXPERIMENTAL_IMPL_USE_CORO_HPP #define ASIO_EXPERIMENTAL_IMPL_USE_CORO_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/push_options.hpp" namespace asio { namespace experimental { template struct coro; namespace detail { template struct coro_promise; template 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; std::optional> 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 Executor1> auto await_suspend( coroutine_handle > h) noexcept { promise->executor = h.promise().get_executor(); promise->h = h; return coroutine_handle::from_promise(*promise); } template static auto resume_impl(std::tuple&& tup) { return std::move(tup); } template static auto resume_impl(std::tuple&& 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::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 Executor1> auto await_suspend( coroutine_handle > h) noexcept { promise->executor = h.promise().get_executor(); promise->h = h; return coroutine_handle::from_promise(*promise); } template static auto resume_impl(std::tuple&& tup) { return std::move(tup); } static void resume_impl(std::tuple<>&&) {} template static auto resume_impl(std::tuple&& tup) { return get<0>(std::move(tup)); } template static auto resume_impl(std::tuple&& 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::index_sequence) { return std::make_tuple(std::get(std::move(tup))...); }(std::make_index_sequence{}); } } template static auto resume_impl( std::tuple&& tup) { auto ec = get<0>(std::move(tup)); if (ec) asio::detail::throw_exception( asio::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::index_sequence) { return std::make_tuple(std::get(std::move(tup))...); }(std::make_index_sequence{}); } static auto resume_impl(std::tuple&& tup) { auto ex = get<0>(std::move(tup)); if (ex) std::rethrow_exception(ex); } static auto resume_impl( std::tuple&& tup) { auto ec = get<0>(std::move(tup)); if (ec) asio::detail::throw_error(ec, "error_code in use_coro"); } auto await_resume() const { auto res = std::move(promise->result.value()); coroutine_handle::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 struct async_result, R(Args...)> { using return_type = experimental::detail::coro_init_handler< Executor, typename decay::type...>; template static return_type initiate(Initiation initiation, experimental::use_coro_t, InitArgs... args) { std::move(initiation)(co_await return_type::handler, std::move(args)...); } }; #endif // !defined(GENERATING_DOCUMENTATION) } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_EXPERIMENTAL_IMPL_USE_CORO_HPP