co_spawn.hpp 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. //
  2. // impl/co_spawn.hpp
  3. // ~~~~~~~~~~~~~~~~~
  4. //
  5. // Copyright (c) 2003-2022 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 ASIO_IMPL_CO_SPAWN_HPP
  11. #define ASIO_IMPL_CO_SPAWN_HPP
  12. #if defined(_MSC_VER) && (_MSC_VER >= 1200)
  13. # pragma once
  14. #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
  15. #include "asio/detail/config.hpp"
  16. #include "asio/associated_cancellation_slot.hpp"
  17. #include "asio/awaitable.hpp"
  18. #include "asio/dispatch.hpp"
  19. #include "asio/execution/outstanding_work.hpp"
  20. #include "asio/post.hpp"
  21. #include "asio/prefer.hpp"
  22. #include "asio/use_awaitable.hpp"
  23. #include "asio/detail/push_options.hpp"
  24. namespace asio {
  25. namespace detail {
  26. template <typename Executor, typename = void>
  27. class co_spawn_work_guard
  28. {
  29. public:
  30. typedef typename decay<
  31. typename prefer_result<Executor,
  32. execution::outstanding_work_t::tracked_t
  33. >::type
  34. >::type executor_type;
  35. co_spawn_work_guard(const Executor& ex)
  36. : executor_(asio::prefer(ex, execution::outstanding_work.tracked))
  37. {
  38. }
  39. executor_type get_executor() const ASIO_NOEXCEPT
  40. {
  41. return executor_;
  42. }
  43. private:
  44. executor_type executor_;
  45. };
  46. #if !defined(ASIO_NO_TS_EXECUTORS)
  47. template <typename Executor>
  48. struct co_spawn_work_guard<Executor,
  49. typename enable_if<
  50. !execution::is_executor<Executor>::value
  51. >::type> : executor_work_guard<Executor>
  52. {
  53. co_spawn_work_guard(const Executor& ex)
  54. : executor_work_guard<Executor>(ex)
  55. {
  56. }
  57. };
  58. #endif // !defined(ASIO_NO_TS_EXECUTORS)
  59. template <typename Executor>
  60. inline co_spawn_work_guard<Executor>
  61. make_co_spawn_work_guard(const Executor& ex)
  62. {
  63. return co_spawn_work_guard<Executor>(ex);
  64. }
  65. template <typename T, typename Executor, typename F, typename Handler>
  66. awaitable<awaitable_thread_entry_point, Executor> co_spawn_entry_point(
  67. awaitable<T, Executor>*, Executor ex, F f, Handler handler)
  68. {
  69. auto spawn_work = make_co_spawn_work_guard(ex);
  70. auto handler_work = make_co_spawn_work_guard(
  71. asio::get_associated_executor(handler, ex));
  72. (void) co_await (dispatch)(
  73. use_awaitable_t<Executor>{__FILE__, __LINE__, "co_spawn_entry_point"});
  74. (co_await awaitable_thread_has_context_switched{}) = false;
  75. std::exception_ptr e = nullptr;
  76. bool done = false;
  77. try
  78. {
  79. T t = co_await f();
  80. done = true;
  81. if (co_await awaitable_thread_has_context_switched{})
  82. {
  83. (dispatch)(handler_work.get_executor(),
  84. [handler = std::move(handler), t = std::move(t)]() mutable
  85. {
  86. std::move(handler)(std::exception_ptr(), std::move(t));
  87. });
  88. }
  89. else
  90. {
  91. (post)(handler_work.get_executor(),
  92. [handler = std::move(handler), t = std::move(t)]() mutable
  93. {
  94. std::move(handler)(std::exception_ptr(), std::move(t));
  95. });
  96. }
  97. co_return;
  98. }
  99. catch (...)
  100. {
  101. if (done)
  102. throw;
  103. e = std::current_exception();
  104. }
  105. if (co_await awaitable_thread_has_context_switched{})
  106. {
  107. (dispatch)(handler_work.get_executor(),
  108. [handler = std::move(handler), e]() mutable
  109. {
  110. std::move(handler)(e, T());
  111. });
  112. }
  113. else
  114. {
  115. (post)(handler_work.get_executor(),
  116. [handler = std::move(handler), e]() mutable
  117. {
  118. std::move(handler)(e, T());
  119. });
  120. }
  121. }
  122. template <typename Executor, typename F, typename Handler>
  123. awaitable<awaitable_thread_entry_point, Executor> co_spawn_entry_point(
  124. awaitable<void, Executor>*, Executor ex, F f, Handler handler)
  125. {
  126. auto spawn_work = make_co_spawn_work_guard(ex);
  127. auto handler_work = make_co_spawn_work_guard(
  128. asio::get_associated_executor(handler, ex));
  129. (void) co_await (dispatch)(
  130. use_awaitable_t<Executor>{__FILE__, __LINE__, "co_spawn_entry_point"});
  131. (co_await awaitable_thread_has_context_switched{}) = false;
  132. std::exception_ptr e = nullptr;
  133. try
  134. {
  135. co_await f();
  136. }
  137. catch (...)
  138. {
  139. e = std::current_exception();
  140. }
  141. if (co_await awaitable_thread_has_context_switched{})
  142. {
  143. (dispatch)(handler_work.get_executor(),
  144. [handler = std::move(handler), e]() mutable
  145. {
  146. std::move(handler)(e);
  147. });
  148. }
  149. else
  150. {
  151. (post)(handler_work.get_executor(),
  152. [handler = std::move(handler), e]() mutable
  153. {
  154. std::move(handler)(e);
  155. });
  156. }
  157. }
  158. template <typename T, typename Executor>
  159. class awaitable_as_function
  160. {
  161. public:
  162. explicit awaitable_as_function(awaitable<T, Executor>&& a)
  163. : awaitable_(std::move(a))
  164. {
  165. }
  166. awaitable<T, Executor> operator()()
  167. {
  168. return std::move(awaitable_);
  169. }
  170. private:
  171. awaitable<T, Executor> awaitable_;
  172. };
  173. template <typename Executor>
  174. class initiate_co_spawn
  175. {
  176. public:
  177. typedef Executor executor_type;
  178. template <typename OtherExecutor>
  179. explicit initiate_co_spawn(const OtherExecutor& ex)
  180. : ex_(ex)
  181. {
  182. }
  183. executor_type get_executor() const ASIO_NOEXCEPT
  184. {
  185. return ex_;
  186. }
  187. template <typename Handler, typename F>
  188. void operator()(Handler&& handler, F&& f) const
  189. {
  190. typedef typename result_of<F()>::type awaitable_type;
  191. cancellation_state proxy_cancel_state(
  192. asio::get_associated_cancellation_slot(handler),
  193. enable_total_cancellation());
  194. cancellation_state cancel_state(proxy_cancel_state.slot());
  195. auto a = (co_spawn_entry_point)(static_cast<awaitable_type*>(nullptr),
  196. ex_, std::forward<F>(f), std::forward<Handler>(handler));
  197. awaitable_handler<executor_type, void>(std::move(a), ex_,
  198. proxy_cancel_state.slot(), cancel_state).launch();
  199. }
  200. private:
  201. Executor ex_;
  202. };
  203. } // namespace detail
  204. template <typename Executor, typename T, typename AwaitableExecutor,
  205. ASIO_COMPLETION_TOKEN_FOR(
  206. void(std::exception_ptr, T)) CompletionToken>
  207. inline ASIO_INITFN_AUTO_RESULT_TYPE(
  208. CompletionToken, void(std::exception_ptr, T))
  209. co_spawn(const Executor& ex,
  210. awaitable<T, AwaitableExecutor> a, CompletionToken&& token,
  211. typename constraint<
  212. (is_executor<Executor>::value || execution::is_executor<Executor>::value)
  213. && is_convertible<Executor, AwaitableExecutor>::value
  214. >::type)
  215. {
  216. return async_initiate<CompletionToken, void(std::exception_ptr, T)>(
  217. detail::initiate_co_spawn<AwaitableExecutor>(AwaitableExecutor(ex)),
  218. token, detail::awaitable_as_function<T, AwaitableExecutor>(std::move(a)));
  219. }
  220. template <typename Executor, typename AwaitableExecutor,
  221. ASIO_COMPLETION_TOKEN_FOR(
  222. void(std::exception_ptr)) CompletionToken>
  223. inline ASIO_INITFN_AUTO_RESULT_TYPE(
  224. CompletionToken, void(std::exception_ptr))
  225. co_spawn(const Executor& ex,
  226. awaitable<void, AwaitableExecutor> a, CompletionToken&& token,
  227. typename constraint<
  228. (is_executor<Executor>::value || execution::is_executor<Executor>::value)
  229. && is_convertible<Executor, AwaitableExecutor>::value
  230. >::type)
  231. {
  232. return async_initiate<CompletionToken, void(std::exception_ptr)>(
  233. detail::initiate_co_spawn<AwaitableExecutor>(AwaitableExecutor(ex)),
  234. token, detail::awaitable_as_function<
  235. void, AwaitableExecutor>(std::move(a)));
  236. }
  237. template <typename ExecutionContext, typename T, typename AwaitableExecutor,
  238. ASIO_COMPLETION_TOKEN_FOR(
  239. void(std::exception_ptr, T)) CompletionToken>
  240. inline ASIO_INITFN_AUTO_RESULT_TYPE(
  241. CompletionToken, void(std::exception_ptr, T))
  242. co_spawn(ExecutionContext& ctx,
  243. awaitable<T, AwaitableExecutor> a, CompletionToken&& token,
  244. typename constraint<
  245. is_convertible<ExecutionContext&, execution_context&>::value
  246. && is_convertible<typename ExecutionContext::executor_type,
  247. AwaitableExecutor>::value
  248. >::type)
  249. {
  250. return (co_spawn)(ctx.get_executor(), std::move(a),
  251. std::forward<CompletionToken>(token));
  252. }
  253. template <typename ExecutionContext, typename AwaitableExecutor,
  254. ASIO_COMPLETION_TOKEN_FOR(
  255. void(std::exception_ptr)) CompletionToken>
  256. inline ASIO_INITFN_AUTO_RESULT_TYPE(
  257. CompletionToken, void(std::exception_ptr))
  258. co_spawn(ExecutionContext& ctx,
  259. awaitable<void, AwaitableExecutor> a, CompletionToken&& token,
  260. typename constraint<
  261. is_convertible<ExecutionContext&, execution_context&>::value
  262. && is_convertible<typename ExecutionContext::executor_type,
  263. AwaitableExecutor>::value
  264. >::type)
  265. {
  266. return (co_spawn)(ctx.get_executor(), std::move(a),
  267. std::forward<CompletionToken>(token));
  268. }
  269. template <typename Executor, typename F,
  270. ASIO_COMPLETION_TOKEN_FOR(typename detail::awaitable_signature<
  271. typename result_of<F()>::type>::type) CompletionToken>
  272. inline ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken,
  273. typename detail::awaitable_signature<typename result_of<F()>::type>::type)
  274. co_spawn(const Executor& ex, F&& f, CompletionToken&& token,
  275. typename constraint<
  276. is_executor<Executor>::value || execution::is_executor<Executor>::value
  277. >::type)
  278. {
  279. return async_initiate<CompletionToken,
  280. typename detail::awaitable_signature<typename result_of<F()>::type>::type>(
  281. detail::initiate_co_spawn<
  282. typename result_of<F()>::type::executor_type>(ex),
  283. token, std::forward<F>(f));
  284. }
  285. template <typename ExecutionContext, typename F,
  286. ASIO_COMPLETION_TOKEN_FOR(typename detail::awaitable_signature<
  287. typename result_of<F()>::type>::type) CompletionToken>
  288. inline ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken,
  289. typename detail::awaitable_signature<typename result_of<F()>::type>::type)
  290. co_spawn(ExecutionContext& ctx, F&& f, CompletionToken&& token,
  291. typename constraint<
  292. is_convertible<ExecutionContext&, execution_context&>::value
  293. >::type)
  294. {
  295. return (co_spawn)(ctx.get_executor(), std::forward<F>(f),
  296. std::forward<CompletionToken>(token));
  297. }
  298. } // namespace asio
  299. #include "asio/detail/pop_options.hpp"
  300. #endif // ASIO_IMPL_CO_SPAWN_HPP