promise.hpp 6.5 KB

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