// // 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 #include namespace boost { namespace asio { namespace experimental { namespace detail { template >:: template rebind_alloc, bool Noexcept = noexcept(std::declval().allocate(0u))> struct coro_promise_allocator { using allocator_type = Allocator; template void* operator new(const std::size_t size, Executor executor, Args&&...) { static_assert(std::is_nothrow_move_constructible_v); allocator_type alloc{get_associated_allocator(executor)}; const auto csize = size + sizeof(alloc); const auto raw = std::allocator_traits::allocate(alloc, csize); new (raw) allocator_type(std::move(alloc)); return static_cast(raw) + sizeof(alloc); } template 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)...); } void operator delete(void* raw, std::size_t size ) { auto * alloc_p = static_cast(raw); auto alloc = std::move(*alloc_p); alloc_p->~allocator_type(); std::allocator_traits::deallocate(alloc, static_cast(raw), size + sizeof(allocator_type)); } }; template struct coro_promise_allocator, false> { }; template struct coro_promise_allocator { using allocator_type = Allocator; template void* operator new(const std::size_t size, Executor executor, Args&&...) noexcept { static_assert(std::is_nothrow_move_constructible_v); allocator_type alloc{get_associated_allocator(executor)}; const auto csize = size + sizeof(alloc); const auto raw = std::allocator_traits::allocate(alloc, csize); if (raw == nullptr) return nullptr; new (raw) allocator_type(std::move(alloc)); return static_cast(raw) + sizeof(alloc); } template 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)...); } void operator delete(void* raw, std::size_t size) noexcept { auto * alloc_p = static_cast(raw); auto alloc = std::move(*alloc_p); alloc_p->~allocator_type(); const auto csize = size + sizeof(allocator_type); std::allocator_traits::deallocate(alloc, static_cast(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