co_spawn.hpp 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. //
  2. // experimental/co_spawn.hpp
  3. // ~~~~~~~~~~~~~~~~~~~~~~~~~
  4. //
  5. // Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com)
  6. //
  7. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  8. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  9. //
  10. #ifndef BOOST_ASIO_EXPERIMENTAL_CO_SPAWN_HPP
  11. #define BOOST_ASIO_EXPERIMENTAL_CO_SPAWN_HPP
  12. #if defined(_MSC_VER) && (_MSC_VER >= 1200)
  13. # pragma once
  14. #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
  15. #include <boost/asio/detail/config.hpp>
  16. #if defined(BOOST_ASIO_HAS_CO_AWAIT) || defined(GENERATING_DOCUMENTATION)
  17. #include <experimental/coroutine>
  18. #include <boost/asio/executor.hpp>
  19. #include <boost/asio/strand.hpp>
  20. #include <boost/asio/detail/push_options.hpp>
  21. namespace boost {
  22. namespace asio {
  23. namespace experimental {
  24. namespace detail {
  25. using std::experimental::coroutine_handle;
  26. template <typename> class awaiter;
  27. template <typename> class awaitee_base;
  28. template <typename, typename> class awaitee;
  29. template <typename, typename> class await_handler_base;
  30. template <typename Executor, typename F, typename CompletionToken>
  31. auto co_spawn(const Executor& ex, F&& f, CompletionToken&& token);
  32. } // namespace detail
  33. namespace this_coro {
  34. /// Awaitable type that returns a completion token for the current coroutine.
  35. struct token_t {};
  36. /// Awaitable object that returns a completion token for the current coroutine.
  37. constexpr inline token_t token() { return {}; }
  38. /// Awaitable type that returns the executor of the current coroutine.
  39. struct executor_t {};
  40. /// Awaitable object that returns the executor of the current coroutine.
  41. constexpr inline executor_t executor() { return {}; }
  42. } // namespace this_coro
  43. /// A completion token that represents the currently executing coroutine.
  44. /**
  45. * The await_token class is used to represent the currently executing
  46. * coroutine. An await_token may be passed as a handler to an asynchronous
  47. * operation. For example:
  48. *
  49. * @code awaitable<void> my_coroutine()
  50. * {
  51. * await_token token = co_await this_coro::token();
  52. * ...
  53. * std::size_t n = co_await my_socket.async_read_some(buffer, token);
  54. * ...
  55. * } @endcode
  56. *
  57. * The initiating function (async_read_some in the above example) suspends the
  58. * current coroutine. The coroutine is resumed when the asynchronous operation
  59. * completes, and the result of the operation is returned.
  60. */
  61. template <typename Executor>
  62. class await_token
  63. {
  64. public:
  65. /// The associated executor type.
  66. typedef Executor executor_type;
  67. /// Copy constructor.
  68. await_token(const await_token& other) noexcept
  69. : awaiter_(other.awaiter_)
  70. {
  71. }
  72. /// Move constructor.
  73. await_token(await_token&& other) noexcept
  74. : awaiter_(std::exchange(other.awaiter_, nullptr))
  75. {
  76. }
  77. /// Get the associated executor.
  78. executor_type get_executor() const noexcept
  79. {
  80. return awaiter_->get_executor();
  81. }
  82. private:
  83. // No assignment allowed.
  84. await_token& operator=(const await_token&) = delete;
  85. template <typename> friend class detail::awaitee_base;
  86. template <typename, typename> friend class detail::await_handler_base;
  87. // Private constructor used by awaitee_base.
  88. explicit await_token(detail::awaiter<Executor>* a)
  89. : awaiter_(a)
  90. {
  91. }
  92. detail::awaiter<Executor>* awaiter_;
  93. };
  94. /// The return type of a coroutine or asynchronous operation.
  95. template <typename T, typename Executor = strand<executor>>
  96. class awaitable
  97. {
  98. public:
  99. /// The type of the awaited value.
  100. typedef T value_type;
  101. /// The executor type that will be used for the coroutine.
  102. typedef Executor executor_type;
  103. /// Move constructor.
  104. awaitable(awaitable&& other) noexcept
  105. : awaitee_(std::exchange(other.awaitee_, nullptr))
  106. {
  107. }
  108. /// Destructor
  109. ~awaitable()
  110. {
  111. if (awaitee_)
  112. {
  113. detail::coroutine_handle<
  114. detail::awaitee<T, Executor>>::from_promise(
  115. *awaitee_).destroy();
  116. }
  117. }
  118. #if !defined(GENERATING_DOCUMENTATION)
  119. // Support for co_await keyword.
  120. bool await_ready() const noexcept
  121. {
  122. return awaitee_->ready();
  123. }
  124. // Support for co_await keyword.
  125. void await_suspend(detail::coroutine_handle<detail::awaiter<Executor>> h)
  126. {
  127. awaitee_->attach_caller(h);
  128. }
  129. // Support for co_await keyword.
  130. template <class U>
  131. void await_suspend(detail::coroutine_handle<detail::awaitee<U, Executor>> h)
  132. {
  133. awaitee_->attach_caller(h);
  134. }
  135. // Support for co_await keyword.
  136. T await_resume()
  137. {
  138. return awaitee_->get();
  139. }
  140. #endif // !defined(GENERATING_DOCUMENTATION)
  141. private:
  142. template <typename, typename> friend class detail::awaitee;
  143. template <typename, typename> friend class detail::await_handler_base;
  144. // Not copy constructible or copy assignable.
  145. awaitable(const awaitable&) = delete;
  146. awaitable& operator=(const awaitable&) = delete;
  147. // Construct the awaitable from a coroutine's promise object.
  148. explicit awaitable(detail::awaitee<T, Executor>* a) : awaitee_(a) {}
  149. detail::awaitee<T, Executor>* awaitee_;
  150. };
  151. /// Spawn a new thread of execution.
  152. template <typename Executor, typename F, typename CompletionToken,
  153. typename = typename enable_if<is_executor<Executor>::value>::type>
  154. inline auto co_spawn(const Executor& ex, F&& f, CompletionToken&& token)
  155. {
  156. return detail::co_spawn(ex, std::forward<F>(f),
  157. std::forward<CompletionToken>(token));
  158. }
  159. /// Spawn a new thread of execution.
  160. template <typename ExecutionContext, typename F, typename CompletionToken,
  161. typename = typename enable_if<
  162. is_convertible<ExecutionContext&, execution_context&>::value>::type>
  163. inline auto co_spawn(ExecutionContext& ctx, F&& f, CompletionToken&& token)
  164. {
  165. return detail::co_spawn(ctx.get_executor(), std::forward<F>(f),
  166. std::forward<CompletionToken>(token));
  167. }
  168. /// Spawn a new thread of execution.
  169. template <typename Executor, typename F, typename CompletionToken>
  170. inline auto co_spawn(const await_token<Executor>& parent,
  171. F&& f, CompletionToken&& token)
  172. {
  173. return detail::co_spawn(parent.get_executor(), std::forward<F>(f),
  174. std::forward<CompletionToken>(token));
  175. }
  176. } // namespace experimental
  177. } // namespace asio
  178. } // namespace boost
  179. #include <boost/asio/detail/pop_options.hpp>
  180. #include <boost/asio/experimental/impl/co_spawn.hpp>
  181. #endif // defined(BOOST_ASIO_HAS_CO_AWAIT) || defined(GENERATING_DOCUMENTATION)
  182. #endif // BOOST_ASIO_EXPERIMENTAL_CO_SPAWN_HPP