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 and returns
146  
        Posts the coroutine to the thread pool and returns
147  
        immediately. The caller should suspend after calling
147  
        immediately. The caller should suspend after calling
148  
        this function.
148  
        this function.
149  

149  

150  
        @param h The coroutine handle to execute.
150  
        @param h The coroutine handle to execute.
151  

151  

152  
        @return A noop coroutine handle to resume.
152  
        @return A noop coroutine handle to resume.
153  
    */
153  
    */
154  
    coro
154  
    coro
155  
    dispatch(coro h) const
155  
    dispatch(coro h) const
156  
    {
156  
    {
157  
        post(h);
157  
        post(h);
158  
        return std::noop_coroutine();
158  
        return std::noop_coroutine();
159  
    }
159  
    }
160  

160  

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

162  

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

165  

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

171  

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

179  

180  
//------------------------------------------------------------------------------
180  
//------------------------------------------------------------------------------
181  

181  

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

190  

191  
} // capy
191  
} // capy
192  
} // boost
192  
} // boost
193  

193  

194  
#endif
194  
#endif