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

source code of boost/libs/asio/include/boost/asio/thread_pool.hpp