1  
//
1  
//
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3  
// Copyright (c) 2026 Michael Vandeberg
3  
// Copyright (c) 2026 Michael Vandeberg
4  
//
4  
//
5  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
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)
6  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7  
//
7  
//
8  
// Official repository: https://github.com/boostorg/capy
8  
// Official repository: https://github.com/boostorg/capy
9  
//
9  
//
10  

10  

11  
#ifndef BOOST_CAPY_EX_THREAD_POOL_HPP
11  
#ifndef BOOST_CAPY_EX_THREAD_POOL_HPP
12  
#define BOOST_CAPY_EX_THREAD_POOL_HPP
12  
#define BOOST_CAPY_EX_THREAD_POOL_HPP
13  

13  

14  
#include <boost/capy/detail/config.hpp>
14  
#include <boost/capy/detail/config.hpp>
15  
#include <boost/capy/coro.hpp>
15  
#include <boost/capy/coro.hpp>
16  
#include <boost/capy/ex/execution_context.hpp>
16  
#include <boost/capy/ex/execution_context.hpp>
17  
#include <cstddef>
17  
#include <cstddef>
18  
#include <string_view>
18  
#include <string_view>
19  

19  

20  
namespace boost {
20  
namespace boost {
21  
namespace capy {
21  
namespace capy {
22  

22  

23  
/** A pool of threads for executing work concurrently.
23  
/** A pool of threads for executing work concurrently.
24  

24  

25  
    Use this when you need to run coroutines on multiple threads
25  
    Use this when you need to run coroutines on multiple threads
26  
    without the overhead of creating and destroying threads for
26  
    without the overhead of creating and destroying threads for
27  
    each task. Work items are distributed across the pool using
27  
    each task. Work items are distributed across the pool using
28  
    a shared queue.
28  
    a shared queue.
29  

29  

30  
    @par Thread Safety
30  
    @par Thread Safety
31  
    Distinct objects: Safe.
31  
    Distinct objects: Safe.
32  
    Shared objects: Unsafe.
32  
    Shared objects: Unsafe.
33  

33  

34  
    @par Example
34  
    @par Example
35  
    @code
35  
    @code
36  
    thread_pool pool(4);  // 4 worker threads
36  
    thread_pool pool(4);  // 4 worker threads
37  
    auto ex = pool.get_executor();
37  
    auto ex = pool.get_executor();
38  
    ex.post(some_coroutine);
38  
    ex.post(some_coroutine);
39  
    // pool destructor waits for all work to complete
39  
    // pool destructor waits for all work to complete
40  
    @endcode
40  
    @endcode
41  
*/
41  
*/
42  
class BOOST_CAPY_DECL
42  
class BOOST_CAPY_DECL
43  
    thread_pool
43  
    thread_pool
44  
    : public execution_context
44  
    : public execution_context
45  
{
45  
{
46  
    class impl;
46  
    class impl;
47  
    impl* impl_;
47  
    impl* impl_;
48  

48  

49  
public:
49  
public:
50  
    class executor_type;
50  
    class executor_type;
51  

51  

52  
    /** Destroy the thread pool.
52  
    /** Destroy the thread pool.
53  

53  

54  
        Signals all worker threads to stop, waits for them to
54  
        Signals all worker threads to stop, waits for them to
55  
        finish, and destroys any pending work items.
55  
        finish, and destroys any pending work items.
56  
    */
56  
    */
57  
    ~thread_pool();
57  
    ~thread_pool();
58  

58  

59  
    /** Construct a thread pool.
59  
    /** Construct a thread pool.
60  

60  

61  
        Creates a pool with the specified number of worker threads.
61  
        Creates a pool with the specified number of worker threads.
62  
        If `num_threads` is zero, the number of threads is set to
62  
        If `num_threads` is zero, the number of threads is set to
63  
        the hardware concurrency, or one if that cannot be determined.
63  
        the hardware concurrency, or one if that cannot be determined.
64  

64  

65  
        @param num_threads The number of worker threads, or zero
65  
        @param num_threads The number of worker threads, or zero
66  
            for automatic selection.
66  
            for automatic selection.
67  

67  

68  
        @param thread_name_prefix The prefix for worker thread names.
68  
        @param thread_name_prefix The prefix for worker thread names.
69  
            Thread names appear as "{prefix}0", "{prefix}1", etc.
69  
            Thread names appear as "{prefix}0", "{prefix}1", etc.
70  
            The prefix is truncated to 12 characters. Defaults to
70  
            The prefix is truncated to 12 characters. Defaults to
71  
            "capy-pool-".
71  
            "capy-pool-".
72  
    */
72  
    */
73  
    explicit
73  
    explicit
74  
    thread_pool(
74  
    thread_pool(
75  
        std::size_t num_threads = 0,
75  
        std::size_t num_threads = 0,
76  
        std::string_view thread_name_prefix = "capy-pool-");
76  
        std::string_view thread_name_prefix = "capy-pool-");
77  

77  

78  
    thread_pool(thread_pool const&) = delete;
78  
    thread_pool(thread_pool const&) = delete;
79  
    thread_pool& operator=(thread_pool const&) = delete;
79  
    thread_pool& operator=(thread_pool const&) = delete;
80  

80  

81  
    /** Request all worker threads to stop.
81  
    /** Request all worker threads to stop.
82  

82  

83  
        Signals all threads to exit. Threads will finish their
83  
        Signals all threads to exit. Threads will finish their
84  
        current work item before exiting. Does not wait for
84  
        current work item before exiting. Does not wait for
85  
        threads to exit.
85  
        threads to exit.
86  
    */
86  
    */
87  
    void
87  
    void
88  
    stop() noexcept;
88  
    stop() noexcept;
89  

89  

90  
    /** Return an executor for this thread pool.
90  
    /** Return an executor for this thread pool.
91  

91  

92  
        @return An executor associated with this thread pool.
92  
        @return An executor associated with this thread pool.
93  
    */
93  
    */
94  
    executor_type
94  
    executor_type
95  
    get_executor() const noexcept;
95  
    get_executor() const noexcept;
96  
};
96  
};
97  

97  

98  
//------------------------------------------------------------------------------
98  
//------------------------------------------------------------------------------
99  

99  

100  
/** An executor that submits work to a thread_pool.
100  
/** An executor that submits work to a thread_pool.
101  

101  

102  
    Executors are lightweight handles that can be copied and stored.
102  
    Executors are lightweight handles that can be copied and stored.
103  
    All copies refer to the same underlying thread pool.
103  
    All copies refer to the same underlying thread pool.
104  

104  

105  
    @par Thread Safety
105  
    @par Thread Safety
106  
    Distinct objects: Safe.
106  
    Distinct objects: Safe.
107  
    Shared objects: Safe.
107  
    Shared objects: Safe.
108  
*/
108  
*/
109  
class thread_pool::executor_type
109  
class thread_pool::executor_type
110  
{
110  
{
111  
    friend class thread_pool;
111  
    friend class thread_pool;
112  

112  

113  
    thread_pool* pool_ = nullptr;
113  
    thread_pool* pool_ = nullptr;
114  

114  

115  
    explicit
115  
    explicit
116  
    executor_type(thread_pool& pool) noexcept
116  
    executor_type(thread_pool& pool) noexcept
117  
        : pool_(&pool)
117  
        : pool_(&pool)
118  
    {
118  
    {
119  
    }
119  
    }
120  

120  

121  
public:
121  
public:
122  
    /// Default construct a null executor.
122  
    /// Default construct a null executor.
123  
    executor_type() = default;
123  
    executor_type() = default;
124  

124  

125  
    /// Return the underlying thread pool.
125  
    /// Return the underlying thread pool.
126  
    thread_pool&
126  
    thread_pool&
127  
    context() const noexcept
127  
    context() const noexcept
128  
    {
128  
    {
129  
        return *pool_;
129  
        return *pool_;
130  
    }
130  
    }
131  

131  

132  
    /// Notify that work has started (no-op for thread pools).
132  
    /// Notify that work has started (no-op for thread pools).
133  
    void
133  
    void
134  
    on_work_started() const noexcept
134  
    on_work_started() const noexcept
135  
    {
135  
    {
136  
    }
136  
    }
137  

137  

138  
    /// Notify that work has finished (no-op for thread pools).
138  
    /// Notify that work has finished (no-op for thread pools).
139  
    void
139  
    void
140  
    on_work_finished() const noexcept
140  
    on_work_finished() const noexcept
141  
    {
141  
    {
142  
    }
142  
    }
143  

143  

144  
    /** Dispatch a coroutine for execution.
144  
    /** Dispatch a coroutine for execution.
145  

145  

146  
        Posts the coroutine to the thread pool for execution on a
146  
        Posts the coroutine to the thread pool for execution on a
147  
        worker thread. Unlike single-threaded contexts, thread pools
147  
        worker thread. Unlike single-threaded contexts, thread pools
148  
        never execute inline because no single thread "owns" the pool.
148  
        never execute inline because no single thread "owns" the pool.
149  

149  

150  
        After this function returns, the state of `h` is unspecified.
150  
        After this function returns, the state of `h` is unspecified.
151  
        The coroutine may have already started executing on another
151  
        The coroutine may have already started executing on another
152  
        thread, or may be queued for later execution.
152  
        thread, or may be queued for later execution.
153  

153  

154  
        @param h The coroutine handle to execute.
154  
        @param h The coroutine handle to execute.
155  
    */
155  
    */
156  
    void
156  
    void
157  
    dispatch(coro h) const
157  
    dispatch(coro h) const
158  
    {
158  
    {
159  
        post(h);
159  
        post(h);
160  
    }
160  
    }
161  

161  

162  
    /** Post a coroutine to the thread pool.
162  
    /** Post a coroutine to the thread pool.
163  

163  

164  
        The coroutine will be resumed on one of the pool's
164  
        The coroutine will be resumed on one of the pool's
165  
        worker threads.
165  
        worker threads.
166  

166  

167  
        @param h The coroutine handle to execute.
167  
        @param h The coroutine handle to execute.
168  
    */
168  
    */
169  
    BOOST_CAPY_DECL
169  
    BOOST_CAPY_DECL
170  
    void
170  
    void
171  
    post(coro h) const;
171  
    post(coro h) const;
172  

172  

173  
    /// Return true if two executors refer to the same thread pool.
173  
    /// Return true if two executors refer to the same thread pool.
174  
    bool
174  
    bool
175  
    operator==(executor_type const& other) const noexcept
175  
    operator==(executor_type const& other) const noexcept
176  
    {
176  
    {
177  
        return pool_ == other.pool_;
177  
        return pool_ == other.pool_;
178  
    }
178  
    }
179  
};
179  
};
180  

180  

181  
//------------------------------------------------------------------------------
181  
//------------------------------------------------------------------------------
182  

182  

183  
inline
183  
inline
184  
auto
184  
auto
185  
thread_pool::
185  
thread_pool::
186  
get_executor() const noexcept ->
186  
get_executor() const noexcept ->
187  
    executor_type
187  
    executor_type
188  
{
188  
{
189  
    return executor_type(const_cast<thread_pool&>(*this));
189  
    return executor_type(const_cast<thread_pool&>(*this));
190  
}
190  
}
191  

191  

192  
} // capy
192  
} // capy
193  
} // boost
193  
} // boost
194  

194  

195  
#endif
195  
#endif