thread_pool.hpp 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147
  1. //
  2. // thread_pool.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_THREAD_POOL_HPP
  11. #define BOOST_ASIO_THREAD_POOL_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/detail/atomic_count.hpp>
  17. #include <boost/asio/detail/scheduler.hpp>
  18. #include <boost/asio/detail/thread_group.hpp>
  19. #include <boost/asio/execution.hpp>
  20. #include <boost/asio/execution_context.hpp>
  21. #include <boost/asio/detail/push_options.hpp>
  22. namespace boost {
  23. namespace asio {
  24. namespace detail {
  25. struct thread_pool_bits
  26. {
  27. BOOST_ASIO_STATIC_CONSTEXPR(unsigned int, blocking_never = 1);
  28. BOOST_ASIO_STATIC_CONSTEXPR(unsigned int, blocking_always = 2);
  29. BOOST_ASIO_STATIC_CONSTEXPR(unsigned int, blocking_mask = 3);
  30. BOOST_ASIO_STATIC_CONSTEXPR(unsigned int, relationship_continuation = 4);
  31. BOOST_ASIO_STATIC_CONSTEXPR(unsigned int, outstanding_work_tracked = 8);
  32. };
  33. } // namespace detail
  34. /// A simple fixed-size thread pool.
  35. /**
  36. * The thread pool class is an execution context where functions are permitted
  37. * to run on one of a fixed number of threads.
  38. *
  39. * @par Submitting tasks to the pool
  40. *
  41. * To submit functions to the thread pool, use the @ref boost::asio::dispatch,
  42. * @ref boost::asio::post or @ref boost::asio::defer free functions.
  43. *
  44. * For example:
  45. *
  46. * @code void my_task()
  47. * {
  48. * ...
  49. * }
  50. *
  51. * ...
  52. *
  53. * // Launch the pool with four threads.
  54. * boost::asio::thread_pool pool(4);
  55. *
  56. * // Submit a function to the pool.
  57. * boost::asio::post(pool, my_task);
  58. *
  59. * // Submit a lambda object to the pool.
  60. * boost::asio::post(pool,
  61. * []()
  62. * {
  63. * ...
  64. * });
  65. *
  66. * // Wait for all tasks in the pool to complete.
  67. * pool.join(); @endcode
  68. */
  69. class thread_pool
  70. : public execution_context
  71. {
  72. public:
  73. template <typename Allocator, unsigned int Bits>
  74. class basic_executor_type;
  75. template <typename Allocator, unsigned int Bits>
  76. friend class basic_executor_type;
  77. /// Executor used to submit functions to a thread pool.
  78. typedef basic_executor_type<std::allocator<void>, 0> executor_type;
  79. /// Scheduler used to schedule receivers on a thread pool.
  80. typedef basic_executor_type<std::allocator<void>, 0> scheduler_type;
  81. #if !defined(BOOST_ASIO_NO_TS_EXECUTORS)
  82. /// Constructs a pool with an automatically determined number of threads.
  83. BOOST_ASIO_DECL thread_pool();
  84. #endif // !defined(BOOST_ASIO_NO_TS_EXECUTORS)
  85. /// Constructs a pool with a specified number of threads.
  86. BOOST_ASIO_DECL thread_pool(std::size_t num_threads);
  87. /// Destructor.
  88. /**
  89. * Automatically stops and joins the pool, if not explicitly done beforehand.
  90. */
  91. BOOST_ASIO_DECL ~thread_pool();
  92. /// Obtains the executor associated with the pool.
  93. executor_type get_executor() BOOST_ASIO_NOEXCEPT;
  94. /// Obtains the executor associated with the pool.
  95. executor_type executor() BOOST_ASIO_NOEXCEPT;
  96. /// Obtains the scheduler associated with the pool.
  97. scheduler_type scheduler() BOOST_ASIO_NOEXCEPT;
  98. /// Stops the threads.
  99. /**
  100. * This function stops the threads as soon as possible. As a result of calling
  101. * @c stop(), pending function objects may be never be invoked.
  102. */
  103. BOOST_ASIO_DECL void stop();
  104. /// Attaches the current thread to the pool.
  105. /**
  106. * This function attaches the current thread to the pool so that it may be
  107. * used for executing submitted function objects. Blocks the calling thread
  108. * until the pool is stopped or joined and has no outstanding work.
  109. */
  110. BOOST_ASIO_DECL void attach();
  111. /// Joins the threads.
  112. /**
  113. * This function blocks until the threads in the pool have completed. If @c
  114. * stop() is not called prior to @c join(), the @c join() call will wait
  115. * until the pool has no more outstanding work.
  116. */
  117. BOOST_ASIO_DECL void join();
  118. /// Waits for threads to complete.
  119. /**
  120. * This function blocks until the threads in the pool have completed. If @c
  121. * stop() is not called prior to @c wait(), the @c wait() call will wait
  122. * until the pool has no more outstanding work.
  123. */
  124. BOOST_ASIO_DECL void wait();
  125. private:
  126. thread_pool(const thread_pool&) BOOST_ASIO_DELETED;
  127. thread_pool& operator=(const thread_pool&) BOOST_ASIO_DELETED;
  128. struct thread_function;
  129. // Helper function to create the underlying scheduler.
  130. BOOST_ASIO_DECL detail::scheduler& add_scheduler(detail::scheduler* s);
  131. // The underlying scheduler.
  132. detail::scheduler& scheduler_;
  133. // The threads in the pool.
  134. detail::thread_group threads_;
  135. // The current number of threads in the pool.
  136. detail::atomic_count num_threads_;
  137. };
  138. /// Executor implementation type used to submit functions to a thread pool.
  139. template <typename Allocator, unsigned int Bits>
  140. class thread_pool::basic_executor_type : detail::thread_pool_bits
  141. {
  142. public:
  143. #if !defined(BOOST_ASIO_NO_DEPRECATED)
  144. /// (Deprecated.) The sender type, when this type is used as a scheduler.
  145. typedef basic_executor_type sender_type;
  146. #endif // !defined(BOOST_ASIO_NO_DEPRECATED)
  147. /// The bulk execution shape type.
  148. typedef std::size_t shape_type;
  149. /// The bulk execution index type.
  150. typedef std::size_t index_type;
  151. #if defined(BOOST_ASIO_HAS_DEDUCED_EXECUTION_IS_TYPED_SENDER_TRAIT) \
  152. && defined(BOOST_ASIO_HAS_STD_EXCEPTION_PTR)
  153. template <
  154. template <typename...> class Tuple,
  155. template <typename...> class Variant>
  156. using value_types = Variant<Tuple<>>;
  157. template <template <typename...> class Variant>
  158. using error_types = Variant<std::exception_ptr>;
  159. BOOST_ASIO_STATIC_CONSTEXPR(bool, sends_done = true);
  160. #endif // defined(BOOST_ASIO_HAS_DEDUCED_EXECUTION_IS_TYPED_SENDER_TRAIT)
  161. // && defined(BOOST_ASIO_HAS_STD_EXCEPTION_PTR)
  162. /// Copy constructor.
  163. basic_executor_type(
  164. const basic_executor_type& other) BOOST_ASIO_NOEXCEPT
  165. : pool_(other.pool_),
  166. allocator_(other.allocator_),
  167. bits_(other.bits_)
  168. {
  169. if (Bits & outstanding_work_tracked)
  170. if (pool_)
  171. pool_->scheduler_.work_started();
  172. }
  173. #if defined(BOOST_ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
  174. /// Move constructor.
  175. basic_executor_type(basic_executor_type&& other) BOOST_ASIO_NOEXCEPT
  176. : pool_(other.pool_),
  177. allocator_(BOOST_ASIO_MOVE_CAST(Allocator)(other.allocator_)),
  178. bits_(other.bits_)
  179. {
  180. if (Bits & outstanding_work_tracked)
  181. other.pool_ = 0;
  182. }
  183. #endif // defined(BOOST_ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
  184. /// Destructor.
  185. ~basic_executor_type() BOOST_ASIO_NOEXCEPT
  186. {
  187. if (Bits & outstanding_work_tracked)
  188. if (pool_)
  189. pool_->scheduler_.work_finished();
  190. }
  191. /// Assignment operator.
  192. basic_executor_type& operator=(
  193. const basic_executor_type& other) BOOST_ASIO_NOEXCEPT;
  194. #if defined(BOOST_ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
  195. /// Move assignment operator.
  196. basic_executor_type& operator=(
  197. basic_executor_type&& other) BOOST_ASIO_NOEXCEPT;
  198. #endif // defined(BOOST_ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
  199. #if !defined(GENERATING_DOCUMENTATION)
  200. private:
  201. friend struct boost_asio_require_fn::impl;
  202. friend struct boost_asio_prefer_fn::impl;
  203. #endif // !defined(GENERATING_DOCUMENTATION)
  204. /// Obtain an executor with the @c blocking.possibly property.
  205. /**
  206. * Do not call this function directly. It is intended for use with the
  207. * boost::asio::require customisation point.
  208. *
  209. * For example:
  210. * @code auto ex1 = my_thread_pool.executor();
  211. * auto ex2 = boost::asio::require(ex1,
  212. * boost::asio::execution::blocking.possibly); @endcode
  213. */
  214. BOOST_ASIO_CONSTEXPR basic_executor_type<Allocator,
  215. BOOST_ASIO_UNSPECIFIED(Bits & ~blocking_mask)>
  216. require(execution::blocking_t::possibly_t) const
  217. {
  218. return basic_executor_type<Allocator, Bits & ~blocking_mask>(
  219. pool_, allocator_, bits_ & ~blocking_mask);
  220. }
  221. /// Obtain an executor with the @c blocking.always property.
  222. /**
  223. * Do not call this function directly. It is intended for use with the
  224. * boost::asio::require customisation point.
  225. *
  226. * For example:
  227. * @code auto ex1 = my_thread_pool.executor();
  228. * auto ex2 = boost::asio::require(ex1,
  229. * boost::asio::execution::blocking.always); @endcode
  230. */
  231. BOOST_ASIO_CONSTEXPR basic_executor_type<Allocator,
  232. BOOST_ASIO_UNSPECIFIED((Bits & ~blocking_mask) | blocking_always)>
  233. require(execution::blocking_t::always_t) const
  234. {
  235. return basic_executor_type<Allocator,
  236. BOOST_ASIO_UNSPECIFIED((Bits & ~blocking_mask) | blocking_always)>(
  237. pool_, allocator_, bits_ & ~blocking_mask);
  238. }
  239. /// Obtain an executor with the @c blocking.never property.
  240. /**
  241. * Do not call this function directly. It is intended for use with the
  242. * boost::asio::require customisation point.
  243. *
  244. * For example:
  245. * @code auto ex1 = my_thread_pool.executor();
  246. * auto ex2 = boost::asio::require(ex1,
  247. * boost::asio::execution::blocking.never); @endcode
  248. */
  249. BOOST_ASIO_CONSTEXPR basic_executor_type<Allocator,
  250. BOOST_ASIO_UNSPECIFIED(Bits & ~blocking_mask)>
  251. require(execution::blocking_t::never_t) const
  252. {
  253. return basic_executor_type<Allocator, Bits & ~blocking_mask>(
  254. pool_, allocator_, (bits_ & ~blocking_mask) | blocking_never);
  255. }
  256. /// Obtain an executor with the @c relationship.fork property.
  257. /**
  258. * Do not call this function directly. It is intended for use with the
  259. * boost::asio::require customisation point.
  260. *
  261. * For example:
  262. * @code auto ex1 = my_thread_pool.executor();
  263. * auto ex2 = boost::asio::require(ex1,
  264. * boost::asio::execution::relationship.fork); @endcode
  265. */
  266. BOOST_ASIO_CONSTEXPR basic_executor_type require(
  267. execution::relationship_t::fork_t) const
  268. {
  269. return basic_executor_type(pool_,
  270. allocator_, bits_ & ~relationship_continuation);
  271. }
  272. /// Obtain an executor with the @c relationship.continuation property.
  273. /**
  274. * Do not call this function directly. It is intended for use with the
  275. * boost::asio::require customisation point.
  276. *
  277. * For example:
  278. * @code auto ex1 = my_thread_pool.executor();
  279. * auto ex2 = boost::asio::require(ex1,
  280. * boost::asio::execution::relationship.continuation); @endcode
  281. */
  282. BOOST_ASIO_CONSTEXPR basic_executor_type require(
  283. execution::relationship_t::continuation_t) const
  284. {
  285. return basic_executor_type(pool_,
  286. allocator_, bits_ | relationship_continuation);
  287. }
  288. /// Obtain an executor with the @c outstanding_work.tracked property.
  289. /**
  290. * Do not call this function directly. It is intended for use with the
  291. * boost::asio::require customisation point.
  292. *
  293. * For example:
  294. * @code auto ex1 = my_thread_pool.executor();
  295. * auto ex2 = boost::asio::require(ex1,
  296. * boost::asio::execution::outstanding_work.tracked); @endcode
  297. */
  298. BOOST_ASIO_CONSTEXPR basic_executor_type<Allocator,
  299. BOOST_ASIO_UNSPECIFIED(Bits | outstanding_work_tracked)>
  300. require(execution::outstanding_work_t::tracked_t) const
  301. {
  302. return basic_executor_type<Allocator, Bits | outstanding_work_tracked>(
  303. pool_, allocator_, bits_);
  304. }
  305. /// Obtain an executor with the @c outstanding_work.untracked property.
  306. /**
  307. * Do not call this function directly. It is intended for use with the
  308. * boost::asio::require customisation point.
  309. *
  310. * For example:
  311. * @code auto ex1 = my_thread_pool.executor();
  312. * auto ex2 = boost::asio::require(ex1,
  313. * boost::asio::execution::outstanding_work.untracked); @endcode
  314. */
  315. BOOST_ASIO_CONSTEXPR basic_executor_type<Allocator,
  316. BOOST_ASIO_UNSPECIFIED(Bits & ~outstanding_work_tracked)>
  317. require(execution::outstanding_work_t::untracked_t) const
  318. {
  319. return basic_executor_type<Allocator, Bits & ~outstanding_work_tracked>(
  320. pool_, allocator_, bits_);
  321. }
  322. /// Obtain an executor with the specified @c allocator property.
  323. /**
  324. * Do not call this function directly. It is intended for use with the
  325. * boost::asio::require customisation point.
  326. *
  327. * For example:
  328. * @code auto ex1 = my_thread_pool.executor();
  329. * auto ex2 = boost::asio::require(ex1,
  330. * boost::asio::execution::allocator(my_allocator)); @endcode
  331. */
  332. template <typename OtherAllocator>
  333. BOOST_ASIO_CONSTEXPR basic_executor_type<OtherAllocator, Bits>
  334. require(execution::allocator_t<OtherAllocator> a) const
  335. {
  336. return basic_executor_type<OtherAllocator, Bits>(
  337. pool_, a.value(), bits_);
  338. }
  339. /// Obtain an executor with the default @c allocator property.
  340. /**
  341. * Do not call this function directly. It is intended for use with the
  342. * boost::asio::require customisation point.
  343. *
  344. * For example:
  345. * @code auto ex1 = my_thread_pool.executor();
  346. * auto ex2 = boost::asio::require(ex1,
  347. * boost::asio::execution::allocator); @endcode
  348. */
  349. BOOST_ASIO_CONSTEXPR basic_executor_type<std::allocator<void>, Bits>
  350. require(execution::allocator_t<void>) const
  351. {
  352. return basic_executor_type<std::allocator<void>, Bits>(
  353. pool_, std::allocator<void>(), bits_);
  354. }
  355. #if !defined(GENERATING_DOCUMENTATION)
  356. private:
  357. friend struct boost_asio_query_fn::impl;
  358. friend struct boost::asio::execution::detail::mapping_t<0>;
  359. friend struct boost::asio::execution::detail::outstanding_work_t<0>;
  360. #endif // !defined(GENERATING_DOCUMENTATION)
  361. #if !defined(BOOST_ASIO_NO_DEPRECATED)
  362. /// (Deprecated.) Query the current value of the @c bulk_guarantee property.
  363. /**
  364. * Do not call this function directly. It is intended for use with the
  365. * boost::asio::query customisation point.
  366. *
  367. * For example:
  368. * @code auto ex = my_thread_pool.executor();
  369. * if (boost::asio::query(ex, boost::asio::execution::bulk_guarantee)
  370. * == boost::asio::execution::bulk_guarantee.parallel)
  371. * ... @endcode
  372. */
  373. static BOOST_ASIO_CONSTEXPR execution::bulk_guarantee_t query(
  374. execution::bulk_guarantee_t) BOOST_ASIO_NOEXCEPT
  375. {
  376. return execution::bulk_guarantee.parallel;
  377. }
  378. #endif // !defined(BOOST_ASIO_NO_DEPRECATED)
  379. /// Query the current value of the @c mapping property.
  380. /**
  381. * Do not call this function directly. It is intended for use with the
  382. * boost::asio::query customisation point.
  383. *
  384. * For example:
  385. * @code auto ex = my_thread_pool.executor();
  386. * if (boost::asio::query(ex, boost::asio::execution::mapping)
  387. * == boost::asio::execution::mapping.thread)
  388. * ... @endcode
  389. */
  390. static BOOST_ASIO_CONSTEXPR execution::mapping_t query(
  391. execution::mapping_t) BOOST_ASIO_NOEXCEPT
  392. {
  393. return execution::mapping.thread;
  394. }
  395. /// Query the current value of the @c context property.
  396. /**
  397. * Do not call this function directly. It is intended for use with the
  398. * boost::asio::query customisation point.
  399. *
  400. * For example:
  401. * @code auto ex = my_thread_pool.executor();
  402. * boost::asio::thread_pool& pool = boost::asio::query(
  403. * ex, boost::asio::execution::context); @endcode
  404. */
  405. thread_pool& query(execution::context_t) const BOOST_ASIO_NOEXCEPT
  406. {
  407. return *pool_;
  408. }
  409. /// Query the current value of the @c blocking property.
  410. /**
  411. * Do not call this function directly. It is intended for use with the
  412. * boost::asio::query customisation point.
  413. *
  414. * For example:
  415. * @code auto ex = my_thread_pool.executor();
  416. * if (boost::asio::query(ex, boost::asio::execution::blocking)
  417. * == boost::asio::execution::blocking.always)
  418. * ... @endcode
  419. */
  420. BOOST_ASIO_CONSTEXPR execution::blocking_t query(
  421. execution::blocking_t) const BOOST_ASIO_NOEXCEPT
  422. {
  423. return (bits_ & blocking_never)
  424. ? execution::blocking_t(execution::blocking.never)
  425. : ((Bits & blocking_always)
  426. ? execution::blocking_t(execution::blocking.always)
  427. : execution::blocking_t(execution::blocking.possibly));
  428. }
  429. /// Query the current value of the @c relationship property.
  430. /**
  431. * Do not call this function directly. It is intended for use with the
  432. * boost::asio::query customisation point.
  433. *
  434. * For example:
  435. * @code auto ex = my_thread_pool.executor();
  436. * if (boost::asio::query(ex, boost::asio::execution::relationship)
  437. * == boost::asio::execution::relationship.continuation)
  438. * ... @endcode
  439. */
  440. BOOST_ASIO_CONSTEXPR execution::relationship_t query(
  441. execution::relationship_t) const BOOST_ASIO_NOEXCEPT
  442. {
  443. return (bits_ & relationship_continuation)
  444. ? execution::relationship_t(execution::relationship.continuation)
  445. : execution::relationship_t(execution::relationship.fork);
  446. }
  447. /// Query the current value of the @c outstanding_work property.
  448. /**
  449. * Do not call this function directly. It is intended for use with the
  450. * boost::asio::query customisation point.
  451. *
  452. * For example:
  453. * @code auto ex = my_thread_pool.executor();
  454. * if (boost::asio::query(ex, boost::asio::execution::outstanding_work)
  455. * == boost::asio::execution::outstanding_work.tracked)
  456. * ... @endcode
  457. */
  458. static BOOST_ASIO_CONSTEXPR execution::outstanding_work_t query(
  459. execution::outstanding_work_t) BOOST_ASIO_NOEXCEPT
  460. {
  461. return (Bits & outstanding_work_tracked)
  462. ? execution::outstanding_work_t(execution::outstanding_work.tracked)
  463. : execution::outstanding_work_t(execution::outstanding_work.untracked);
  464. }
  465. /// Query the current value of the @c allocator property.
  466. /**
  467. * Do not call this function directly. It is intended for use with the
  468. * boost::asio::query customisation point.
  469. *
  470. * For example:
  471. * @code auto ex = my_thread_pool.executor();
  472. * auto alloc = boost::asio::query(ex,
  473. * boost::asio::execution::allocator); @endcode
  474. */
  475. template <typename OtherAllocator>
  476. BOOST_ASIO_CONSTEXPR Allocator query(
  477. execution::allocator_t<OtherAllocator>) const BOOST_ASIO_NOEXCEPT
  478. {
  479. return allocator_;
  480. }
  481. /// Query the current value of the @c allocator property.
  482. /**
  483. * Do not call this function directly. It is intended for use with the
  484. * boost::asio::query customisation point.
  485. *
  486. * For example:
  487. * @code auto ex = my_thread_pool.executor();
  488. * auto alloc = boost::asio::query(ex,
  489. * boost::asio::execution::allocator); @endcode
  490. */
  491. BOOST_ASIO_CONSTEXPR Allocator query(
  492. execution::allocator_t<void>) const BOOST_ASIO_NOEXCEPT
  493. {
  494. return allocator_;
  495. }
  496. /// Query the occupancy (recommended number of work items) for the pool.
  497. /**
  498. * Do not call this function directly. It is intended for use with the
  499. * boost::asio::query customisation point.
  500. *
  501. * For example:
  502. * @code auto ex = my_thread_pool.executor();
  503. * std::size_t occupancy = boost::asio::query(
  504. * ex, boost::asio::execution::occupancy); @endcode
  505. */
  506. std::size_t query(execution::occupancy_t) const BOOST_ASIO_NOEXCEPT
  507. {
  508. return static_cast<std::size_t>(pool_->num_threads_);
  509. }
  510. public:
  511. /// Determine whether the thread pool is running in the current thread.
  512. /**
  513. * @return @c true if the current thread is running the thread pool. Otherwise
  514. * returns @c false.
  515. */
  516. bool running_in_this_thread() const BOOST_ASIO_NOEXCEPT;
  517. /// Compare two executors for equality.
  518. /**
  519. * Two executors are equal if they refer to the same underlying thread pool.
  520. */
  521. friend bool operator==(const basic_executor_type& a,
  522. const basic_executor_type& b) BOOST_ASIO_NOEXCEPT
  523. {
  524. return a.pool_ == b.pool_
  525. && a.allocator_ == b.allocator_
  526. && a.bits_ == b.bits_;
  527. }
  528. /// Compare two executors for inequality.
  529. /**
  530. * Two executors are equal if they refer to the same underlying thread pool.
  531. */
  532. friend bool operator!=(const basic_executor_type& a,
  533. const basic_executor_type& b) BOOST_ASIO_NOEXCEPT
  534. {
  535. return a.pool_ != b.pool_
  536. || a.allocator_ != b.allocator_
  537. || a.bits_ != b.bits_;
  538. }
  539. /// Execution function.
  540. template <typename Function>
  541. void execute(BOOST_ASIO_MOVE_ARG(Function) f) const
  542. {
  543. this->do_execute(BOOST_ASIO_MOVE_CAST(Function)(f),
  544. integral_constant<bool, (Bits & blocking_always) != 0>());
  545. }
  546. public:
  547. #if !defined(BOOST_ASIO_NO_DEPRECATED)
  548. /// (Deprecated.) Bulk execution function.
  549. template <typename Function>
  550. void bulk_execute(BOOST_ASIO_MOVE_ARG(Function) f, std::size_t n) const
  551. {
  552. this->do_bulk_execute(BOOST_ASIO_MOVE_CAST(Function)(f), n,
  553. integral_constant<bool, (Bits & blocking_always) != 0>());
  554. }
  555. /// (Deprecated.) Schedule function.
  556. /**
  557. * Do not call this function directly. It is intended for use with the
  558. * execution::schedule customisation point.
  559. *
  560. * @return An object that satisfies the sender concept.
  561. */
  562. sender_type schedule() const BOOST_ASIO_NOEXCEPT
  563. {
  564. return *this;
  565. }
  566. /// (Deprecated.) Connect function.
  567. /**
  568. * Do not call this function directly. It is intended for use with the
  569. * execution::connect customisation point.
  570. *
  571. * @return An object of an unspecified type that satisfies the @c
  572. * operation_state concept.
  573. */
  574. template <BOOST_ASIO_EXECUTION_RECEIVER_OF_0 Receiver>
  575. #if defined(GENERATING_DOCUMENTATION)
  576. unspecified
  577. #else // defined(GENERATING_DOCUMENTATION)
  578. execution::detail::as_operation<basic_executor_type, Receiver>
  579. #endif // defined(GENERATING_DOCUMENTATION)
  580. connect(BOOST_ASIO_MOVE_ARG(Receiver) r) const
  581. {
  582. return execution::detail::as_operation<basic_executor_type, Receiver>(
  583. *this, BOOST_ASIO_MOVE_CAST(Receiver)(r));
  584. }
  585. #endif // !defined(BOOST_ASIO_NO_DEPRECATED)
  586. #if !defined(BOOST_ASIO_NO_TS_EXECUTORS)
  587. /// Obtain the underlying execution context.
  588. thread_pool& context() const BOOST_ASIO_NOEXCEPT;
  589. /// Inform the thread pool that it has some outstanding work to do.
  590. /**
  591. * This function is used to inform the thread pool that some work has begun.
  592. * This ensures that the thread pool's join() function will not return while
  593. * the work is underway.
  594. */
  595. void on_work_started() const BOOST_ASIO_NOEXCEPT;
  596. /// Inform the thread pool that some work is no longer outstanding.
  597. /**
  598. * This function is used to inform the thread pool that some work has
  599. * finished. Once the count of unfinished work reaches zero, the thread
  600. * pool's join() function is permitted to exit.
  601. */
  602. void on_work_finished() const BOOST_ASIO_NOEXCEPT;
  603. /// Request the thread pool to invoke the given function object.
  604. /**
  605. * This function is used to ask the thread pool to execute the given function
  606. * object. If the current thread belongs to the pool, @c dispatch() executes
  607. * the function before returning. Otherwise, the function will be scheduled
  608. * to run on the thread pool.
  609. *
  610. * @param f The function object to be called. The executor will make
  611. * a copy of the handler object as required. The function signature of the
  612. * function object must be: @code void function(); @endcode
  613. *
  614. * @param a An allocator that may be used by the executor to allocate the
  615. * internal storage needed for function invocation.
  616. */
  617. template <typename Function, typename OtherAllocator>
  618. void dispatch(BOOST_ASIO_MOVE_ARG(Function) f,
  619. const OtherAllocator& a) const;
  620. /// Request the thread pool to invoke the given function object.
  621. /**
  622. * This function is used to ask the thread pool to execute the given function
  623. * object. The function object will never be executed inside @c post().
  624. * Instead, it will be scheduled to run on the thread pool.
  625. *
  626. * @param f The function object to be called. The executor will make
  627. * a copy of the handler object as required. The function signature of the
  628. * function object must be: @code void function(); @endcode
  629. *
  630. * @param a An allocator that may be used by the executor to allocate the
  631. * internal storage needed for function invocation.
  632. */
  633. template <typename Function, typename OtherAllocator>
  634. void post(BOOST_ASIO_MOVE_ARG(Function) f,
  635. const OtherAllocator& a) const;
  636. /// Request the thread pool to invoke the given function object.
  637. /**
  638. * This function is used to ask the thread pool to execute the given function
  639. * object. The function object will never be executed inside @c defer().
  640. * Instead, it will be scheduled to run on the thread pool.
  641. *
  642. * If the current thread belongs to the thread pool, @c defer() will delay
  643. * scheduling the function object until the current thread returns control to
  644. * the pool.
  645. *
  646. * @param f The function object to be called. The executor will make
  647. * a copy of the handler object as required. The function signature of the
  648. * function object must be: @code void function(); @endcode
  649. *
  650. * @param a An allocator that may be used by the executor to allocate the
  651. * internal storage needed for function invocation.
  652. */
  653. template <typename Function, typename OtherAllocator>
  654. void defer(BOOST_ASIO_MOVE_ARG(Function) f,
  655. const OtherAllocator& a) const;
  656. #endif // !defined(BOOST_ASIO_NO_TS_EXECUTORS)
  657. private:
  658. friend class thread_pool;
  659. template <typename, unsigned int> friend class basic_executor_type;
  660. // Constructor used by thread_pool::get_executor().
  661. explicit basic_executor_type(thread_pool& p) BOOST_ASIO_NOEXCEPT
  662. : pool_(&p),
  663. allocator_(),
  664. bits_(0)
  665. {
  666. if (Bits & outstanding_work_tracked)
  667. pool_->scheduler_.work_started();
  668. }
  669. // Constructor used by require().
  670. basic_executor_type(thread_pool* p,
  671. const Allocator& a, unsigned int bits) BOOST_ASIO_NOEXCEPT
  672. : pool_(p),
  673. allocator_(a),
  674. bits_(bits)
  675. {
  676. if (Bits & outstanding_work_tracked)
  677. if (pool_)
  678. pool_->scheduler_.work_started();
  679. }
  680. /// Execution helper implementation for possibly and never blocking.
  681. template <typename Function>
  682. void do_execute(BOOST_ASIO_MOVE_ARG(Function) f, false_type) const;
  683. /// Execution helper implementation for always blocking.
  684. template <typename Function>
  685. void do_execute(BOOST_ASIO_MOVE_ARG(Function) f, true_type) const;
  686. /// Bulk execution helper implementation for possibly and never blocking.
  687. template <typename Function>
  688. void do_bulk_execute(BOOST_ASIO_MOVE_ARG(Function) f,
  689. std::size_t n, false_type) const;
  690. /// Bulk execution helper implementation for always blocking.
  691. template <typename Function>
  692. void do_bulk_execute(BOOST_ASIO_MOVE_ARG(Function) f,
  693. std::size_t n, true_type) const;
  694. // The underlying thread pool.
  695. thread_pool* pool_;
  696. // The allocator used for execution functions.
  697. Allocator allocator_;
  698. // The runtime-switched properties of the thread pool executor.
  699. unsigned int bits_;
  700. };
  701. #if !defined(GENERATING_DOCUMENTATION)
  702. namespace traits {
  703. #if !defined(BOOST_ASIO_HAS_DEDUCED_EQUALITY_COMPARABLE_TRAIT)
  704. template <typename Allocator, unsigned int Bits>
  705. struct equality_comparable<
  706. boost::asio::thread_pool::basic_executor_type<Allocator, Bits>
  707. >
  708. {
  709. BOOST_ASIO_STATIC_CONSTEXPR(bool, is_valid = true);
  710. BOOST_ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true);
  711. };
  712. #endif // !defined(BOOST_ASIO_HAS_DEDUCED_EQUALITY_COMPARABLE_TRAIT)
  713. #if !defined(BOOST_ASIO_HAS_DEDUCED_EXECUTE_MEMBER_TRAIT)
  714. template <typename Allocator, unsigned int Bits, typename Function>
  715. struct execute_member<
  716. boost::asio::thread_pool::basic_executor_type<Allocator, Bits>,
  717. Function
  718. >
  719. {
  720. BOOST_ASIO_STATIC_CONSTEXPR(bool, is_valid = true);
  721. BOOST_ASIO_STATIC_CONSTEXPR(bool, is_noexcept = false);
  722. typedef void result_type;
  723. };
  724. #endif // !defined(BOOST_ASIO_HAS_DEDUCED_EXECUTE_MEMBER_TRAIT)
  725. #if !defined(BOOST_ASIO_HAS_DEDUCED_SCHEDULE_MEMBER_TRAIT)
  726. #if !defined(BOOST_ASIO_NO_DEPRECATED)
  727. template <typename Allocator, unsigned int Bits>
  728. struct schedule_member<
  729. const boost::asio::thread_pool::basic_executor_type<Allocator, Bits>
  730. >
  731. {
  732. BOOST_ASIO_STATIC_CONSTEXPR(bool, is_valid = true);
  733. BOOST_ASIO_STATIC_CONSTEXPR(bool, is_noexcept = false);
  734. typedef boost::asio::thread_pool::basic_executor_type<
  735. Allocator, Bits> result_type;
  736. };
  737. #endif // !defined(BOOST_ASIO_NO_DEPRECATED)
  738. #endif // !defined(BOOST_ASIO_HAS_DEDUCED_SCHEDULE_MEMBER_TRAIT)
  739. #if !defined(BOOST_ASIO_HAS_DEDUCED_CONNECT_MEMBER_TRAIT)
  740. #if !defined(BOOST_ASIO_NO_DEPRECATED)
  741. template <typename Allocator, unsigned int Bits, typename Receiver>
  742. struct connect_member<
  743. const boost::asio::thread_pool::basic_executor_type<Allocator, Bits>,
  744. Receiver
  745. >
  746. {
  747. BOOST_ASIO_STATIC_CONSTEXPR(bool, is_valid = true);
  748. BOOST_ASIO_STATIC_CONSTEXPR(bool, is_noexcept = false);
  749. typedef boost::asio::execution::detail::as_operation<
  750. boost::asio::thread_pool::basic_executor_type<Allocator, Bits>,
  751. Receiver> result_type;
  752. };
  753. #endif // !defined(BOOST_ASIO_NO_DEPRECATED)
  754. #endif // !defined(BOOST_ASIO_HAS_DEDUCED_CONNECT_MEMBER_TRAIT)
  755. #if !defined(BOOST_ASIO_HAS_DEDUCED_REQUIRE_MEMBER_TRAIT)
  756. template <typename Allocator, unsigned int Bits>
  757. struct require_member<
  758. boost::asio::thread_pool::basic_executor_type<Allocator, Bits>,
  759. boost::asio::execution::blocking_t::possibly_t
  760. > : boost::asio::detail::thread_pool_bits
  761. {
  762. BOOST_ASIO_STATIC_CONSTEXPR(bool, is_valid = true);
  763. BOOST_ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true);
  764. typedef boost::asio::thread_pool::basic_executor_type<
  765. Allocator, Bits & ~blocking_mask> result_type;
  766. };
  767. template <typename Allocator, unsigned int Bits>
  768. struct require_member<
  769. boost::asio::thread_pool::basic_executor_type<Allocator, Bits>,
  770. boost::asio::execution::blocking_t::always_t
  771. > : boost::asio::detail::thread_pool_bits
  772. {
  773. BOOST_ASIO_STATIC_CONSTEXPR(bool, is_valid = true);
  774. BOOST_ASIO_STATIC_CONSTEXPR(bool, is_noexcept = false);
  775. typedef boost::asio::thread_pool::basic_executor_type<Allocator,
  776. (Bits & ~blocking_mask) | blocking_always> result_type;
  777. };
  778. template <typename Allocator, unsigned int Bits>
  779. struct require_member<
  780. boost::asio::thread_pool::basic_executor_type<Allocator, Bits>,
  781. boost::asio::execution::blocking_t::never_t
  782. > : boost::asio::detail::thread_pool_bits
  783. {
  784. BOOST_ASIO_STATIC_CONSTEXPR(bool, is_valid = true);
  785. BOOST_ASIO_STATIC_CONSTEXPR(bool, is_noexcept = false);
  786. typedef boost::asio::thread_pool::basic_executor_type<
  787. Allocator, Bits & ~blocking_mask> result_type;
  788. };
  789. template <typename Allocator, unsigned int Bits>
  790. struct require_member<
  791. boost::asio::thread_pool::basic_executor_type<Allocator, Bits>,
  792. boost::asio::execution::relationship_t::fork_t
  793. >
  794. {
  795. BOOST_ASIO_STATIC_CONSTEXPR(bool, is_valid = true);
  796. BOOST_ASIO_STATIC_CONSTEXPR(bool, is_noexcept = false);
  797. typedef boost::asio::thread_pool::basic_executor_type<
  798. Allocator, Bits> result_type;
  799. };
  800. template <typename Allocator, unsigned int Bits>
  801. struct require_member<
  802. boost::asio::thread_pool::basic_executor_type<Allocator, Bits>,
  803. boost::asio::execution::relationship_t::continuation_t
  804. >
  805. {
  806. BOOST_ASIO_STATIC_CONSTEXPR(bool, is_valid = true);
  807. BOOST_ASIO_STATIC_CONSTEXPR(bool, is_noexcept = false);
  808. typedef boost::asio::thread_pool::basic_executor_type<
  809. Allocator, Bits> result_type;
  810. };
  811. template <typename Allocator, unsigned int Bits>
  812. struct require_member<
  813. boost::asio::thread_pool::basic_executor_type<Allocator, Bits>,
  814. boost::asio::execution::outstanding_work_t::tracked_t
  815. > : boost::asio::detail::thread_pool_bits
  816. {
  817. BOOST_ASIO_STATIC_CONSTEXPR(bool, is_valid = true);
  818. BOOST_ASIO_STATIC_CONSTEXPR(bool, is_noexcept = false);
  819. typedef boost::asio::thread_pool::basic_executor_type<
  820. Allocator, Bits | outstanding_work_tracked> result_type;
  821. };
  822. template <typename Allocator, unsigned int Bits>
  823. struct require_member<
  824. boost::asio::thread_pool::basic_executor_type<Allocator, Bits>,
  825. boost::asio::execution::outstanding_work_t::untracked_t
  826. > : boost::asio::detail::thread_pool_bits
  827. {
  828. BOOST_ASIO_STATIC_CONSTEXPR(bool, is_valid = true);
  829. BOOST_ASIO_STATIC_CONSTEXPR(bool, is_noexcept = false);
  830. typedef boost::asio::thread_pool::basic_executor_type<
  831. Allocator, Bits & ~outstanding_work_tracked> result_type;
  832. };
  833. template <typename Allocator, unsigned int Bits>
  834. struct require_member<
  835. boost::asio::thread_pool::basic_executor_type<Allocator, Bits>,
  836. boost::asio::execution::allocator_t<void>
  837. >
  838. {
  839. BOOST_ASIO_STATIC_CONSTEXPR(bool, is_valid = true);
  840. BOOST_ASIO_STATIC_CONSTEXPR(bool, is_noexcept = false);
  841. typedef boost::asio::thread_pool::basic_executor_type<
  842. std::allocator<void>, Bits> result_type;
  843. };
  844. template <unsigned int Bits,
  845. typename Allocator, typename OtherAllocator>
  846. struct require_member<
  847. boost::asio::thread_pool::basic_executor_type<Allocator, Bits>,
  848. boost::asio::execution::allocator_t<OtherAllocator>
  849. >
  850. {
  851. BOOST_ASIO_STATIC_CONSTEXPR(bool, is_valid = true);
  852. BOOST_ASIO_STATIC_CONSTEXPR(bool, is_noexcept = false);
  853. typedef boost::asio::thread_pool::basic_executor_type<
  854. OtherAllocator, Bits> result_type;
  855. };
  856. #endif // !defined(BOOST_ASIO_HAS_DEDUCED_REQUIRE_MEMBER_TRAIT)
  857. #if !defined(BOOST_ASIO_HAS_DEDUCED_QUERY_STATIC_CONSTEXPR_MEMBER_TRAIT)
  858. #if !defined(BOOST_ASIO_NO_DEPRECATED)
  859. template <typename Allocator, unsigned int Bits, typename Property>
  860. struct query_static_constexpr_member<
  861. boost::asio::thread_pool::basic_executor_type<Allocator, Bits>,
  862. Property,
  863. typename boost::asio::enable_if<
  864. boost::asio::is_convertible<
  865. Property,
  866. boost::asio::execution::bulk_guarantee_t
  867. >::value
  868. >::type
  869. >
  870. {
  871. BOOST_ASIO_STATIC_CONSTEXPR(bool, is_valid = true);
  872. BOOST_ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true);
  873. typedef boost::asio::execution::bulk_guarantee_t::parallel_t result_type;
  874. static BOOST_ASIO_CONSTEXPR result_type value() BOOST_ASIO_NOEXCEPT
  875. {
  876. return result_type();
  877. }
  878. };
  879. #endif // !defined(BOOST_ASIO_NO_DEPRECATED)
  880. template <typename Allocator, unsigned int Bits, typename Property>
  881. struct query_static_constexpr_member<
  882. boost::asio::thread_pool::basic_executor_type<Allocator, Bits>,
  883. Property,
  884. typename boost::asio::enable_if<
  885. boost::asio::is_convertible<
  886. Property,
  887. boost::asio::execution::outstanding_work_t
  888. >::value
  889. >::type
  890. > : boost::asio::detail::thread_pool_bits
  891. {
  892. BOOST_ASIO_STATIC_CONSTEXPR(bool, is_valid = true);
  893. BOOST_ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true);
  894. typedef boost::asio::execution::outstanding_work_t result_type;
  895. static BOOST_ASIO_CONSTEXPR result_type value() BOOST_ASIO_NOEXCEPT
  896. {
  897. return (Bits & outstanding_work_tracked)
  898. ? execution::outstanding_work_t(execution::outstanding_work.tracked)
  899. : execution::outstanding_work_t(execution::outstanding_work.untracked);
  900. }
  901. };
  902. template <typename Allocator, unsigned int Bits, typename Property>
  903. struct query_static_constexpr_member<
  904. boost::asio::thread_pool::basic_executor_type<Allocator, Bits>,
  905. Property,
  906. typename boost::asio::enable_if<
  907. boost::asio::is_convertible<
  908. Property,
  909. boost::asio::execution::mapping_t
  910. >::value
  911. >::type
  912. >
  913. {
  914. BOOST_ASIO_STATIC_CONSTEXPR(bool, is_valid = true);
  915. BOOST_ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true);
  916. typedef boost::asio::execution::mapping_t::thread_t result_type;
  917. static BOOST_ASIO_CONSTEXPR result_type value() BOOST_ASIO_NOEXCEPT
  918. {
  919. return result_type();
  920. }
  921. };
  922. #endif // !defined(BOOST_ASIO_HAS_DEDUCED_QUERY_STATIC_CONSTEXPR_MEMBER_TRAIT)
  923. #if !defined(BOOST_ASIO_HAS_DEDUCED_QUERY_MEMBER_TRAIT)
  924. template <typename Allocator, unsigned int Bits, typename Property>
  925. struct query_member<
  926. boost::asio::thread_pool::basic_executor_type<Allocator, Bits>,
  927. Property,
  928. typename boost::asio::enable_if<
  929. boost::asio::is_convertible<
  930. Property,
  931. boost::asio::execution::blocking_t
  932. >::value
  933. >::type
  934. >
  935. {
  936. BOOST_ASIO_STATIC_CONSTEXPR(bool, is_valid = true);
  937. BOOST_ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true);
  938. typedef boost::asio::execution::blocking_t result_type;
  939. };
  940. template <typename Allocator, unsigned int Bits, typename Property>
  941. struct query_member<
  942. boost::asio::thread_pool::basic_executor_type<Allocator, Bits>,
  943. Property,
  944. typename boost::asio::enable_if<
  945. boost::asio::is_convertible<
  946. Property,
  947. boost::asio::execution::relationship_t
  948. >::value
  949. >::type
  950. >
  951. {
  952. BOOST_ASIO_STATIC_CONSTEXPR(bool, is_valid = true);
  953. BOOST_ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true);
  954. typedef boost::asio::execution::relationship_t result_type;
  955. };
  956. template <typename Allocator, unsigned int Bits>
  957. struct query_member<
  958. boost::asio::thread_pool::basic_executor_type<Allocator, Bits>,
  959. boost::asio::execution::occupancy_t
  960. >
  961. {
  962. BOOST_ASIO_STATIC_CONSTEXPR(bool, is_valid = true);
  963. BOOST_ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true);
  964. typedef std::size_t result_type;
  965. };
  966. template <typename Allocator, unsigned int Bits>
  967. struct query_member<
  968. boost::asio::thread_pool::basic_executor_type<Allocator, Bits>,
  969. boost::asio::execution::context_t
  970. >
  971. {
  972. BOOST_ASIO_STATIC_CONSTEXPR(bool, is_valid = true);
  973. BOOST_ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true);
  974. typedef boost::asio::thread_pool& result_type;
  975. };
  976. template <typename Allocator, unsigned int Bits>
  977. struct query_member<
  978. boost::asio::thread_pool::basic_executor_type<Allocator, Bits>,
  979. boost::asio::execution::allocator_t<void>
  980. >
  981. {
  982. BOOST_ASIO_STATIC_CONSTEXPR(bool, is_valid = true);
  983. BOOST_ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true);
  984. typedef Allocator result_type;
  985. };
  986. template <typename Allocator, unsigned int Bits, typename OtherAllocator>
  987. struct query_member<
  988. boost::asio::thread_pool::basic_executor_type<Allocator, Bits>,
  989. boost::asio::execution::allocator_t<OtherAllocator>
  990. >
  991. {
  992. BOOST_ASIO_STATIC_CONSTEXPR(bool, is_valid = true);
  993. BOOST_ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true);
  994. typedef Allocator result_type;
  995. };
  996. #endif // !defined(BOOST_ASIO_HAS_DEDUCED_QUERY_MEMBER_TRAIT)
  997. } // namespace traits
  998. namespace execution {
  999. template <>
  1000. struct is_executor<thread_pool> : false_type
  1001. {
  1002. };
  1003. } // namespace execution
  1004. #endif // !defined(GENERATING_DOCUMENTATION)
  1005. } // namespace asio
  1006. } // namespace boost
  1007. #include <boost/asio/detail/pop_options.hpp>
  1008. #include <boost/asio/impl/thread_pool.hpp>
  1009. #if defined(BOOST_ASIO_HEADER_ONLY)
  1010. # include <boost/asio/impl/thread_pool.ipp>
  1011. #endif // defined(BOOST_ASIO_HEADER_ONLY)
  1012. #endif // BOOST_ASIO_THREAD_POOL_HPP