dispatch.hpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514
  1. //
  2. // dispatch.hpp
  3. // ~~~~~~~~~~~~
  4. //
  5. // Copyright (c) 2003-2025 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_DISPATCH_HPP
  11. #define BOOST_ASIO_DISPATCH_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/async_result.hpp>
  17. #include <boost/asio/detail/initiate_dispatch.hpp>
  18. #include <boost/asio/detail/type_traits.hpp>
  19. #include <boost/asio/execution_context.hpp>
  20. #include <boost/asio/execution/executor.hpp>
  21. #include <boost/asio/is_executor.hpp>
  22. #include <boost/asio/detail/push_options.hpp>
  23. namespace boost {
  24. namespace asio {
  25. /// Submits a completion token or function object for execution.
  26. /**
  27. * This function submits an object for execution using the object's associated
  28. * executor. The function object may be called from the current thread prior to
  29. * returning from <tt>dispatch()</tt>. Otherwise, it is queued for execution.
  30. *
  31. * @param token The @ref completion_token that will be used to produce a
  32. * completion handler. The function signature of the completion handler must be:
  33. * @code void handler(); @endcode
  34. *
  35. * @returns This function returns <tt>async_initiate<NullaryToken,
  36. * void()>(Init{}, token)</tt>, where @c Init is a function object type defined
  37. * as:
  38. *
  39. * @code class Init
  40. * {
  41. * public:
  42. * template <typename CompletionHandler>
  43. * void operator()(CompletionHandler&& completion_handler) const;
  44. * }; @endcode
  45. *
  46. * The function call operator of @c Init:
  47. *
  48. * @li Obtains the handler's associated executor object @c ex of type @c Ex by
  49. * performing
  50. * @code auto ex = get_associated_executor(completion_handler); @endcode
  51. *
  52. * @li Obtains the handler's associated allocator object @c alloc by performing
  53. * @code auto alloc = get_associated_allocator(completion_handler); @endcode
  54. *
  55. * @li If <tt>execution::is_executor<Ex>::value</tt> is true, performs
  56. * @code prefer(ex, execution::allocator(alloc)).execute(
  57. * std::forward<CompletionHandler>(completion_handler)); @endcode
  58. *
  59. * @li If <tt>execution::is_executor<Ex>::value</tt> is false, performs
  60. * @code ex.dispatch(
  61. * std::forward<CompletionHandler>(completion_handler),
  62. * alloc); @endcode
  63. *
  64. * @par Completion Signature
  65. * @code void() @endcode
  66. */
  67. template <BOOST_ASIO_COMPLETION_TOKEN_FOR(void()) NullaryToken = deferred_t>
  68. inline auto dispatch(NullaryToken&& token = deferred_t())
  69. -> decltype(
  70. async_initiate<NullaryToken, void()>(
  71. declval<detail::initiate_dispatch>(), token))
  72. {
  73. return async_initiate<NullaryToken, void()>(
  74. detail::initiate_dispatch(), token);
  75. }
  76. /// Submits a completion token or function object for execution.
  77. /**
  78. * This function submits an object for execution using the specified executor.
  79. * The function object may be called from the current thread prior to returning
  80. * from <tt>dispatch()</tt>. Otherwise, it is queued for execution.
  81. *
  82. * @param ex The target executor.
  83. *
  84. * @param token The @ref completion_token that will be used to produce a
  85. * completion handler. The function signature of the completion handler must be:
  86. * @code void handler(); @endcode
  87. *
  88. * @returns This function returns <tt>async_initiate<NullaryToken,
  89. * void()>(Init{ex}, token)</tt>, where @c Init is a function object type
  90. * defined as:
  91. *
  92. * @code class Init
  93. * {
  94. * public:
  95. * using executor_type = Executor;
  96. * explicit Init(const Executor& ex) : ex_(ex) {}
  97. * executor_type get_executor() const noexcept { return ex_; }
  98. * template <typename CompletionHandler>
  99. * void operator()(CompletionHandler&& completion_handler) const;
  100. * private:
  101. * Executor ex_; // exposition only
  102. * }; @endcode
  103. *
  104. * The function call operator of @c Init:
  105. *
  106. * @li Obtains the handler's associated executor object @c ex1 of type @c Ex1 by
  107. * performing
  108. * @code auto ex1 = get_associated_executor(completion_handler, ex); @endcode
  109. *
  110. * @li Obtains the handler's associated allocator object @c alloc by performing
  111. * @code auto alloc = get_associated_allocator(completion_handler); @endcode
  112. *
  113. * @li If <tt>execution::is_executor<Ex1>::value</tt> is true, constructs a
  114. * function object @c f with a member @c executor_ that is initialised with
  115. * <tt>prefer(ex1, execution::outstanding_work.tracked)</tt>, a member @c
  116. * handler_ that is a decay-copy of @c completion_handler, and a function call
  117. * operator that performs:
  118. * @code auto a = get_associated_allocator(handler_);
  119. * prefer(executor_, execution::allocator(a)).execute(std::move(handler_));
  120. * @endcode
  121. *
  122. * @li If <tt>execution::is_executor<Ex1>::value</tt> is false, constructs a
  123. * function object @c f with a member @c work_ that is initialised with
  124. * <tt>make_work_guard(ex1)</tt>, a member @c handler_ that is a decay-copy of
  125. * @c completion_handler, and a function call operator that performs:
  126. * @code auto a = get_associated_allocator(handler_);
  127. * work_.get_executor().dispatch(std::move(handler_), a);
  128. * work_.reset(); @endcode
  129. *
  130. * @li If <tt>execution::is_executor<Executor>::value</tt> is true, performs
  131. * @code prefer(ex, execution::allocator(alloc)).execute(std::move(f)); @endcode
  132. *
  133. * @li If <tt>execution::is_executor<Executor>::value</tt> is false, performs
  134. * @code ex.dispatch(std::move(f), alloc); @endcode
  135. *
  136. * @par Completion Signature
  137. * @code void() @endcode
  138. */
  139. template <typename Executor,
  140. BOOST_ASIO_COMPLETION_TOKEN_FOR(void()) NullaryToken
  141. = default_completion_token_t<Executor>>
  142. inline auto dispatch(const Executor& ex,
  143. NullaryToken&& token = default_completion_token_t<Executor>(),
  144. constraint_t<
  145. execution::is_executor<Executor>::value || is_executor<Executor>::value
  146. > = 0)
  147. -> decltype(
  148. async_initiate<NullaryToken, void()>(
  149. declval<detail::initiate_dispatch_with_executor<Executor>>(),
  150. token, detail::empty_work_function()))
  151. {
  152. return async_initiate<NullaryToken, void()>(
  153. detail::initiate_dispatch_with_executor<Executor>(ex),
  154. token, detail::empty_work_function());
  155. }
  156. /// Submits a completion token or function object for execution.
  157. /**
  158. * @param ctx An execution context, from which the target executor is obtained.
  159. *
  160. * @param token The @ref completion_token that will be used to produce a
  161. * completion handler. The function signature of the completion handler must be:
  162. * @code void handler(); @endcode
  163. *
  164. * @returns <tt>dispatch(ctx.get_executor(), forward<NullaryToken>(token))</tt>.
  165. *
  166. * @par Completion Signature
  167. * @code void() @endcode
  168. */
  169. template <typename ExecutionContext,
  170. BOOST_ASIO_COMPLETION_TOKEN_FOR(void()) NullaryToken
  171. = default_completion_token_t<typename ExecutionContext::executor_type>>
  172. inline auto dispatch(ExecutionContext& ctx,
  173. NullaryToken&& token = default_completion_token_t<
  174. typename ExecutionContext::executor_type>(),
  175. constraint_t<
  176. is_convertible<ExecutionContext&, execution_context&>::value
  177. > = 0)
  178. -> decltype(
  179. async_initiate<NullaryToken, void()>(
  180. declval<detail::initiate_dispatch_with_executor<
  181. typename ExecutionContext::executor_type>>(),
  182. token, detail::empty_work_function()))
  183. {
  184. return async_initiate<NullaryToken, void()>(
  185. detail::initiate_dispatch_with_executor<
  186. typename ExecutionContext::executor_type>(ctx.get_executor()),
  187. token, detail::empty_work_function());
  188. }
  189. /// Submits a function to be run on a specified target executor, and after
  190. /// completion submits the completion handler.
  191. /**
  192. * This function submits a function object for execution using the specified
  193. * executor. The function object may be called from the current thread prior to
  194. * returning from <tt>dispatch()</tt>. Otherwise, it is queued for execution.
  195. * After the submitted function completes, the completion handler is dispatched
  196. * to run on its associated executor.
  197. *
  198. * @param function A nullary function to be executed on the target executor.
  199. *
  200. * @param ex The target executor.
  201. *
  202. * @param token The @ref completion_token that will be used to produce a
  203. * completion handler. The function signature of the completion handler must be:
  204. * @code void handler(); @endcode
  205. *
  206. * @returns This function returns <tt>async_initiate<NullaryToken,
  207. * void()>(Init{ex}, token, forward<Function>(function))</tt>, where @c Init is
  208. * a function object type defined as:
  209. *
  210. * @code class Init
  211. * {
  212. * public:
  213. * using executor_type = Executor;
  214. * explicit Init(const Executor& ex) : ex_(ex) {}
  215. * executor_type get_executor() const noexcept { return ex_; }
  216. * template <typename CompletionHandler>
  217. * void operator()(CompletionHandler&& completion_handler,
  218. * Function&& function) const;
  219. * private:
  220. * Executor ex_; // exposition only
  221. * }; @endcode
  222. *
  223. * The function call operator of @c Init:
  224. *
  225. * @li Obtains the handler's associated executor object @c ex1 of type @c Ex1 by
  226. * performing
  227. * @code auto ex1 = get_associated_executor(completion_handler, ex); @endcode
  228. *
  229. * @li Obtains the handler's associated allocator object @c alloc by performing
  230. * @code auto alloc = get_associated_allocator(completion_handler); @endcode
  231. *
  232. * @li If <tt>execution::is_executor<Ex1>::value</tt> is true, constructs a
  233. * function object wrapper @c f with a member @c executor_ that is initialised
  234. * with <tt>prefer(ex1, execution::outstanding_work.tracked)</tt>, a member @c
  235. * function_ that is a decay-copy of @c function, a member @c handler_ that is a
  236. * decay-copy of @c completion_handler, and a function call operator that
  237. * performs:
  238. * @code std::move(function_)();
  239. * auto a = get_associated_allocator(handler_);
  240. * prefer(executor_, execution::allocator(a)).execute(std::move(handler_));
  241. * @endcode
  242. *
  243. * @li If <tt>execution::is_executor<Ex1>::value</tt> is false, constructs a
  244. * function object wrapper @c f with a member @c work_ that is initialised with
  245. * <tt>make_work_guard(ex1)</tt>, a member @c function_ that is a decay-copy of
  246. * @c function, a member @c handler_ that is a decay-copy of @c
  247. * completion_handler, and a function call operator that performs:
  248. * @code std::move(function_)();
  249. * auto a = get_associated_allocator(handler_);
  250. * work_.get_executor().dispatch(std::move(handler_), a);
  251. * work_.reset(); @endcode
  252. *
  253. * @li If <tt>execution::is_executor<Executor>::value</tt> is true, performs
  254. * @code prefer(
  255. * require(ex, execution::blocking.never),
  256. * execution::relationship.fork,
  257. * execution::allocator(alloc)
  258. * ).execute(std::move(f)); @endcode
  259. *
  260. * @li If <tt>execution::is_executor<Executor>::value</tt> is false, performs
  261. * @code ex.dispatch(std::move(f), alloc); @endcode
  262. *
  263. * @note If the function object throws an exception, that exception is allowed
  264. * to propagate to the target executor. The behaviour in this case is dependent
  265. * on the executor. For example, boost::asio::io_context will allow the
  266. * exception to propagate to the caller that runs the @c io_context, whereas
  267. * boost::asio::thread_pool will call @c std::terminate.
  268. *
  269. * @par Completion Signature
  270. * @code void() @endcode
  271. */
  272. template <typename Function, typename Executor,
  273. BOOST_ASIO_COMPLETION_TOKEN_FOR(void()) NullaryToken
  274. = default_completion_token_t<Executor>>
  275. inline auto dispatch(Function&& function, const Executor& ex,
  276. NullaryToken&& token = default_completion_token_t<Executor>(),
  277. constraint_t<
  278. is_void<result_of_t<decay_t<Function>()>>::value
  279. > = 0,
  280. constraint_t<
  281. (execution::is_executor<Executor>::value
  282. && can_require<Executor, execution::blocking_t::never_t>::value)
  283. || is_executor<Executor>::value
  284. > = 0)
  285. -> decltype(
  286. async_initiate<NullaryToken, void()>(
  287. declval<detail::initiate_dispatch_with_executor<Executor>>(),
  288. token, static_cast<Function&&>(function)))
  289. {
  290. return async_initiate<NullaryToken, void()>(
  291. detail::initiate_dispatch_with_executor<Executor>(ex),
  292. token, static_cast<Function&&>(function));
  293. }
  294. /// Submits a function to be run on a specified target executor, and passes the
  295. /// result to a completion handler.
  296. /**
  297. * This function submits a function object for execution using the specified
  298. * executor. The function object may be called from the current thread prior to
  299. * returning from <tt>dispatch()</tt>. Otherwise, it is queued for execution.
  300. * After the submitted function completes, the completion handler is dispatched
  301. * along with the function's result, to run on its associated executor.
  302. *
  303. * @param function A nullary function to be executed on the target executor.
  304. *
  305. * @param ex The target executor.
  306. *
  307. * @param token The @ref completion_token that will be used to produce a
  308. * completion handler. The function signature of the completion handler must be:
  309. * @code void handler(decay_t<result_of_t<decay_t<Function>()>>); @endcode
  310. *
  311. * @returns This function returns <tt>async_initiate<CompletionToken,
  312. * void()>(Init{ex}, token)</tt>, where @c Init is a function object type
  313. * defined as:
  314. *
  315. * @code class Init
  316. * {
  317. * public:
  318. * using executor_type = Executor;
  319. * explicit Init(const Executor& ex) : ex_(ex) {}
  320. * executor_type get_executor() const noexcept { return ex_; }
  321. * template <typename CompletionHandler>
  322. * void operator()(CompletionHandler&& completion_handler,
  323. * Function&& function) const;
  324. * private:
  325. * Executor ex_; // exposition only
  326. * }; @endcode
  327. *
  328. * The function call operator of @c Init:
  329. *
  330. * @li Obtains the handler's associated executor object @c ex1 of type @c Ex1 by
  331. * performing
  332. * @code auto ex1 = get_associated_executor(completion_handler, ex); @endcode
  333. *
  334. * @li Obtains the handler's associated allocator object @c alloc by performing
  335. * @code auto alloc = get_associated_allocator(completion_handler); @endcode
  336. *
  337. * @li If <tt>execution::is_executor<Ex1>::value</tt> is true, constructs a
  338. * function object wrapper @c f with a member @c executor_ that is initialised
  339. * with <tt>prefer(ex1, execution::outstanding_work.tracked)</tt>, a member @c
  340. * function_ that is a decay-copy of @c function, a member @c handler_ that is a
  341. * decay-copy of @c completion_handler, and a function call operator that
  342. * performs:
  343. * @code auto result = std::move(function_)();
  344. * auto a = get_associated_allocator(handler_);
  345. * prefer(executor_, execution::allocator(a)).execute(
  346. * std::bind(std::move(handler_), std::move(result)));
  347. * @endcode
  348. *
  349. * @li If <tt>execution::is_executor<Ex1>::value</tt> is false, constructs a
  350. * function object wrapper @c f with a member @c work_ that is initialised with
  351. * <tt>make_work_guard(ex1)</tt>, a member @c function_ that is a decay-copy of
  352. * @c function, a member @c handler_ that is a decay-copy of @c
  353. * completion_handler, and a function call operator that performs:
  354. * @code auto result = std::move(function_)();
  355. * auto a = get_associated_allocator(handler_);
  356. * work_.get_executor().dispatch(
  357. * std::bind(std::move(handler_), std::move(result)), a);
  358. * work_.reset(); @endcode
  359. *
  360. * @li If <tt>execution::is_executor<Executor>::value</tt> is true, performs
  361. * @code prefer(
  362. * require(ex, execution::blocking.never),
  363. * execution::relationship.fork,
  364. * execution::allocator(alloc)
  365. * ).execute(std::move(f)); @endcode
  366. *
  367. * @li If <tt>execution::is_executor<Executor>::value</tt> is false, performs
  368. * @code ex.dispatch(std::move(f), alloc); @endcode
  369. *
  370. * @note If the function object throws an exception, that exception is allowed
  371. * to propagate to the target executor. The behaviour in this case is dependent
  372. * on the executor. For example, boost::asio::io_context will allow the
  373. * exception to propagate to the caller that runs the @c io_context, whereas
  374. * boost::asio::thread_pool will call @c std::terminate.
  375. *
  376. * @par Completion Signature
  377. * @code void(decay_t<result_of_t<decay_t<Function>()>>) @endcode
  378. */
  379. template <typename Function, typename Executor,
  380. BOOST_ASIO_COMPLETION_TOKEN_FOR(
  381. void(decay_t<result_of_t<decay_t<Function>()>>)) CompletionToken
  382. = default_completion_token_t<Executor>>
  383. inline auto dispatch(Function&& function, const Executor& ex,
  384. CompletionToken&& token = default_completion_token_t<Executor>(),
  385. constraint_t<
  386. !is_void<result_of_t<decay_t<Function>()>>::value
  387. > = 0,
  388. constraint_t<
  389. (execution::is_executor<Executor>::value
  390. && can_require<Executor, execution::blocking_t::never_t>::value)
  391. || is_executor<Executor>::value
  392. > = 0)
  393. -> decltype(
  394. async_initiate<CompletionToken, void(detail::work_result_t<Function>)>(
  395. declval<detail::initiate_dispatch_with_executor<Executor>>(),
  396. token, static_cast<Function&&>(function)))
  397. {
  398. return async_initiate<CompletionToken, void(detail::work_result_t<Function>)>(
  399. detail::initiate_dispatch_with_executor<Executor>(ex),
  400. token, static_cast<Function&&>(function));
  401. }
  402. /// Submits a function to be run on a specified execution context, and after
  403. /// completion submits the completion handler.
  404. /**
  405. * @param function A nullary function to be executed on the target executor.
  406. *
  407. * @param ctx An execution context, from which the target executor is obtained.
  408. *
  409. * @param token The @ref completion_token that will be used to produce a
  410. * completion handler. The function signature of the completion handler must be:
  411. * @code void handler(); @endcode
  412. *
  413. * @returns <tt>dispatch(forward<Function>(function), ctx.get_executor(),
  414. * forward<NullaryToken>(token))</tt>.
  415. *
  416. * @note If the function object throws an exception, that exception is allowed
  417. * to propagate to the target executor. The behaviour in this case is dependent
  418. * on the executor. For example, boost::asio::io_context will allow the
  419. * exception to propagate to the caller that runs the @c io_context, whereas
  420. * boost::asio::thread_pool will call @c std::terminate.
  421. *
  422. * @par Completion Signature
  423. * @code void() @endcode
  424. */
  425. template <typename Function, typename ExecutionContext,
  426. BOOST_ASIO_COMPLETION_TOKEN_FOR(void()) NullaryToken
  427. = default_completion_token_t<typename ExecutionContext::executor_type>>
  428. inline auto dispatch(Function&& function, ExecutionContext& ctx,
  429. NullaryToken&& token = default_completion_token_t<
  430. typename ExecutionContext::executor_type>(),
  431. constraint_t<
  432. is_void<result_of_t<decay_t<Function>()>>::value
  433. > = 0,
  434. constraint_t<
  435. is_convertible<ExecutionContext&, execution_context&>::value
  436. > = 0)
  437. -> decltype(
  438. async_initiate<NullaryToken, void()>(
  439. declval<detail::initiate_dispatch_with_executor<
  440. typename ExecutionContext::executor_type>>(),
  441. token, static_cast<Function&&>(function)))
  442. {
  443. return async_initiate<NullaryToken, void()>(
  444. detail::initiate_dispatch_with_executor<
  445. typename ExecutionContext::executor_type>(ctx.get_executor()),
  446. token, static_cast<Function&&>(function));
  447. }
  448. /// Submits a function to be run on a specified execution context, and passes
  449. /// the result to a completion handler.
  450. /**
  451. * @param function A nullary function to be executed on the target executor.
  452. *
  453. * @param ctx An execution context, from which the target executor is obtained.
  454. *
  455. * @param token The @ref completion_token that will be used to produce a
  456. * completion handler. The function signature of the completion handler must be:
  457. * @code void handler(); @endcode
  458. *
  459. * @returns <tt>dispatch(forward<Function>(function), ctx.get_executor(),
  460. * forward<CompletionToken>(token))</tt>.
  461. *
  462. * @note If the function object throws an exception, that exception is allowed
  463. * to propagate to the target executor. The behaviour in this case is dependent
  464. * on the executor. For example, boost::asio::io_context will allow the
  465. * exception to propagate to the caller that runs the @c io_context, whereas
  466. * boost::asio::thread_pool will call @c std::terminate.
  467. *
  468. * @par Completion Signature
  469. * @code void(decay_t<result_of_t<decay_t<Function>()>>) @endcode
  470. */
  471. template <typename Function, typename ExecutionContext,
  472. BOOST_ASIO_COMPLETION_TOKEN_FOR(
  473. void(decay_t<result_of_t<decay_t<Function>()>>)) CompletionToken
  474. = default_completion_token_t<typename ExecutionContext::executor_type>>
  475. inline auto dispatch(Function&& function, ExecutionContext& ctx,
  476. CompletionToken&& token = default_completion_token_t<
  477. typename ExecutionContext::executor_type>(),
  478. constraint_t<
  479. !is_void<result_of_t<decay_t<Function>()>>::value
  480. > = 0,
  481. constraint_t<
  482. is_convertible<ExecutionContext&, execution_context&>::value
  483. > = 0)
  484. -> decltype(
  485. async_initiate<CompletionToken, void(detail::work_result_t<Function>)>(
  486. declval<detail::initiate_dispatch_with_executor<
  487. typename ExecutionContext::executor_type>>(),
  488. token, static_cast<Function&&>(function)))
  489. {
  490. return async_initiate<CompletionToken, void(detail::work_result_t<Function>)>(
  491. detail::initiate_dispatch_with_executor<
  492. typename ExecutionContext::executor_type>(ctx.get_executor()),
  493. token, static_cast<Function&&>(function));
  494. }
  495. } // namespace asio
  496. } // namespace boost
  497. #include <boost/asio/detail/pop_options.hpp>
  498. #endif // BOOST_ASIO_DISPATCH_HPP