use_coro.hpp 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. //
  2. // experimental/impl/use_coro.hpp
  3. // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  4. //
  5. // Copyright (c) 2021-2022 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_USE_CORO_HPP
  12. #define ASIO_EXPERIMENTAL_IMPL_USE_CORO_HPP
  13. #if defined(_MSC_VER) && (_MSC_VER >= 1200)
  14. # pragma once
  15. #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
  16. #include "asio/detail/push_options.hpp"
  17. namespace asio {
  18. namespace experimental {
  19. template <typename Yield, typename Return, typename Executor>
  20. struct coro;
  21. namespace detail {
  22. template <typename Yield, typename Return, typename Executor>
  23. struct coro_promise;
  24. template <typename Executor, typename... Ts>
  25. struct coro_init_handler
  26. {
  27. struct handler_t
  28. {
  29. };
  30. constexpr static handler_t handler{};
  31. struct init_helper;
  32. struct promise_type
  33. {
  34. auto initial_suspend() noexcept { return suspend_always{}; }
  35. auto final_suspend() noexcept { return suspend_always(); }
  36. void return_void() {}
  37. void unhandled_exception() { assert(false); }
  38. auto await_transform(handler_t)
  39. {
  40. assert(executor);
  41. assert(h);
  42. return init_helper{this};
  43. }
  44. std::optional<Executor> executor;
  45. std::optional<std::tuple<Ts...>> result;
  46. coroutine_handle<> h;
  47. coro_init_handler get_return_object() { return coro_init_handler{this}; }
  48. cancellation_slot cancel_slot;
  49. };
  50. struct init_helper
  51. {
  52. promise_type *self_;
  53. constexpr static bool await_ready() noexcept { return true; }
  54. constexpr static void await_suspend(coroutine_handle<>) noexcept {}
  55. auto await_resume() const noexcept
  56. {
  57. assert(self_);
  58. return bind_cancellation_slot(self_->cancel_slot,
  59. bind_executor(*self_->executor, [self = self_](Ts... ts)
  60. {
  61. self->cancel_slot.clear();
  62. self->result.emplace(std::move(ts)...);
  63. self->h.resume();
  64. }));
  65. }
  66. };
  67. promise_type* promise;
  68. void unhandled_exception() noexcept
  69. {
  70. throw;
  71. }
  72. struct noexcept_version
  73. {
  74. promise_type *promise;
  75. constexpr static bool await_ready() noexcept { return false; }
  76. template <typename Yield, typename Return,
  77. convertible_to<Executor> Executor1>
  78. auto await_suspend(
  79. coroutine_handle<coro_promise<Yield, Return, Executor1> > h) noexcept
  80. {
  81. promise->executor = h.promise().get_executor();
  82. promise->h = h;
  83. return coroutine_handle<promise_type>::from_promise(*promise);
  84. }
  85. template <typename... Args>
  86. static auto resume_impl(std::tuple<Args...>&& tup)
  87. {
  88. return std::move(tup);
  89. }
  90. template <typename Arg>
  91. static auto resume_impl(std::tuple<Arg>&& tup)
  92. {
  93. return get<0>(std::move(tup));
  94. }
  95. static void resume_impl(std::tuple<>&&) {}
  96. auto await_resume() const noexcept
  97. {
  98. auto res = std::move(promise->result.value());
  99. coroutine_handle<promise_type>::from_promise(*promise).destroy();
  100. return resume_impl(std::move(res));
  101. }
  102. };
  103. struct throwing_version
  104. {
  105. promise_type *promise;
  106. constexpr static bool await_ready() noexcept { return false; }
  107. template <typename Yield, typename Return,
  108. convertible_to<Executor> Executor1>
  109. auto await_suspend(
  110. coroutine_handle<coro_promise<Yield, Return, Executor1> > h) noexcept
  111. {
  112. promise->executor = h.promise().get_executor();
  113. promise->h = h;
  114. return coroutine_handle<promise_type>::from_promise(*promise);
  115. }
  116. template <typename... Args>
  117. static auto resume_impl(std::tuple<Args...>&& tup)
  118. {
  119. return std::move(tup);
  120. }
  121. static void resume_impl(std::tuple<>&&) {}
  122. template <typename Arg>
  123. static auto resume_impl(std::tuple<Arg>&& tup)
  124. {
  125. return get<0>(std::move(tup));
  126. }
  127. template <typename... Args>
  128. static auto resume_impl(std::tuple<std::exception_ptr, Args...>&& tup)
  129. {
  130. auto ex = get<0>(std::move(tup));
  131. if (ex)
  132. std::rethrow_exception(ex);
  133. if constexpr (sizeof...(Args) == 0u)
  134. return;
  135. else if constexpr (sizeof...(Args) == 1u)
  136. return get<1>(std::move(tup));
  137. else
  138. {
  139. return
  140. [&]<std::size_t... Idx>(std::index_sequence<Idx...>)
  141. {
  142. return std::make_tuple(std::get<Idx + 1>(std::move(tup))...);
  143. }(std::make_index_sequence<sizeof...(Args) - 1>{});
  144. }
  145. }
  146. template <typename... Args>
  147. static auto resume_impl(
  148. std::tuple<asio::error_code, Args...>&& tup)
  149. {
  150. auto ec = get<0>(std::move(tup));
  151. if (ec)
  152. asio::detail::throw_exception(
  153. asio::system_error(ec, "error_code in use_coro"));
  154. if constexpr (sizeof...(Args) == 0u)
  155. return;
  156. else if constexpr (sizeof...(Args) == 1u)
  157. return get<1>(std::move(tup));
  158. else
  159. return
  160. [&]<std::size_t... Idx>(std::index_sequence<Idx...>)
  161. {
  162. return std::make_tuple(std::get<Idx + 1>(std::move(tup))...);
  163. }(std::make_index_sequence<sizeof...(Args) - 1>{});
  164. }
  165. static auto resume_impl(std::tuple<std::exception_ptr>&& tup)
  166. {
  167. auto ex = get<0>(std::move(tup));
  168. if (ex)
  169. std::rethrow_exception(ex);
  170. }
  171. static auto resume_impl(
  172. std::tuple<asio::error_code>&& tup)
  173. {
  174. auto ec = get<0>(std::move(tup));
  175. if (ec)
  176. asio::detail::throw_error(ec, "error_code in use_coro");
  177. }
  178. auto await_resume() const
  179. {
  180. auto res = std::move(promise->result.value());
  181. coroutine_handle<promise_type>::from_promise(*promise).destroy();
  182. return resume_impl(std::move(res));
  183. }
  184. };
  185. auto as_noexcept(cancellation_slot&& sl) && noexcept
  186. {
  187. promise->cancel_slot = std::move(sl);
  188. return noexcept_version{promise};
  189. }
  190. auto as_throwing(cancellation_slot&& sl) && noexcept
  191. {
  192. promise->cancel_slot = std::move(sl);
  193. return throwing_version{promise};
  194. }
  195. };
  196. } // namespace detail
  197. } // namespace experimental
  198. #if !defined(GENERATING_DOCUMENTATION)
  199. template <typename Executor, typename R, typename... Args>
  200. struct async_result<experimental::use_coro_t<Executor>, R(Args...)>
  201. {
  202. using return_type = experimental::detail::coro_init_handler<
  203. Executor, typename decay<Args>::type...>;
  204. template <typename Initiation, typename... InitArgs>
  205. static return_type initiate(Initiation initiation,
  206. experimental::use_coro_t<Executor>, InitArgs... args)
  207. {
  208. std::move(initiation)(co_await return_type::handler, std::move(args)...);
  209. }
  210. };
  211. #endif // !defined(GENERATING_DOCUMENTATION)
  212. } // namespace asio
  213. #include "asio/detail/pop_options.hpp"
  214. #endif // ASIO_EXPERIMENTAL_IMPL_USE_CORO_HPP