co_spawn.hpp 11 KB

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