thread_pool.hpp 29 KB

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