thread_pool.hpp 35 KB

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