spawn.hpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  1. //
  2. // impl/spawn.hpp
  3. // ~~~~~~~~~~~~~~
  4. //
  5. // Copyright (c) 2003-2019 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_SPAWN_HPP
  11. #define BOOST_ASIO_IMPL_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_allocator.hpp>
  17. #include <boost/asio/associated_executor.hpp>
  18. #include <boost/asio/async_result.hpp>
  19. #include <boost/asio/bind_executor.hpp>
  20. #include <boost/asio/detail/atomic_count.hpp>
  21. #include <boost/asio/detail/handler_alloc_helpers.hpp>
  22. #include <boost/asio/detail/handler_cont_helpers.hpp>
  23. #include <boost/asio/detail/handler_invoke_helpers.hpp>
  24. #include <boost/asio/detail/memory.hpp>
  25. #include <boost/asio/detail/noncopyable.hpp>
  26. #include <boost/asio/detail/type_traits.hpp>
  27. #include <boost/system/system_error.hpp>
  28. #include <boost/asio/detail/push_options.hpp>
  29. namespace boost {
  30. namespace asio {
  31. namespace detail {
  32. template <typename Handler, typename T>
  33. class coro_handler
  34. {
  35. public:
  36. coro_handler(basic_yield_context<Handler> ctx)
  37. : coro_(ctx.coro_.lock()),
  38. ca_(ctx.ca_),
  39. handler_(ctx.handler_),
  40. ready_(0),
  41. ec_(ctx.ec_),
  42. value_(0)
  43. {
  44. }
  45. void operator()(T value)
  46. {
  47. *ec_ = boost::system::error_code();
  48. *value_ = BOOST_ASIO_MOVE_CAST(T)(value);
  49. if (--*ready_ == 0)
  50. (*coro_)();
  51. }
  52. void operator()(boost::system::error_code ec, T value)
  53. {
  54. *ec_ = ec;
  55. *value_ = BOOST_ASIO_MOVE_CAST(T)(value);
  56. if (--*ready_ == 0)
  57. (*coro_)();
  58. }
  59. //private:
  60. shared_ptr<typename basic_yield_context<Handler>::callee_type> coro_;
  61. typename basic_yield_context<Handler>::caller_type& ca_;
  62. Handler handler_;
  63. atomic_count* ready_;
  64. boost::system::error_code* ec_;
  65. T* value_;
  66. };
  67. template <typename Handler>
  68. class coro_handler<Handler, void>
  69. {
  70. public:
  71. coro_handler(basic_yield_context<Handler> ctx)
  72. : coro_(ctx.coro_.lock()),
  73. ca_(ctx.ca_),
  74. handler_(ctx.handler_),
  75. ready_(0),
  76. ec_(ctx.ec_)
  77. {
  78. }
  79. void operator()()
  80. {
  81. *ec_ = boost::system::error_code();
  82. if (--*ready_ == 0)
  83. (*coro_)();
  84. }
  85. void operator()(boost::system::error_code ec)
  86. {
  87. *ec_ = ec;
  88. if (--*ready_ == 0)
  89. (*coro_)();
  90. }
  91. //private:
  92. shared_ptr<typename basic_yield_context<Handler>::callee_type> coro_;
  93. typename basic_yield_context<Handler>::caller_type& ca_;
  94. Handler handler_;
  95. atomic_count* ready_;
  96. boost::system::error_code* ec_;
  97. };
  98. template <typename Handler, typename T>
  99. inline void* asio_handler_allocate(std::size_t size,
  100. coro_handler<Handler, T>* this_handler)
  101. {
  102. return boost_asio_handler_alloc_helpers::allocate(
  103. size, this_handler->handler_);
  104. }
  105. template <typename Handler, typename T>
  106. inline void asio_handler_deallocate(void* pointer, std::size_t size,
  107. coro_handler<Handler, T>* this_handler)
  108. {
  109. boost_asio_handler_alloc_helpers::deallocate(
  110. pointer, size, this_handler->handler_);
  111. }
  112. template <typename Handler, typename T>
  113. inline bool asio_handler_is_continuation(coro_handler<Handler, T>*)
  114. {
  115. return true;
  116. }
  117. template <typename Function, typename Handler, typename T>
  118. inline void asio_handler_invoke(Function& function,
  119. coro_handler<Handler, T>* this_handler)
  120. {
  121. boost_asio_handler_invoke_helpers::invoke(
  122. function, this_handler->handler_);
  123. }
  124. template <typename Function, typename Handler, typename T>
  125. inline void asio_handler_invoke(const Function& function,
  126. coro_handler<Handler, T>* this_handler)
  127. {
  128. boost_asio_handler_invoke_helpers::invoke(
  129. function, this_handler->handler_);
  130. }
  131. template <typename Handler, typename T>
  132. class coro_async_result
  133. {
  134. public:
  135. typedef coro_handler<Handler, T> completion_handler_type;
  136. typedef T return_type;
  137. explicit coro_async_result(completion_handler_type& h)
  138. : handler_(h),
  139. ca_(h.ca_),
  140. ready_(2)
  141. {
  142. h.ready_ = &ready_;
  143. out_ec_ = h.ec_;
  144. if (!out_ec_) h.ec_ = &ec_;
  145. h.value_ = &value_;
  146. }
  147. return_type get()
  148. {
  149. // Must not hold shared_ptr to coro while suspended.
  150. handler_.coro_.reset();
  151. if (--ready_ != 0)
  152. ca_();
  153. if (!out_ec_ && ec_) throw boost::system::system_error(ec_);
  154. return BOOST_ASIO_MOVE_CAST(return_type)(value_);
  155. }
  156. private:
  157. completion_handler_type& handler_;
  158. typename basic_yield_context<Handler>::caller_type& ca_;
  159. atomic_count ready_;
  160. boost::system::error_code* out_ec_;
  161. boost::system::error_code ec_;
  162. return_type value_;
  163. };
  164. template <typename Handler>
  165. class coro_async_result<Handler, void>
  166. {
  167. public:
  168. typedef coro_handler<Handler, void> completion_handler_type;
  169. typedef void return_type;
  170. explicit coro_async_result(completion_handler_type& h)
  171. : handler_(h),
  172. ca_(h.ca_),
  173. ready_(2)
  174. {
  175. h.ready_ = &ready_;
  176. out_ec_ = h.ec_;
  177. if (!out_ec_) h.ec_ = &ec_;
  178. }
  179. void get()
  180. {
  181. // Must not hold shared_ptr to coro while suspended.
  182. handler_.coro_.reset();
  183. if (--ready_ != 0)
  184. ca_();
  185. if (!out_ec_ && ec_) throw boost::system::system_error(ec_);
  186. }
  187. private:
  188. completion_handler_type& handler_;
  189. typename basic_yield_context<Handler>::caller_type& ca_;
  190. atomic_count ready_;
  191. boost::system::error_code* out_ec_;
  192. boost::system::error_code ec_;
  193. };
  194. } // namespace detail
  195. #if !defined(GENERATING_DOCUMENTATION)
  196. template <typename Handler, typename ReturnType>
  197. class async_result<basic_yield_context<Handler>, ReturnType()>
  198. : public detail::coro_async_result<Handler, void>
  199. {
  200. public:
  201. explicit async_result(
  202. typename detail::coro_async_result<Handler,
  203. void>::completion_handler_type& h)
  204. : detail::coro_async_result<Handler, void>(h)
  205. {
  206. }
  207. };
  208. template <typename Handler, typename ReturnType, typename Arg1>
  209. class async_result<basic_yield_context<Handler>, ReturnType(Arg1)>
  210. : public detail::coro_async_result<Handler, typename decay<Arg1>::type>
  211. {
  212. public:
  213. explicit async_result(
  214. typename detail::coro_async_result<Handler,
  215. typename decay<Arg1>::type>::completion_handler_type& h)
  216. : detail::coro_async_result<Handler, typename decay<Arg1>::type>(h)
  217. {
  218. }
  219. };
  220. template <typename Handler, typename ReturnType>
  221. class async_result<basic_yield_context<Handler>,
  222. ReturnType(boost::system::error_code)>
  223. : public detail::coro_async_result<Handler, void>
  224. {
  225. public:
  226. explicit async_result(
  227. typename detail::coro_async_result<Handler,
  228. void>::completion_handler_type& h)
  229. : detail::coro_async_result<Handler, void>(h)
  230. {
  231. }
  232. };
  233. template <typename Handler, typename ReturnType, typename Arg2>
  234. class async_result<basic_yield_context<Handler>,
  235. ReturnType(boost::system::error_code, Arg2)>
  236. : public detail::coro_async_result<Handler, typename decay<Arg2>::type>
  237. {
  238. public:
  239. explicit async_result(
  240. typename detail::coro_async_result<Handler,
  241. typename decay<Arg2>::type>::completion_handler_type& h)
  242. : detail::coro_async_result<Handler, typename decay<Arg2>::type>(h)
  243. {
  244. }
  245. };
  246. template <typename Handler, typename T, typename Allocator>
  247. struct associated_allocator<detail::coro_handler<Handler, T>, Allocator>
  248. {
  249. typedef typename associated_allocator<Handler, Allocator>::type type;
  250. static type get(const detail::coro_handler<Handler, T>& h,
  251. const Allocator& a = Allocator()) BOOST_ASIO_NOEXCEPT
  252. {
  253. return associated_allocator<Handler, Allocator>::get(h.handler_, a);
  254. }
  255. };
  256. template <typename Handler, typename T, typename Executor>
  257. struct associated_executor<detail::coro_handler<Handler, T>, Executor>
  258. {
  259. typedef typename associated_executor<Handler, Executor>::type type;
  260. static type get(const detail::coro_handler<Handler, T>& h,
  261. const Executor& ex = Executor()) BOOST_ASIO_NOEXCEPT
  262. {
  263. return associated_executor<Handler, Executor>::get(h.handler_, ex);
  264. }
  265. };
  266. namespace detail {
  267. template <typename Handler, typename Function>
  268. struct spawn_data : private noncopyable
  269. {
  270. template <typename Hand, typename Func>
  271. spawn_data(BOOST_ASIO_MOVE_ARG(Hand) handler,
  272. bool call_handler, BOOST_ASIO_MOVE_ARG(Func) function)
  273. : handler_(BOOST_ASIO_MOVE_CAST(Hand)(handler)),
  274. call_handler_(call_handler),
  275. function_(BOOST_ASIO_MOVE_CAST(Func)(function))
  276. {
  277. }
  278. weak_ptr<typename basic_yield_context<Handler>::callee_type> coro_;
  279. Handler handler_;
  280. bool call_handler_;
  281. Function function_;
  282. };
  283. template <typename Handler, typename Function>
  284. struct coro_entry_point
  285. {
  286. void operator()(typename basic_yield_context<Handler>::caller_type& ca)
  287. {
  288. shared_ptr<spawn_data<Handler, Function> > data(data_);
  289. #if !defined(BOOST_COROUTINES_UNIDIRECT) && !defined(BOOST_COROUTINES_V2)
  290. ca(); // Yield until coroutine pointer has been initialised.
  291. #endif // !defined(BOOST_COROUTINES_UNIDIRECT) && !defined(BOOST_COROUTINES_V2)
  292. const basic_yield_context<Handler> yield(
  293. data->coro_, ca, data->handler_);
  294. (data->function_)(yield);
  295. if (data->call_handler_)
  296. (data->handler_)();
  297. }
  298. shared_ptr<spawn_data<Handler, Function> > data_;
  299. };
  300. template <typename Handler, typename Function>
  301. struct spawn_helper
  302. {
  303. void operator()()
  304. {
  305. typedef typename basic_yield_context<Handler>::callee_type callee_type;
  306. coro_entry_point<Handler, Function> entry_point = { data_ };
  307. shared_ptr<callee_type> coro(new callee_type(entry_point, attributes_));
  308. data_->coro_ = coro;
  309. (*coro)();
  310. }
  311. shared_ptr<spawn_data<Handler, Function> > data_;
  312. boost::coroutines::attributes attributes_;
  313. };
  314. template <typename Function, typename Handler, typename Function1>
  315. inline void asio_handler_invoke(Function& function,
  316. spawn_helper<Handler, Function1>* this_handler)
  317. {
  318. boost_asio_handler_invoke_helpers::invoke(
  319. function, this_handler->data_->handler_);
  320. }
  321. template <typename Function, typename Handler, typename Function1>
  322. inline void asio_handler_invoke(const Function& function,
  323. spawn_helper<Handler, Function1>* this_handler)
  324. {
  325. boost_asio_handler_invoke_helpers::invoke(
  326. function, this_handler->data_->handler_);
  327. }
  328. inline void default_spawn_handler() {}
  329. } // namespace detail
  330. template <typename Function>
  331. inline void spawn(BOOST_ASIO_MOVE_ARG(Function) function,
  332. const boost::coroutines::attributes& attributes)
  333. {
  334. typedef typename decay<Function>::type function_type;
  335. typename associated_executor<function_type>::type ex(
  336. (get_associated_executor)(function));
  337. boost::asio::spawn(ex, BOOST_ASIO_MOVE_CAST(Function)(function), attributes);
  338. }
  339. template <typename Handler, typename Function>
  340. void spawn(BOOST_ASIO_MOVE_ARG(Handler) handler,
  341. BOOST_ASIO_MOVE_ARG(Function) function,
  342. const boost::coroutines::attributes& attributes,
  343. typename enable_if<!is_executor<typename decay<Handler>::type>::value &&
  344. !is_convertible<Handler&, execution_context&>::value>::type*)
  345. {
  346. typedef typename decay<Handler>::type handler_type;
  347. typedef typename decay<Function>::type function_type;
  348. typename associated_executor<handler_type>::type ex(
  349. (get_associated_executor)(handler));
  350. typename associated_allocator<handler_type>::type a(
  351. (get_associated_allocator)(handler));
  352. detail::spawn_helper<handler_type, function_type> helper;
  353. helper.data_.reset(
  354. new detail::spawn_data<handler_type, function_type>(
  355. BOOST_ASIO_MOVE_CAST(Handler)(handler), true,
  356. BOOST_ASIO_MOVE_CAST(Function)(function)));
  357. helper.attributes_ = attributes;
  358. ex.dispatch(helper, a);
  359. }
  360. template <typename Handler, typename Function>
  361. void spawn(basic_yield_context<Handler> ctx,
  362. BOOST_ASIO_MOVE_ARG(Function) function,
  363. const boost::coroutines::attributes& attributes)
  364. {
  365. typedef typename decay<Function>::type function_type;
  366. Handler handler(ctx.handler_); // Explicit copy that might be moved from.
  367. typename associated_executor<Handler>::type ex(
  368. (get_associated_executor)(handler));
  369. typename associated_allocator<Handler>::type a(
  370. (get_associated_allocator)(handler));
  371. detail::spawn_helper<Handler, function_type> helper;
  372. helper.data_.reset(
  373. new detail::spawn_data<Handler, function_type>(
  374. BOOST_ASIO_MOVE_CAST(Handler)(handler), false,
  375. BOOST_ASIO_MOVE_CAST(Function)(function)));
  376. helper.attributes_ = attributes;
  377. ex.dispatch(helper, a);
  378. }
  379. template <typename Function, typename Executor>
  380. inline void spawn(const Executor& ex,
  381. BOOST_ASIO_MOVE_ARG(Function) function,
  382. const boost::coroutines::attributes& attributes,
  383. typename enable_if<is_executor<Executor>::value>::type*)
  384. {
  385. boost::asio::spawn(boost::asio::strand<Executor>(ex),
  386. BOOST_ASIO_MOVE_CAST(Function)(function), attributes);
  387. }
  388. template <typename Function, typename Executor>
  389. inline void spawn(const strand<Executor>& ex,
  390. BOOST_ASIO_MOVE_ARG(Function) function,
  391. const boost::coroutines::attributes& attributes)
  392. {
  393. boost::asio::spawn(boost::asio::bind_executor(
  394. ex, &detail::default_spawn_handler),
  395. BOOST_ASIO_MOVE_CAST(Function)(function), attributes);
  396. }
  397. template <typename Function>
  398. inline void spawn(const boost::asio::io_context::strand& s,
  399. BOOST_ASIO_MOVE_ARG(Function) function,
  400. const boost::coroutines::attributes& attributes)
  401. {
  402. boost::asio::spawn(boost::asio::bind_executor(
  403. s, &detail::default_spawn_handler),
  404. BOOST_ASIO_MOVE_CAST(Function)(function), attributes);
  405. }
  406. template <typename Function, typename ExecutionContext>
  407. inline void spawn(ExecutionContext& ctx,
  408. BOOST_ASIO_MOVE_ARG(Function) function,
  409. const boost::coroutines::attributes& attributes,
  410. typename enable_if<is_convertible<
  411. ExecutionContext&, execution_context&>::value>::type*)
  412. {
  413. boost::asio::spawn(ctx.get_executor(),
  414. BOOST_ASIO_MOVE_CAST(Function)(function), attributes);
  415. }
  416. #endif // !defined(GENERATING_DOCUMENTATION)
  417. } // namespace asio
  418. } // namespace boost
  419. #include <boost/asio/detail/pop_options.hpp>
  420. #endif // BOOST_ASIO_IMPL_SPAWN_HPP