promise.hpp 6.3 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, boost::asio::use_awaitable);
  76. * co_await boost::asio::async_write_some(to_write, boost::asio::deferred);
  77. * co_await p;
  78. * }
  79. * @endcode
  80. */
  81. template<typename Signature = void(),
  82. typename Executor = boost::asio::any_io_executor,
  83. typename Allocator = std::allocator<void>>
  84. struct promise
  85. #else
  86. template <typename ... Ts, typename Executor, typename Allocator>
  87. struct promise<void(Ts...), Executor, Allocator>
  88. #endif // defined(GENERATING_DOCUMENTATION)
  89. {
  90. /// The value that's returned by the promise.
  91. using value_type = typename promise_value_type<Ts...>::type;
  92. /// Cancel the promise. Usually done through the destructor.
  93. void cancel(cancellation_type level = cancellation_type::all)
  94. {
  95. if (impl_ && !impl_->done)
  96. {
  97. boost::asio::dispatch(impl_->executor,
  98. [level, impl = impl_]{ impl->cancel.emit(level); });
  99. }
  100. }
  101. /// Check if the promise is completed already.
  102. bool completed() const noexcept
  103. {
  104. return impl_ && impl_->done;
  105. }
  106. /// Wait for the promise to become ready.
  107. template <BOOST_ASIO_COMPLETION_TOKEN_FOR(void(Ts...)) CompletionToken>
  108. inline BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(Ts...))
  109. operator()(CompletionToken&& token)
  110. {
  111. assert(impl_);
  112. return async_initiate<CompletionToken, void(Ts...)>(
  113. initiate_async_wait{impl_}, token);
  114. }
  115. promise() = delete;
  116. promise(const promise& ) = delete;
  117. promise(promise&& ) noexcept = default;
  118. /// Destruct the promise and cancel the operation.
  119. /**
  120. * It is safe to destruct a promise of a promise that didn't complete.
  121. */
  122. ~promise() { cancel(); }
  123. private:
  124. #if !defined(GENERATING_DOCUMENTATION)
  125. template <typename, typename, typename> friend struct promise;
  126. friend struct detail::promise_handler<void(Ts...), Executor, Allocator>;
  127. #endif // !defined(GENERATING_DOCUMENTATION)
  128. std::shared_ptr<detail::promise_impl<
  129. void(Ts...), Executor, Allocator>> impl_;
  130. promise(
  131. std::shared_ptr<detail::promise_impl<
  132. void(Ts...), Executor, Allocator>> impl)
  133. : impl_(impl)
  134. {
  135. }
  136. struct initiate_async_wait
  137. {
  138. std::shared_ptr<detail::promise_impl<
  139. void(Ts...), Executor, Allocator>> self_;
  140. template <typename WaitHandler>
  141. void operator()(WaitHandler&& handler) const
  142. {
  143. const auto alloc = get_associated_allocator(
  144. handler, self_->get_allocator());
  145. auto cancel = get_associated_cancellation_slot(handler);
  146. if (self_->done)
  147. {
  148. auto exec = boost::asio::get_associated_executor(
  149. handler, self_->get_executor());
  150. boost::asio::post(exec,
  151. [self = std::move(self_),
  152. handler = std::forward<WaitHandler>(handler)]() mutable
  153. {
  154. self->apply(std::move(handler));
  155. });
  156. }
  157. else
  158. {
  159. if (cancel.is_connected())
  160. {
  161. struct cancel_handler
  162. {
  163. std::weak_ptr<detail::promise_impl<
  164. void(Ts...), Executor, Allocator>> self;
  165. cancel_handler(
  166. std::weak_ptr<detail::promise_impl<
  167. void(Ts...), Executor, Allocator>> self)
  168. : self(std::move(self))
  169. {
  170. }
  171. void operator()(cancellation_type level) const
  172. {
  173. if (auto p = self.lock())
  174. {
  175. p->cancel.emit(level);
  176. p->cancel_();
  177. }
  178. }
  179. };
  180. cancel.template emplace<cancel_handler>(self_);
  181. }
  182. self_->set_completion(alloc, std::forward<WaitHandler>(handler));
  183. }
  184. }
  185. };
  186. };
  187. } // namespace experimental
  188. } // namespace asio
  189. } // namespace boost
  190. #include <boost/asio/detail/pop_options.hpp>
  191. #endif // BOOST_ASIO_EXPERIMENTAL_PROMISE_HPP