// Copyright (c) 2022 Klemens D. Morgenstern // // 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_COBALT_DETAIL_WITH_HPP #define BOOST_COBALT_DETAIL_WITH_HPP #include #include #include namespace boost::cobalt::detail { template struct [[nodiscard]] with_impl { struct promise_type; bool await_ready() { return false;} template BOOST_NOINLINE auto await_suspend(std::coroutine_handle h) -> std::coroutine_handle; inline T await_resume(); private: with_impl(promise_type & promise) : promise(promise) {} promise_type & promise; }; template struct with_promise_value { std::optional result; void return_value(std::optional && value) { if (value) // so non-move-assign types work result.emplace(std::move(*value)); } T get_result() { return std::move(result).value(); } }; template<> struct with_promise_value { void return_void() {} void get_result() {} }; template struct with_impl::promise_type : with_promise_value, enable_awaitables, enable_await_allocator { using enable_awaitables::await_transform; using enable_await_allocator::await_transform; using executor_type = executor; const executor_type & get_executor() const {return *exec;} std::optional exec; with_impl get_return_object() { return with_impl{*this}; } std::exception_ptr e; void unhandled_exception() { e = std::current_exception(); } std::suspend_always initial_suspend() noexcept {return {};} struct final_awaitable { promise_type *promise; bool await_ready() const noexcept { return false; } BOOST_NOINLINE auto await_suspend(std::coroutine_handle h) noexcept -> std::coroutine_handle { return std::coroutine_handle::from_address(h.promise().awaited_from.address()); } void await_resume() noexcept { } }; auto final_suspend() noexcept { return final_awaitable{this}; } using cancellation_slot_type = asio::cancellation_slot; cancellation_slot_type get_cancellation_slot() const {return slot_;} asio::cancellation_slot slot_; std::coroutine_handle awaited_from{nullptr}; }; template T with_impl::await_resume() { auto e = promise.e; auto res = std::move(promise.get_result()); std::coroutine_handle::from_promise(promise).destroy(); if (e) std::rethrow_exception(e); return std::move(res); } template<> inline void with_impl::await_resume() { auto e = promise.e; std::coroutine_handle::from_promise(promise).destroy(); if (e) std::rethrow_exception(e); } template template auto with_impl::await_suspend(std::coroutine_handle h) -> std::coroutine_handle { if constexpr (requires (Promise p) {p.get_executor();}) promise.exec.emplace(h.promise().get_executor()); else promise.exec.emplace(this_thread::get_executor()); if constexpr (requires (Promise p) {p.get_cancellation_slot();}) promise.slot_ = h.promise().get_cancellation_slot(); promise.awaited_from = h; return std::coroutine_handle::from_promise(promise); } } #endif //BOOST_COBALT_DETAIL_WITH_HPP