defer.hpp 23 KB

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