Line data 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 <boost/capy/coro.hpp>
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 via stop token. Threads will
84 : finish their current work item before exiting. Does not
85 : wait for 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 : //------------------------------------------------------------------------------
99 :
100 : /** An executor that submits work to a thread_pool.
101 :
102 : Executors are lightweight handles that can be copied and stored.
103 : All copies refer to the same underlying thread pool.
104 :
105 : @par Thread Safety
106 : Distinct objects: Safe.
107 : Shared objects: Safe.
108 : */
109 : class thread_pool::executor_type
110 : {
111 : friend class thread_pool;
112 :
113 : thread_pool* pool_ = nullptr;
114 :
115 : explicit
116 58 : executor_type(thread_pool& pool) noexcept
117 58 : : pool_(&pool)
118 : {
119 58 : }
120 :
121 : public:
122 : /// Default construct a null executor.
123 : executor_type() = default;
124 :
125 : /// Return the underlying thread pool.
126 : thread_pool&
127 27 : context() const noexcept
128 : {
129 27 : return *pool_;
130 : }
131 :
132 : /// Notify that work has started (no-op for thread pools).
133 : void
134 4 : on_work_started() const noexcept
135 : {
136 4 : }
137 :
138 : /// Notify that work has finished (no-op for thread pools).
139 : void
140 4 : on_work_finished() const noexcept
141 : {
142 4 : }
143 :
144 : /** Dispatch a coroutine for execution.
145 :
146 : Posts the coroutine to the thread pool and returns
147 : immediately. The caller should suspend after calling
148 : this function.
149 :
150 : @param h The coroutine handle to execute.
151 :
152 : @return A noop coroutine handle to resume.
153 : */
154 : coro
155 3 : dispatch(coro h) const
156 : {
157 3 : post(h);
158 3 : return std::noop_coroutine();
159 : }
160 :
161 : /** Post a coroutine to the thread pool.
162 :
163 : The coroutine will be resumed on one of the pool's
164 : worker threads.
165 :
166 : @param h The coroutine handle to execute.
167 : */
168 : BOOST_CAPY_DECL
169 : void
170 : post(coro h) const;
171 :
172 : /// Return true if two executors refer to the same thread pool.
173 : bool
174 13 : operator==(executor_type const& other) const noexcept
175 : {
176 13 : return pool_ == other.pool_;
177 : }
178 : };
179 :
180 : //------------------------------------------------------------------------------
181 :
182 : inline
183 : auto
184 58 : thread_pool::
185 : get_executor() const noexcept ->
186 : executor_type
187 : {
188 58 : return executor_type(const_cast<thread_pool&>(*this));
189 : }
190 :
191 : } // capy
192 : } // boost
193 :
194 : #endif
|