promise.hpp 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. //
  2. // experimental/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_PROMISE_HPP
  12. #define BOOST_ASIO_EXPERIMENTAL_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/detail/type_traits.hpp>
  18. #include <boost/asio/any_io_executor.hpp>
  19. #include <boost/asio/associated_cancellation_slot.hpp>
  20. #include <boost/asio/associated_executor.hpp>
  21. #include <boost/asio/bind_executor.hpp>
  22. #include <boost/asio/cancellation_signal.hpp>
  23. #include <boost/asio/dispatch.hpp>
  24. #include <boost/asio/experimental/impl/promise.hpp>
  25. #include <boost/asio/post.hpp>
  26. #include <algorithm>
  27. #include <boost/asio/detail/push_options.hpp>
  28. namespace boost {
  29. namespace asio {
  30. namespace experimental {
  31. template <typename T>
  32. struct is_promise : std::false_type {};
  33. template <typename ... Ts>
  34. struct is_promise<promise<Ts...>> : std::true_type {};
  35. template <typename T>
  36. constexpr bool is_promise_v = is_promise<T>::value;
  37. template <typename ... Ts>
  38. struct promise_value_type
  39. {
  40. using type = std::tuple<Ts...>;
  41. };
  42. template <typename T>
  43. struct promise_value_type<T>
  44. {
  45. using type = T;
  46. };
  47. template <>
  48. struct promise_value_type<>
  49. {
  50. using type = std::tuple<>;
  51. };
  52. #if defined(GENERATING_DOCUMENTATION)
  53. /// A disposable handle for an eager operation.
  54. /**
  55. * @tparam Signature The signature of the operation.
  56. *
  57. * @tparam Executor The executor to be used by the promise (taken from the
  58. * operation).
  59. *
  60. * @tparam Allocator The allocator used for the promise. Can be set through
  61. * use_allocator.
  62. *
  63. * A promise can be used to initiate an asynchronous option that can be
  64. * completed later. If the promise gets destroyed before completion, the
  65. * operation gets a cancel signal and the result is ignored.
  66. *
  67. * A promise fulfills the requirements of async_operation.
  68. *
  69. * @par Examples
  70. * Reading and writing from one coroutine.
  71. * @code
  72. * awaitable<void> read_write_some(boost::asio::ip::tcp::socket & sock,
  73. * boost::asio::mutable_buffer read_buf, boost::asio::const_buffer to_write)
  74. * {
  75. * auto p = boost::asio::async_read(read_buf,
  76. * boost::asio::experimental::use_promise);
  77. * co_await boost::asio::async_write_some(to_write);
  78. * co_await p;
  79. * }
  80. * @endcode
  81. */
  82. template<typename Signature = void(),
  83. typename Executor = boost::asio::any_io_executor,
  84. typename Allocator = std::allocator<void>>
  85. struct promise
  86. #else
  87. template <typename ... Ts, typename Executor, typename Allocator>
  88. struct promise<void(Ts...), Executor, Allocator>
  89. #endif // defined(GENERATING_DOCUMENTATION)
  90. {
  91. /// The value that's returned by the promise.
  92. using value_type = typename promise_value_type<Ts...>::type;
  93. /// Cancel the promise. Usually done through the destructor.
  94. void cancel(cancellation_type level = cancellation_type::all)
  95. {
  96. if (impl_ && !impl_->done)
  97. {
  98. boost::asio::dispatch(impl_->executor,
  99. [level, impl = impl_]{ impl->cancel.emit(level); });
  100. }
  101. }
  102. /// Check if the promise is completed already.
  103. bool completed() const noexcept
  104. {
  105. return impl_ && impl_->done;
  106. }
  107. /// Wait for the promise to become ready.
  108. template <BOOST_ASIO_COMPLETION_TOKEN_FOR(void(Ts...)) CompletionToken>
  109. inline BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(Ts...))
  110. operator()(CompletionToken&& token)
  111. {
  112. assert(impl_);
  113. return async_initiate<CompletionToken, void(Ts...)>(
  114. initiate_async_wait{impl_}, token);
  115. }
  116. promise() = delete;
  117. promise(const promise& ) = delete;
  118. promise(promise&& ) noexcept = default;
  119. /// Destruct the promise and cancel the operation.
  120. /**
  121. * It is safe to destruct a promise of a promise that didn't complete.
  122. */
  123. ~promise() { cancel(); }
  124. private:
  125. #if !defined(GENERATING_DOCUMENTATION)
  126. template <typename, typename, typename> friend struct promise;
  127. friend struct detail::promise_handler<void(Ts...), Executor, Allocator>;
  128. #endif // !defined(GENERATING_DOCUMENTATION)
  129. std::shared_ptr<detail::promise_impl<
  130. void(Ts...), Executor, Allocator>> impl_;
  131. promise(
  132. std::shared_ptr<detail::promise_impl<
  133. void(Ts...), Executor, Allocator>> impl)
  134. : impl_(impl)
  135. {
  136. }
  137. struct initiate_async_wait
  138. {
  139. std::shared_ptr<detail::promise_impl<
  140. void(Ts...), Executor, Allocator>> self_;
  141. template <typename WaitHandler>
  142. void operator()(WaitHandler&& handler) const
  143. {
  144. const auto alloc = get_associated_allocator(
  145. handler, self_->get_allocator());
  146. auto cancel = get_associated_cancellation_slot(handler);
  147. if (self_->done)
  148. {
  149. auto exec = boost::asio::get_associated_executor(
  150. handler, self_->get_executor());
  151. boost::asio::post(exec,
  152. [self = std::move(self_),
  153. handler = std::forward<WaitHandler>(handler)]() mutable
  154. {
  155. self->apply(std::move(handler));
  156. });
  157. }
  158. else
  159. {
  160. if (cancel.is_connected())
  161. {
  162. struct cancel_handler
  163. {
  164. std::weak_ptr<detail::promise_impl<
  165. void(Ts...), Executor, Allocator>> self;
  166. cancel_handler(
  167. std::weak_ptr<detail::promise_impl<
  168. void(Ts...), Executor, Allocator>> self)
  169. : self(std::move(self))
  170. {
  171. }
  172. void operator()(cancellation_type level) const
  173. {
  174. if (auto p = self.lock())
  175. {
  176. p->cancel.emit(level);
  177. p->cancel_();
  178. }
  179. }
  180. };
  181. cancel.template emplace<cancel_handler>(self_);
  182. }
  183. self_->set_completion(alloc, std::forward<WaitHandler>(handler));
  184. }
  185. }
  186. };
  187. };
  188. } // namespace experimental
  189. } // namespace asio
  190. } // namespace boost
  191. #include <boost/asio/detail/pop_options.hpp>
  192. #endif // BOOST_ASIO_EXPERIMENTAL_PROMISE_HPP