promise.hpp 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. //
  2. // experimental/impl/promise.hpp
  3. // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  4. //
  5. // Copyright (c) 2021-2023 Klemens D. Morgenstern
  6. // (klemens dot morgenstern at gmx dot net)
  7. //
  8. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  9. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  10. //
  11. #ifndef BOOST_ASIO_EXPERIMENTAL_IMPL_PROMISE_HPP
  12. #define BOOST_ASIO_EXPERIMENTAL_IMPL_PROMISE_HPP
  13. #if defined(_MSC_VER) && (_MSC_VER >= 1200)
  14. # pragma once
  15. #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
  16. #include <boost/asio/detail/config.hpp>
  17. #include <boost/asio/cancellation_signal.hpp>
  18. #include <boost/asio/detail/utility.hpp>
  19. #include <boost/asio/error.hpp>
  20. #include <boost/system/system_error.hpp>
  21. #include <tuple>
  22. #include <boost/asio/detail/push_options.hpp>
  23. namespace boost {
  24. namespace asio {
  25. namespace experimental {
  26. template<typename Signature = void(),
  27. typename Executor = boost::asio::any_io_executor,
  28. typename Allocator = std::allocator<void>>
  29. struct promise;
  30. namespace detail {
  31. template<typename Signature, typename Executor, typename Allocator>
  32. struct promise_impl;
  33. template<typename... Ts, typename Executor, typename Allocator>
  34. struct promise_impl<void(Ts...), Executor, Allocator>
  35. {
  36. using result_type = std::tuple<Ts...>;
  37. promise_impl(Allocator allocator, Executor executor)
  38. : allocator(std::move(allocator)), executor(std::move(executor))
  39. {
  40. }
  41. promise_impl(const promise_impl&) = delete;
  42. ~promise_impl()
  43. {
  44. if (completion)
  45. this->cancel_();
  46. if (done)
  47. reinterpret_cast<result_type*>(&result)->~result_type();
  48. }
  49. aligned_storage_t<sizeof(result_type), alignof(result_type)> result;
  50. std::atomic<bool> done{false};
  51. cancellation_signal cancel;
  52. Allocator allocator;
  53. Executor executor;
  54. template<typename Func, std::size_t... Idx>
  55. void apply_impl(Func f, boost::asio::detail::index_sequence<Idx...>)
  56. {
  57. auto& result_type = *reinterpret_cast<promise_impl::result_type*>(&result);
  58. f(std::get<Idx>(std::move(result_type))...);
  59. }
  60. using allocator_type = Allocator;
  61. allocator_type get_allocator() {return allocator;}
  62. using executor_type = Executor;
  63. executor_type get_executor() {return executor;}
  64. template<typename Func>
  65. void apply(Func f)
  66. {
  67. apply_impl(std::forward<Func>(f),
  68. boost::asio::detail::make_index_sequence<sizeof...(Ts)>{});
  69. }
  70. struct completion_base
  71. {
  72. virtual void invoke(Ts&&...ts) = 0;
  73. };
  74. template<typename Alloc, typename WaitHandler_>
  75. struct completion_impl final : completion_base
  76. {
  77. WaitHandler_ handler;
  78. Alloc allocator;
  79. void invoke(Ts&&... ts)
  80. {
  81. auto h = std::move(handler);
  82. using alloc_t = typename std::allocator_traits<
  83. typename boost::asio::decay<Alloc>::type>::template
  84. rebind_alloc<completion_impl>;
  85. alloc_t alloc_{allocator};
  86. this->~completion_impl();
  87. std::allocator_traits<alloc_t>::deallocate(alloc_, this, 1u);
  88. std::move(h)(std::forward<Ts>(ts)...);
  89. }
  90. template<typename Alloc_, typename Handler_>
  91. completion_impl(Alloc_&& alloc, Handler_&& wh)
  92. : handler(std::forward<Handler_>(wh)),
  93. allocator(std::forward<Alloc_>(alloc))
  94. {
  95. }
  96. };
  97. completion_base* completion = nullptr;
  98. typename boost::asio::aligned_storage<sizeof(void*) * 4,
  99. alignof(completion_base)>::type completion_opt;
  100. template<typename Alloc, typename Handler>
  101. void set_completion(Alloc&& alloc, Handler&& handler)
  102. {
  103. if (completion)
  104. cancel_();
  105. using impl_t = completion_impl<
  106. typename boost::asio::decay<Alloc>::type, Handler>;
  107. using alloc_t = typename std::allocator_traits<
  108. typename boost::asio::decay<Alloc>::type>::template rebind_alloc<impl_t>;
  109. alloc_t alloc_{alloc};
  110. auto p = std::allocator_traits<alloc_t>::allocate(alloc_, 1u);
  111. completion = new (p) impl_t(std::forward<Alloc>(alloc),
  112. std::forward<Handler>(handler));
  113. }
  114. template<typename... T_>
  115. void complete(T_&&... ts)
  116. {
  117. assert(completion);
  118. std::exchange(completion, nullptr)->invoke(std::forward<T_>(ts)...);
  119. }
  120. template<std::size_t... Idx>
  121. void complete_with_result_impl(boost::asio::detail::index_sequence<Idx...>)
  122. {
  123. auto& result_type = *reinterpret_cast<promise_impl::result_type*>(&result);
  124. this->complete(std::get<Idx>(std::move(result_type))...);
  125. }
  126. void complete_with_result()
  127. {
  128. complete_with_result_impl(
  129. boost::asio::detail::make_index_sequence<sizeof...(Ts)>{});
  130. }
  131. template<typename... T_>
  132. void cancel_impl_(std::exception_ptr*, T_*...)
  133. {
  134. complete(
  135. std::make_exception_ptr(
  136. boost::system::system_error(
  137. boost::asio::error::operation_aborted)),
  138. T_{}...);
  139. }
  140. template<typename... T_>
  141. void cancel_impl_(boost::system::error_code*, T_*...)
  142. {
  143. complete(boost::asio::error::operation_aborted, T_{}...);
  144. }
  145. template<typename... T_>
  146. void cancel_impl_(T_*...)
  147. {
  148. complete(T_{}...);
  149. }
  150. void cancel_()
  151. {
  152. cancel_impl_(static_cast<Ts*>(nullptr)...);
  153. }
  154. };
  155. template<typename Signature = void(),
  156. typename Executor = boost::asio::any_io_executor,
  157. typename Allocator = any_io_executor>
  158. struct promise_handler;
  159. template<typename... Ts, typename Executor, typename Allocator>
  160. struct promise_handler<void(Ts...), Executor, Allocator>
  161. {
  162. using promise_type = promise<void(Ts...), Executor, Allocator>;
  163. promise_handler(
  164. Allocator allocator, Executor executor) // get_associated_allocator(exec)
  165. : impl_(
  166. std::allocate_shared<promise_impl<void(Ts...), Executor, Allocator>>(
  167. allocator, allocator, executor))
  168. {
  169. }
  170. std::shared_ptr<promise_impl<void(Ts...), Executor, Allocator>> impl_;
  171. using cancellation_slot_type = cancellation_slot;
  172. cancellation_slot_type get_cancellation_slot() const noexcept
  173. {
  174. return impl_->cancel.slot();
  175. }
  176. using allocator_type = Allocator;
  177. allocator_type get_allocator() const noexcept
  178. {
  179. return impl_->get_allocator();
  180. }
  181. using executor_type = Executor;
  182. Executor get_executor() const noexcept
  183. {
  184. return impl_->get_executor();
  185. }
  186. auto make_promise() -> promise<void(Ts...), executor_type, allocator_type>
  187. {
  188. return promise<void(Ts...), executor_type, allocator_type>{impl_};
  189. }
  190. void operator()(std::remove_reference_t<Ts>... ts)
  191. {
  192. assert(impl_);
  193. using result_type = typename promise_impl<
  194. void(Ts...), allocator_type, executor_type>::result_type ;
  195. new (&impl_->result) result_type(std::move(ts)...);
  196. impl_->done = true;
  197. if (impl_->completion)
  198. impl_->complete_with_result();
  199. }
  200. };
  201. } // namespace detail
  202. } // namespace experimental
  203. } // namespace asio
  204. } // namespace boost
  205. #include <boost/asio/detail/pop_options.hpp>
  206. #endif // BOOST_ASIO_EXPERIMENTAL_IMPL_PROMISE_HPP