thread_pool.hpp 35 KB

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