include/boost/capy/ex/thread_pool.hpp

100.0% Lines (14/14) 100.0% Functions (6/6)
Line TLA Hits Source Code
1 //
2 // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3 // Copyright (c) 2026 Michael Vandeberg
4 //
5 // Distributed under the Boost Software License, Version 1.0. (See accompanying
6 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 //
8 // Official repository: https://github.com/boostorg/capy
9 //
10
11 #ifndef BOOST_CAPY_EX_THREAD_POOL_HPP
12 #define BOOST_CAPY_EX_THREAD_POOL_HPP
13
14 #include <boost/capy/detail/config.hpp>
15 #include <coroutine>
16 #include <boost/capy/ex/execution_context.hpp>
17 #include <cstddef>
18 #include <string_view>
19
20 namespace boost {
21 namespace capy {
22
23 /** A pool of threads for executing work concurrently.
24
25 Use this when you need to run coroutines on multiple threads
26 without the overhead of creating and destroying threads for
27 each task. Work items are distributed across the pool using
28 a shared queue.
29
30 @par Thread Safety
31 Distinct objects: Safe.
32 Shared objects: Unsafe.
33
34 @par Example
35 @code
36 thread_pool pool(4); // 4 worker threads
37 auto ex = pool.get_executor();
38 ex.post(some_coroutine);
39 // pool destructor waits for all work to complete
40 @endcode
41 */
42 class BOOST_CAPY_DECL
43 thread_pool
44 : public execution_context
45 {
46 class impl;
47 impl* impl_;
48
49 public:
50 class executor_type;
51
52 /** Destroy the thread pool.
53
54 Signals all worker threads to stop, waits for them to
55 finish, and destroys any pending work items.
56 */
57 ~thread_pool();
58
59 /** Construct a thread pool.
60
61 Creates a pool with the specified number of worker threads.
62 If `num_threads` is zero, the number of threads is set to
63 the hardware concurrency, or one if that cannot be determined.
64
65 @param num_threads The number of worker threads, or zero
66 for automatic selection.
67
68 @param thread_name_prefix The prefix for worker thread names.
69 Thread names appear as "{prefix}0", "{prefix}1", etc.
70 The prefix is truncated to 12 characters. Defaults to
71 "capy-pool-".
72 */
73 explicit
74 thread_pool(
75 std::size_t num_threads = 0,
76 std::string_view thread_name_prefix = "capy-pool-");
77
78 thread_pool(thread_pool const&) = delete;
79 thread_pool& operator=(thread_pool const&) = delete;
80
81 /** Request all worker threads to stop.
82
83 Signals all threads to exit. Threads will finish their
84 current work item before exiting. Does not wait for
85 threads to exit.
86 */
87 void
88 stop() noexcept;
89
90 /** Return an executor for this thread pool.
91
92 @return An executor associated with this thread pool.
93 */
94 executor_type
95 get_executor() const noexcept;
96 };
97
98 /** An executor that submits work to a thread_pool.
99
100 Executors are lightweight handles that can be copied and stored.
101 All copies refer to the same underlying thread pool.
102
103 @par Thread Safety
104 Distinct objects: Safe.
105 Shared objects: Safe.
106 */
107 class thread_pool::executor_type
108 {
109 friend class thread_pool;
110
111 thread_pool* pool_ = nullptr;
112
113 explicit
114 63x executor_type(thread_pool& pool) noexcept
115 63x : pool_(&pool)
116 {
117 63x }
118
119 public:
120 /** Construct a default null executor.
121
122 The resulting executor is not associated with any pool.
123 `context()`, `dispatch()`, and `post()` require the
124 executor to be associated with a pool before use.
125 */
126 executor_type() = default;
127
128 /// Return the underlying thread pool.
129 thread_pool&
130 33x context() const noexcept
131 {
132 33x return *pool_;
133 }
134
135 /// Notify that work has started (no-op for thread pools).
136 void
137 8x on_work_started() const noexcept
138 {
139 8x }
140
141 /// Notify that work has finished (no-op for thread pools).
142 void
143 8x on_work_finished() const noexcept
144 {
145 8x }
146
147 /** Dispatch a coroutine for execution.
148
149 Posts the coroutine to the thread pool for execution on a
150 worker thread and returns `std::noop_coroutine()`. Thread
151 pools never execute inline because no single thread "owns"
152 the pool.
153
154 @param h The coroutine handle to execute.
155
156 @return `std::noop_coroutine()` always.
157 */
158 std::coroutine_handle<>
159 6x dispatch(std::coroutine_handle<> h) const
160 {
161 6x post(h);
162 6x return std::noop_coroutine();
163 }
164
165 /** Post a coroutine to the thread pool.
166
167 The coroutine will be resumed on one of the pool's
168 worker threads.
169
170 @param h The coroutine handle to execute.
171 */
172 BOOST_CAPY_DECL
173 void
174 post(std::coroutine_handle<> h) const;
175
176 /// Return true if two executors refer to the same thread pool.
177 bool
178 13x operator==(executor_type const& other) const noexcept
179 {
180 13x return pool_ == other.pool_;
181 }
182 };
183
184 } // capy
185 } // boost
186
187 #endif
188