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 <coroutine>
15  
#include <coroutine>
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 -
//------------------------------------------------------------------------------
 
98 -

 
99  

97  

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

99  

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

102  

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

110  

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

112  

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

118  

121  
public:
119  
public:
122 -
    /// Default construct a null executor.
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 +
    */
123  
    executor_type() = default;
126  
    executor_type() = default;
124  

127  

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

134  

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

140  

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

146  

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

148  

146  
        Posts the coroutine to the thread pool for execution on a
149  
        Posts the coroutine to the thread pool for execution on a
147  
        worker thread and returns `std::noop_coroutine()`. Thread
150  
        worker thread and returns `std::noop_coroutine()`. Thread
148  
        pools never execute inline because no single thread "owns"
151  
        pools never execute inline because no single thread "owns"
149  
        the pool.
152  
        the pool.
150  

153  

151  
        @param h The coroutine handle to execute.
154  
        @param h The coroutine handle to execute.
152  

155  

153  
        @return `std::noop_coroutine()` always.
156  
        @return `std::noop_coroutine()` always.
154  
    */
157  
    */
155  
    std::coroutine_handle<>
158  
    std::coroutine_handle<>
156  
    dispatch(std::coroutine_handle<> h) const
159  
    dispatch(std::coroutine_handle<> h) const
157  
    {
160  
    {
158  
        post(h);
161  
        post(h);
159  
        return std::noop_coroutine();
162  
        return std::noop_coroutine();
160  
    }
163  
    }
161  

164  

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

166  

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

169  

167  
        @param h The coroutine handle to execute.
170  
        @param h The coroutine handle to execute.
168  
    */
171  
    */
169  
    BOOST_CAPY_DECL
172  
    BOOST_CAPY_DECL
170  
    void
173  
    void
171  
    post(std::coroutine_handle<> h) const;
174  
    post(std::coroutine_handle<> h) const;
172  

175  

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

183  

181  
} // capy
184  
} // capy
182  
} // boost
185  
} // boost
183  

186  

184  
#endif
187  
#endif