// // Copyright (c) 2024 Klemens Morgenstern (klemens.morgenstern@gmx.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_COBALT_EXPERIMENTAL_COMPOSITION_HPP #define BOOST_COBALT_EXPERIMENTAL_COMPOSITION_HPP #include #include namespace boost::cobalt::detail { template struct composition_promise : promise_cancellation_base, enable_await_allocator>, enable_await_executor> { void get_return_object() {} using promise_cancellation_base::await_transform; using enable_await_allocator>::await_transform; using enable_await_executor>::await_transform; using handler_type = completion_handler; using allocator_type = typename handler_type::allocator_type; allocator_type get_allocator() const {return handler.get_allocator();} #if !defined(BOOST_COBALT_NO_PMR) using resource_type = pmr::memory_resource; #else using resource_type = cobalt::detail::sbo_resource; #endif template BOOST_COBALT_MSVC_NOINLINE auto await_transform(asio::deferred_async_operation op_) { struct deferred_op : op { asio::deferred_async_operation op_; deferred_op(asio::deferred_async_operation op_, resource_type * resource) : op_(std::move(op_)), resource(resource) {} void initiate(cobalt::completion_handler complete) override { std::move(op_)(std::move(complete)); } resource_type * resource; typename op::awaitable_base operator co_await() { return static_cast&&>(*this).operator co_await().replace_resource(this->resource); } }; return cobalt::as_tuple(deferred_op{std::move(op_), handler.get_allocator().resource()}); } template requires requires (Op && op, resource_type* res) { {static_cast(op).operator co_await().replace_resource(res)} -> awaitable_type; } BOOST_COBALT_MSVC_NOINLINE auto await_transform(Op && op_) { struct replacing_op { Op op; resource_type * resource; auto operator co_await() { return std::forward(op).operator co_await().replace_resource(this->resource); } }; return cobalt::as_tuple(replacing_op{std::forward(op_), handler.get_allocator().resource()}); } using executor_type = typename handler_type::executor_type ; const executor & get_executor() const {return handler.get_executor();} template static void * operator new(const std::size_t size, Ts & ... args) { using tt = std::pair; // | memory_resource | size_t | | coroutine. constexpr auto block_size = sizeof(tt) / sizeof(std::max_align_t) + (sizeof(tt) % sizeof(std::max_align_t) ? 1 : 0); auto res = std::get(std::tie(args...)).get_allocator().resource(); const auto p = res->allocate(size + (block_size * sizeof(std::max_align_t))); new (p) tt(res, size); return static_cast(p) + block_size; } static void operator delete(void * raw) noexcept { using tt = std::pair; // | memory_resource | size_t | | coroutine. constexpr auto block_size = sizeof(tt) / sizeof(std::max_align_t) + (sizeof(tt) % sizeof(std::max_align_t) ? 1 : 0); const auto p = static_cast(raw) - block_size; const auto tp = *reinterpret_cast(p); const auto res = tp.first; const auto size = tp.second; res->deallocate(p, size + (block_size * sizeof(std::max_align_t))); } completion_handler handler; template composition_promise(Ts && ... args) : handler(std::move(std::get(std::tie(args...)))) { } void unhandled_exception() { throw ; } constexpr static std::suspend_never initial_suspend() {return {};} void return_value(std::tuple args) { handler.result.emplace(std::move(args)); } struct final_awaitable { constexpr bool await_ready() noexcept {return false;} completion_handler handler; BOOST_COBALT_MSVC_NOINLINE std::coroutine_handle await_suspend(std::coroutine_handle h) noexcept { auto exec = handler.get_executor(); auto ho = handler.self.release(); detail::self_destroy(h, exec); if (handler.completed_immediately != nullptr && *handler.completed_immediately == completed_immediately_t::initiating) //not maybe here, because we don't go through the immediate executor { *handler.completed_immediately = completed_immediately_t::yes; return std::noop_coroutine(); } return ho; } constexpr void await_resume() noexcept {} }; BOOST_COBALT_MSVC_NOINLINE auto final_suspend() noexcept { return final_awaitable{std::move(handler)}; } }; } template struct std::coroutine_traits> { using promise_type = ::boost::cobalt::detail::composition_promise; }; template struct std::coroutine_traits> { using promise_type = ::boost::cobalt::detail::composition_promise; }; template struct std::coroutine_traits> { using promise_type = ::boost::cobalt::detail::composition_promise; }; template struct std::coroutine_traits> { using promise_type = ::boost::cobalt::detail::composition_promise; }; template struct std::coroutine_traits> { using promise_type = ::boost::cobalt::detail::composition_promise; }; template struct std::coroutine_traits> { using promise_type = ::boost::cobalt::detail::composition_promise; }; template struct std::coroutine_traits> { using promise_type = ::boost::cobalt::detail::composition_promise; }; template struct std::coroutine_traits> { using promise_type = ::boost::cobalt::detail::composition_promise; }; template struct std::coroutine_traits> { using promise_type = ::boost::cobalt::detail::composition_promise; }; template struct std::coroutine_traits> { using promise_type = ::boost::cobalt::detail::composition_promise; }; #endif //BOOST_COBALT_EXPERIMENTAL_COMPOSITION_HPP