Line data Source code
1 : //
2 : // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3 : //
4 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 : //
7 : // Official repository: https://github.com/boostorg/capy
8 : //
9 :
10 : #ifndef BOOST_CAPY_EX_THREAD_POOL_HPP
11 : #define BOOST_CAPY_EX_THREAD_POOL_HPP
12 :
13 : #include <boost/capy/detail/config.hpp>
14 : #include <boost/capy/ex/any_coro.hpp>
15 : #include <boost/capy/ex/execution_context.hpp>
16 : #include <cstddef>
17 :
18 : namespace boost {
19 : namespace capy {
20 :
21 : /** A thread pool execution context for running work asynchronously.
22 :
23 : This class provides a pool of worker threads that execute
24 : submitted work items. It inherits from `execution_context`,
25 : providing service management and a nested `executor_type`
26 : that satisfies the `capy::executor` concept.
27 :
28 : Work is submitted via the executor obtained from `get_executor()`.
29 : The executor's `post()`, `dispatch()`, and `defer()` functions
30 : queue coroutines for execution on pool threads.
31 :
32 : @par Thread Safety
33 : All member functions may be called concurrently.
34 :
35 : @par Example
36 : @code
37 : thread_pool pool(4); // 4 worker threads
38 : auto ex = pool.get_executor();
39 :
40 : // Post a coroutine for execution
41 : ex.post(my_coroutine_handle);
42 : @endcode
43 :
44 : @see execution_context, executor
45 : */
46 : class BOOST_CAPY_DECL
47 : thread_pool
48 : : public execution_context
49 : {
50 : class impl;
51 : impl* impl_;
52 :
53 : public:
54 : class executor_type;
55 :
56 : /** Destructor.
57 :
58 : Signals all threads to stop and waits for them to complete.
59 : Calls `shutdown()` and `destroy()` to clean up services.
60 : */
61 : ~thread_pool();
62 :
63 : /** Construct a thread pool.
64 :
65 : @param num_threads The number of worker threads to create.
66 : If zero, defaults to the hardware concurrency.
67 : */
68 : explicit
69 : thread_pool(std::size_t num_threads = 0);
70 :
71 : thread_pool(thread_pool const&) = delete;
72 : thread_pool& operator=(thread_pool const&) = delete;
73 :
74 : /** Return an executor for this thread pool.
75 :
76 : The returned executor can be used to post work items
77 : to this thread pool.
78 :
79 : @return An executor associated with this thread pool.
80 : */
81 : executor_type
82 : get_executor() const noexcept;
83 : };
84 :
85 : //------------------------------------------------------------------------------
86 :
87 : /** An executor for dispatching work to a thread_pool.
88 :
89 : The executor provides the interface for posting work items
90 : to the associated thread_pool. It satisfies the `capy::executor`
91 : concept.
92 :
93 : Executors are lightweight handles that can be copied and compared
94 : for equality. Two executors compare equal if they refer to the
95 : same thread_pool.
96 :
97 : @par Thread Safety
98 : Distinct objects: Safe.@n
99 : Shared objects: Safe.
100 : */
101 : class thread_pool::executor_type
102 : {
103 : friend class thread_pool;
104 :
105 : thread_pool* pool_ = nullptr;
106 :
107 : explicit
108 11 : executor_type(thread_pool& pool) noexcept
109 11 : : pool_(&pool)
110 : {
111 11 : }
112 :
113 : public:
114 : /** Default constructor.
115 :
116 : Constructs an executor not associated with any thread_pool.
117 : */
118 : executor_type() = default;
119 :
120 : /** Return a reference to the associated execution context.
121 :
122 : @return Reference to the thread_pool.
123 : */
124 : thread_pool&
125 1 : context() const noexcept
126 : {
127 1 : return *pool_;
128 : }
129 :
130 : /** Informs the executor that work is beginning.
131 :
132 : Must be paired with `on_work_finished()`.
133 : */
134 : BOOST_CAPY_DECL
135 : void
136 : on_work_started() const noexcept;
137 :
138 : /** Informs the executor that work has completed.
139 :
140 : @par Preconditions
141 : A preceding call to `on_work_started()` on an equal executor.
142 : */
143 : BOOST_CAPY_DECL
144 : void
145 : on_work_finished() const noexcept;
146 :
147 : /** Dispatch a coroutine handle.
148 :
149 : For thread_pool, dispatch always posts the work since
150 : the calling thread is never "inside" the pool's run loop.
151 :
152 : @param h The coroutine handle to dispatch.
153 :
154 : @return `std::noop_coroutine()` since work is always posted.
155 : */
156 : any_coro
157 1 : dispatch(any_coro h) const
158 : {
159 1 : post(h);
160 1 : return std::noop_coroutine();
161 : }
162 :
163 : /** Post a coroutine for deferred execution.
164 :
165 : The coroutine will be resumed on one of the pool's
166 : worker threads.
167 :
168 : @param h The coroutine handle to post.
169 : */
170 : BOOST_CAPY_DECL
171 : void
172 : post(any_coro h) const;
173 :
174 : /** Queue a coroutine for deferred execution.
175 :
176 : This is semantically identical to `post`, but conveys that
177 : `h` is a continuation of the current call context.
178 :
179 : @param h The coroutine handle to defer.
180 : */
181 : void
182 1 : defer(any_coro h) const
183 : {
184 1 : post(h);
185 1 : }
186 :
187 : /** Compare two executors for equality.
188 :
189 : @return `true` if both executors refer to the same thread_pool.
190 : */
191 : bool
192 3 : operator==(executor_type const& other) const noexcept
193 : {
194 3 : return pool_ == other.pool_;
195 : }
196 : };
197 :
198 : //------------------------------------------------------------------------------
199 :
200 : inline
201 : auto
202 11 : thread_pool::
203 : get_executor() const noexcept ->
204 : executor_type
205 : {
206 11 : return executor_type(const_cast<thread_pool&>(*this));
207 : }
208 :
209 : } // capy
210 : } // boost
211 :
212 : #endif
|