// 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_THIS_CORO_HPP #define BOOST_COBALT_THIS_CORO_HPP #include #include #include #include #include #include #include #include #include #include namespace boost::cobalt { namespace this_coro { /* tag::outline[] // Awaitable type that returns the executor of the current coroutine. struct executor_t {} constexpr executor_t executor; // Awaitable type that returns the cancellation state of the current coroutine. struct cancellation_state_t {}; constexpr cancellation_state_t cancellation_state; // Reset the cancellation state with custom or default filters. constexpr __unspecified__ reset_cancellation_state(); template constexpr __unspecified__ reset_cancellation_state( Filter && filter); template constexpr __unspecified__ reset_cancellation_state( InFilter && in_filter, OutFilter && out_filter); // get & set the throw_if_cancelled setting. __unspecified__ throw_if_cancelled(); __unspecified__ throw_if_cancelled(bool value); // Set the cancellation source in a detached. __unspecified__ reset_cancellation_source(); __unspecified__ reset_cancellation_source(asio::cancellation_slot slot); end::outline[] */ using namespace asio::this_coro; //tag::outline[] // get the allocator the promise struct allocator_t {}; constexpr allocator_t allocator; // get the current cancellation state-type struct cancelled_t {}; constexpr cancelled_t cancelled; // set the over-eager mode of a generator struct initial_t {}; constexpr initial_t initial; //end::outline[] template struct reset_cancellation_source_t { CancellationSlot source; }; template reset_cancellation_source_t reset_cancellation_source(CancellationSlot slot = {}) { return reset_cancellation_source_t{std::move(slot)}; } } template struct promise_cancellation_base { using cancellation_slot_type = asio::cancellation_slot; cancellation_slot_type get_cancellation_slot() const {return state_.slot();} template promise_cancellation_base(CancellationSlot slot = {}, InitialFilter filter = {}) : source_(slot), state_{source_, filter} {} // This await transformation resets the associated cancellation state. auto await_transform(cobalt::this_coro::cancelled_t) noexcept { return cancelled_t_awaitable{state_.cancelled()}; } // This await transformation resets the associated cancellation state. auto await_transform(asio::this_coro::cancellation_state_t) noexcept { return cancellation_state_t_awaitable{state_}; } // This await transformation resets the associated cancellation state. auto await_transform(asio::this_coro::reset_cancellation_state_0_t) noexcept { return reset_cancellation_state_0_t_awaitable{state_, source_}; } // This await transformation resets the associated cancellation state. template auto await_transform( asio::this_coro::reset_cancellation_state_1_t reset) noexcept { return reset_cancellation_state_1_t_awaitable{state_, BOOST_ASIO_MOVE_CAST(Filter)(reset.filter), source_}; } // This await transformation resets the associated cancellation state. template auto await_transform( asio::this_coro::reset_cancellation_state_2_t reset) noexcept { return reset_cancellation_state_2_t_awaitable{state_, BOOST_ASIO_MOVE_CAST(InFilter)(reset.in_filter), BOOST_ASIO_MOVE_CAST(OutFilter)(reset.out_filter), source_}; } const asio::cancellation_state & cancellation_state() const {return state_;} asio::cancellation_state & cancellation_state() {return state_;} asio::cancellation_type cancelled() const { return state_.cancelled(); } cancellation_slot_type get_cancellation_slot() {return state_.slot();} void reset_cancellation_source(CancellationSlot source = CancellationSlot()) { source_ = source; state_ = asio::cancellation_state{source, DefaultFilter()}; state_.clear(); } CancellationSlot & source() {return source_;} const CancellationSlot & source() const {return source_;} private: CancellationSlot source_; asio::cancellation_state state_{source_, DefaultFilter() }; struct cancelled_t_awaitable { asio::cancellation_type state; bool await_ready() const noexcept { return true; } void await_suspend(std::coroutine_handle) noexcept { } auto await_resume() const { return state; } }; struct cancellation_state_t_awaitable { asio::cancellation_state &state; bool await_ready() const noexcept { return true; } void await_suspend(std::coroutine_handle) noexcept { } auto await_resume() const { return state; } }; struct reset_cancellation_state_0_t_awaitable { asio::cancellation_state &state; CancellationSlot &source; bool await_ready() const noexcept { return true; } void await_suspend(std::coroutine_handle) noexcept { } auto await_resume() const { state = asio::cancellation_state(source, DefaultFilter()); } }; template struct reset_cancellation_state_1_t_awaitable { asio::cancellation_state & state; Filter filter_; CancellationSlot &source; bool await_ready() const noexcept { return true; } void await_suspend(std::coroutine_handle) noexcept { } auto await_resume() { state = asio::cancellation_state( source, BOOST_ASIO_MOVE_CAST(Filter)(filter_)); } }; template struct reset_cancellation_state_2_t_awaitable { asio::cancellation_state & state; InFilter in_filter_; OutFilter out_filter_; CancellationSlot &source; bool await_ready() const noexcept { return true; } void await_suspend(std::coroutine_handle) noexcept { } auto await_resume() { state = asio::cancellation_state( source, BOOST_ASIO_MOVE_CAST(InFilter)(in_filter_), BOOST_ASIO_MOVE_CAST(OutFilter)(out_filter_)); } }; }; struct promise_throw_if_cancelled_base { promise_throw_if_cancelled_base(bool throw_if_cancelled = true) : throw_if_cancelled_(throw_if_cancelled) {} // This await transformation determines whether cancellation is propagated as // an exception. auto await_transform(this_coro::throw_if_cancelled_0_t) noexcept { return throw_if_cancelled_0_awaitable_{throw_if_cancelled_}; } // This await transformation sets whether cancellation is propagated as an // exception. auto await_transform(this_coro::throw_if_cancelled_1_t throw_if_cancelled) noexcept { return throw_if_cancelled_1_awaitable_{this, throw_if_cancelled.value}; } bool throw_if_cancelled() const {return throw_if_cancelled_;} protected: bool throw_if_cancelled_{true}; struct throw_if_cancelled_0_awaitable_ { bool value_; bool await_ready() const noexcept { return true; } void await_suspend(std::coroutine_handle) noexcept { } auto await_resume() { return value_; } }; struct throw_if_cancelled_1_awaitable_ { promise_throw_if_cancelled_base* this_; bool value_; bool await_ready() const noexcept { return true; } void await_suspend(std::coroutine_handle) noexcept { } auto await_resume() { this_->throw_if_cancelled_ = value_; } }; }; struct promise_memory_resource_base { #if !defined(BOOST_COBALT_NO_PMR) using allocator_type = pmr::polymorphic_allocator; allocator_type get_allocator() const {return allocator_type{resource};} #if defined(__cpp_sized_deallocation) template static void * operator new(const std::size_t size, Args & ... args) { auto res = detail::get_memory_resource_from_args(args...); const auto p = res->allocate(size + sizeof(std::max_align_t)); auto pp = static_cast(p); *pp = res; return static_cast(p) + 1; } static void operator delete(void * raw, const std::size_t size) noexcept { const auto p = static_cast(raw) - 1; pmr::memory_resource * res = *reinterpret_cast(p); res->deallocate(p, size + sizeof(std::max_align_t)); } #else template static void * operator new(const std::size_t size, Args & ... 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 = detail::get_memory_resource_from_args(args...); 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))); } #endif promise_memory_resource_base(pmr::memory_resource * resource = this_thread::get_default_resource()) : resource(resource) {} private: pmr::memory_resource * resource = this_thread::get_default_resource(); #endif }; #if defined(__cpp_sized_deallocation) /// Allocate the memory and put the allocator behind the cobalt memory template void *allocate_coroutine(const std::size_t size, AllocatorType alloc_) { using alloc_type = typename std::allocator_traits::template rebind_alloc; alloc_type alloc{alloc_}; const std::size_t aligned_size = size / sizeof(std::max_align_t) + (size % sizeof(std::max_align_t) > 0 ? 1 : 0); const std::size_t alloc_size = sizeof(AllocatorType) / sizeof(std::max_align_t) + (sizeof(AllocatorType) % sizeof(std::max_align_t) > 0 ? 1 : 0); const auto raw = std::allocator_traits::allocate(alloc, alloc_size + aligned_size); new(raw + aligned_size) alloc_type(std::move(alloc)); return raw; } /// Deallocate the memory and destroy the allocator in the cobalt memory. template void deallocate_coroutine(void *raw_, const std::size_t size) { using alloc_type = typename std::allocator_traits::template rebind_alloc; const auto raw = static_cast(raw_); const std::size_t aligned_size = size / sizeof(std::max_align_t) + (size % sizeof(std::max_align_t) > 0 ? 1 : 0); const std::size_t alloc_size = sizeof(AllocatorType) / sizeof(std::max_align_t) + (sizeof(AllocatorType) % sizeof(std::max_align_t) > 0 ? 1 : 0); auto alloc_p = reinterpret_cast(raw + aligned_size); auto alloc = std::move(*alloc_p); alloc_p->~alloc_type(); std::allocator_traits::deallocate(alloc, raw, aligned_size + alloc_size); } #else /// Allocate the memory and put the allocator behind the cobalt memory template void *allocate_coroutine(const std::size_t size, AllocatorType alloc_) { using alloc_type = typename std::allocator_traits::template rebind_alloc; alloc_type alloc{alloc_}; const std::size_t aligned_size = size / sizeof(std::max_align_t) + (size % sizeof(std::max_align_t) > 0 ? 1 : 0); const std::size_t alloc_size = sizeof(AllocatorType) / sizeof(std::max_align_t) + (sizeof(AllocatorType) % sizeof(std::max_align_t) > 0 ? 1 : 0); const std::size_t size_size = sizeof(std::size_t) / sizeof(std::max_align_t) + (sizeof(std::size_t) % sizeof(std::max_align_t) > 0 ? 1 : 0); static_assert(alignof(std::max_align_t) >= sizeof(std::size_t)); const auto raw = std::allocator_traits::allocate(alloc, alloc_size + aligned_size + size_size); new(raw) alloc_type(std::move(alloc)); new(raw + alloc_size) std::size_t(aligned_size); return raw + alloc_size + size_size; } /// Deallocate the memory and destroy the allocator in the cobalt memory. template void deallocate_coroutine(void *raw_) { using alloc_type = typename std::allocator_traits::template rebind_alloc; const auto raw = static_cast(raw_); const std::size_t size_size = sizeof(std::size_t) / sizeof(std::max_align_t) + (sizeof(std::size_t) % sizeof(std::max_align_t) > 0 ? 1 : 0); const std::size_t aligned_size = *reinterpret_cast(raw - size_size); const std::size_t alloc_size = sizeof(AllocatorType) / sizeof(std::max_align_t) + (sizeof(AllocatorType) % sizeof(std::max_align_t) > 0 ? 1 : 0); auto alloc_p = reinterpret_cast(raw - alloc_size - size_size); auto alloc = std::move(*alloc_p); alloc_p->~alloc_type(); std::allocator_traits::deallocate(alloc, raw - alloc_size - size_size, aligned_size + alloc_size + size_size); } #endif template struct enable_await_allocator { auto await_transform(this_coro::allocator_t) { return allocator_awaitable_{static_cast(this)->get_allocator()}; } private: struct allocator_awaitable_ { using allocator_type = typename Promise::allocator_type; allocator_type alloc; constexpr static bool await_ready() { return true; } bool await_suspend( std::coroutine_handle ) { return false; } allocator_type await_resume() { return alloc; } }; }; template struct enable_await_executor { auto await_transform(this_coro::executor_t) { return executor_awaitable_{static_cast(this)->get_executor()}; } private: struct executor_awaitable_ { using executor_type = typename Promise::executor_type; executor_type exec; constexpr static bool await_ready() { return true; } bool await_suspend( std::coroutine_handle ) { return false; } executor_type await_resume() { return exec; } }; }; } #endif //BOOST_COBALT_THIS_CORO_HPP