thread_pool.hpp 37 KB

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