// // experimental/impl/promise.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2021-2023 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_IMPL_PROMISE_HPP #define BOOST_ASIO_EXPERIMENTAL_IMPL_PROMISE_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include <boost/asio/detail/config.hpp> #include <boost/asio/cancellation_signal.hpp> #include <boost/asio/detail/utility.hpp> #include <boost/asio/error.hpp> #include <boost/system/system_error.hpp> #include <tuple> #include <boost/asio/detail/push_options.hpp> namespace boost { namespace asio { namespace experimental { template<typename Signature = void(), typename Executor = boost::asio::any_io_executor, typename Allocator = std::allocator<void>> struct promise; namespace detail { template<typename Signature, typename Executor, typename Allocator> struct promise_impl; template<typename... Ts, typename Executor, typename Allocator> struct promise_impl<void(Ts...), Executor, Allocator> { using result_type = std::tuple<Ts...>; promise_impl(Allocator allocator, Executor executor) : allocator(std::move(allocator)), executor(std::move(executor)) { } promise_impl(const promise_impl&) = delete; ~promise_impl() { if (completion) this->cancel_(); if (done) reinterpret_cast<result_type*>(&result)->~result_type(); } aligned_storage_t<sizeof(result_type), alignof(result_type)> result; std::atomic<bool> done{false}; cancellation_signal cancel; Allocator allocator; Executor executor; template<typename Func, std::size_t... Idx> void apply_impl(Func f, boost::asio::detail::index_sequence<Idx...>) { auto& result_type = *reinterpret_cast<promise_impl::result_type*>(&result); f(std::get<Idx>(std::move(result_type))...); } using allocator_type = Allocator; allocator_type get_allocator() {return allocator;} using executor_type = Executor; executor_type get_executor() {return executor;} template<typename Func> void apply(Func f) { apply_impl(std::forward<Func>(f), boost::asio::detail::make_index_sequence<sizeof...(Ts)>{}); } struct completion_base { virtual void invoke(Ts&&...ts) = 0; }; template<typename Alloc, typename WaitHandler_> struct completion_impl final : completion_base { WaitHandler_ handler; Alloc allocator; void invoke(Ts&&... ts) { auto h = std::move(handler); using alloc_t = typename std::allocator_traits< typename boost::asio::decay<Alloc>::type>::template rebind_alloc<completion_impl>; alloc_t alloc_{allocator}; this->~completion_impl(); std::allocator_traits<alloc_t>::deallocate(alloc_, this, 1u); std::move(h)(std::forward<Ts>(ts)...); } template<typename Alloc_, typename Handler_> completion_impl(Alloc_&& alloc, Handler_&& wh) : handler(std::forward<Handler_>(wh)), allocator(std::forward<Alloc_>(alloc)) { } }; completion_base* completion = nullptr; typename boost::asio::aligned_storage<sizeof(void*) * 4, alignof(completion_base)>::type completion_opt; template<typename Alloc, typename Handler> void set_completion(Alloc&& alloc, Handler&& handler) { if (completion) cancel_(); using impl_t = completion_impl< typename boost::asio::decay<Alloc>::type, Handler>; using alloc_t = typename std::allocator_traits< typename boost::asio::decay<Alloc>::type>::template rebind_alloc<impl_t>; alloc_t alloc_{alloc}; auto p = std::allocator_traits<alloc_t>::allocate(alloc_, 1u); completion = new (p) impl_t(std::forward<Alloc>(alloc), std::forward<Handler>(handler)); } template<typename... T_> void complete(T_&&... ts) { assert(completion); std::exchange(completion, nullptr)->invoke(std::forward<T_>(ts)...); } template<std::size_t... Idx> void complete_with_result_impl(boost::asio::detail::index_sequence<Idx...>) { auto& result_type = *reinterpret_cast<promise_impl::result_type*>(&result); this->complete(std::get<Idx>(std::move(result_type))...); } void complete_with_result() { complete_with_result_impl( boost::asio::detail::make_index_sequence<sizeof...(Ts)>{}); } template<typename... T_> void cancel_impl_(std::exception_ptr*, T_*...) { complete( std::make_exception_ptr( boost::system::system_error( boost::asio::error::operation_aborted)), T_{}...); } template<typename... T_> void cancel_impl_(boost::system::error_code*, T_*...) { complete(boost::asio::error::operation_aborted, T_{}...); } template<typename... T_> void cancel_impl_(T_*...) { complete(T_{}...); } void cancel_() { cancel_impl_(static_cast<Ts*>(nullptr)...); } }; template<typename Signature = void(), typename Executor = boost::asio::any_io_executor, typename Allocator = any_io_executor> struct promise_handler; template<typename... Ts, typename Executor, typename Allocator> struct promise_handler<void(Ts...), Executor, Allocator> { using promise_type = promise<void(Ts...), Executor, Allocator>; promise_handler( Allocator allocator, Executor executor) // get_associated_allocator(exec) : impl_( std::allocate_shared<promise_impl<void(Ts...), Executor, Allocator>>( allocator, allocator, executor)) { } std::shared_ptr<promise_impl<void(Ts...), Executor, Allocator>> impl_; using cancellation_slot_type = cancellation_slot; cancellation_slot_type get_cancellation_slot() const noexcept { return impl_->cancel.slot(); } using allocator_type = Allocator; allocator_type get_allocator() const noexcept { return impl_->get_allocator(); } using executor_type = Executor; Executor get_executor() const noexcept { return impl_->get_executor(); } auto make_promise() -> promise<void(Ts...), executor_type, allocator_type> { return promise<void(Ts...), executor_type, allocator_type>{impl_}; } void operator()(std::remove_reference_t<Ts>... ts) { assert(impl_); using result_type = typename promise_impl< void(Ts...), allocator_type, executor_type>::result_type ; new (&impl_->result) result_type(std::move(ts)...); impl_->done = true; if (impl_->completion) impl_->complete_with_result(); } }; } // namespace detail } // namespace experimental } // namespace asio } // namespace boost #include <boost/asio/detail/pop_options.hpp> #endif // BOOST_ASIO_EXPERIMENTAL_IMPL_PROMISE_HPP