255 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			255 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			C++
		
	
	
	
| //
 | |
| // 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 ASIO_EXPERIMENTAL_IMPL_PROMISE_HPP
 | |
| #define ASIO_EXPERIMENTAL_IMPL_PROMISE_HPP
 | |
| 
 | |
| #if defined(_MSC_VER) && (_MSC_VER >= 1200)
 | |
| # pragma once
 | |
| #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
 | |
| 
 | |
| #include "asio/detail/config.hpp"
 | |
| #include "asio/cancellation_signal.hpp"
 | |
| #include "asio/detail/utility.hpp"
 | |
| #include <tuple>
 | |
| 
 | |
| #include "asio/detail/push_options.hpp"
 | |
| 
 | |
| namespace asio {
 | |
| namespace experimental {
 | |
| 
 | |
| template<typename Signature = void(),
 | |
|     typename Executor = 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();
 | |
|   }
 | |
| 
 | |
|   typename aligned_storage<sizeof(result_type),
 | |
|     alignof(result_type)>::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, 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),
 | |
|         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 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 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 asio::decay<Alloc>::type, Handler>;
 | |
|     using alloc_t = typename std::allocator_traits<
 | |
|       typename 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(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(
 | |
|         asio::detail::make_index_sequence<sizeof...(Ts)>{});
 | |
|   }
 | |
| 
 | |
|   template<typename... T_>
 | |
|   void cancel_impl_(std::exception_ptr*, T_*...)
 | |
|   {
 | |
|     complete(
 | |
|         std::make_exception_ptr(
 | |
|           asio::system_error(
 | |
|             asio::error::operation_aborted)),
 | |
|         T_{}...);
 | |
|   }
 | |
| 
 | |
|   template<typename... T_>
 | |
|   void cancel_impl_(asio::error_code*, T_*...)
 | |
|   {
 | |
|     complete(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 = 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
 | |
| 
 | |
| #include "asio/detail/pop_options.hpp"
 | |
| 
 | |
| #endif // ASIO_EXPERIMENTAL_IMPL_PROMISE_HPP
 |