thread-pool.h revision 1.1.1.2.2.1 1 1.1 christos /* Thread pool
2 1.1 christos
3 1.1.1.2.2.1 perseant Copyright (C) 2019-2024 Free Software Foundation, Inc.
4 1.1 christos
5 1.1 christos This file is part of GDB.
6 1.1 christos
7 1.1 christos This program is free software; you can redistribute it and/or modify
8 1.1 christos it under the terms of the GNU General Public License as published by
9 1.1 christos the Free Software Foundation; either version 3 of the License, or
10 1.1 christos (at your option) any later version.
11 1.1 christos
12 1.1 christos This program is distributed in the hope that it will be useful,
13 1.1 christos but WITHOUT ANY WARRANTY; without even the implied warranty of
14 1.1 christos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 1.1 christos GNU General Public License for more details.
16 1.1 christos
17 1.1 christos You should have received a copy of the GNU General Public License
18 1.1 christos along with this program. If not, see <http://www.gnu.org/licenses/>. */
19 1.1 christos
20 1.1 christos #ifndef GDBSUPPORT_THREAD_POOL_H
21 1.1 christos #define GDBSUPPORT_THREAD_POOL_H
22 1.1 christos
23 1.1 christos #include <queue>
24 1.1 christos #include <vector>
25 1.1 christos #include <functional>
26 1.1.1.2.2.1 perseant #include <chrono>
27 1.1.1.2 christos #if CXX_STD_THREAD
28 1.1.1.2 christos #include <thread>
29 1.1 christos #include <mutex>
30 1.1 christos #include <condition_variable>
31 1.1 christos #include <future>
32 1.1.1.2 christos #endif
33 1.1.1.2.2.1 perseant #include <optional>
34 1.1 christos
35 1.1 christos namespace gdb
36 1.1 christos {
37 1.1 christos
38 1.1.1.2 christos #if CXX_STD_THREAD
39 1.1.1.2 christos
40 1.1.1.2 christos /* Simply use the standard future. */
41 1.1.1.2 christos template<typename T>
42 1.1.1.2 christos using future = std::future<T>;
43 1.1.1.2 christos
44 1.1.1.2.2.1 perseant /* ... and the standard future_status. */
45 1.1.1.2.2.1 perseant using future_status = std::future_status;
46 1.1.1.2.2.1 perseant
47 1.1.1.2 christos #else /* CXX_STD_THREAD */
48 1.1.1.2 christos
49 1.1.1.2.2.1 perseant /* A compatibility enum for std::future_status. This is just the
50 1.1.1.2.2.1 perseant subset needed by gdb. */
51 1.1.1.2.2.1 perseant enum class future_status
52 1.1.1.2.2.1 perseant {
53 1.1.1.2.2.1 perseant ready,
54 1.1.1.2.2.1 perseant timeout,
55 1.1.1.2.2.1 perseant };
56 1.1.1.2.2.1 perseant
57 1.1.1.2 christos /* A compatibility wrapper for std::future. Once <thread> and
58 1.1.1.2 christos <future> are available in all GCC builds -- should that ever happen
59 1.1.1.2 christos -- this can be removed. GCC does not implement threading for
60 1.1.1.2 christos MinGW, see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93687.
61 1.1.1.2 christos
62 1.1.1.2 christos Meanwhile, in this mode, there are no threads. Tasks submitted to
63 1.1.1.2 christos the thread pool are invoked immediately and their result is stored
64 1.1.1.2 christos here. The base template here simply wraps a T and provides some
65 1.1.1.2 christos std::future compatibility methods. The provided methods are chosen
66 1.1.1.2 christos based on what GDB needs presently. */
67 1.1.1.2 christos
68 1.1.1.2 christos template<typename T>
69 1.1.1.2 christos class future
70 1.1.1.2 christos {
71 1.1.1.2 christos public:
72 1.1.1.2 christos
73 1.1.1.2 christos explicit future (T value)
74 1.1.1.2 christos : m_value (std::move (value))
75 1.1.1.2 christos {
76 1.1.1.2 christos }
77 1.1.1.2 christos
78 1.1.1.2 christos future () = default;
79 1.1.1.2 christos future (future &&other) = default;
80 1.1.1.2 christos future (const future &other) = delete;
81 1.1.1.2 christos future &operator= (future &&other) = default;
82 1.1.1.2 christos future &operator= (const future &other) = delete;
83 1.1.1.2 christos
84 1.1.1.2 christos void wait () const { }
85 1.1.1.2 christos
86 1.1.1.2.2.1 perseant template<class Rep, class Period>
87 1.1.1.2.2.1 perseant future_status wait_for (const std::chrono::duration<Rep,Period> &duration)
88 1.1.1.2.2.1 perseant const
89 1.1.1.2.2.1 perseant {
90 1.1.1.2.2.1 perseant return future_status::ready;
91 1.1.1.2.2.1 perseant }
92 1.1.1.2.2.1 perseant
93 1.1.1.2 christos T get () { return std::move (m_value); }
94 1.1.1.2 christos
95 1.1.1.2 christos private:
96 1.1.1.2 christos
97 1.1.1.2 christos T m_value;
98 1.1.1.2 christos };
99 1.1.1.2 christos
100 1.1.1.2 christos /* A specialization for void. */
101 1.1.1.2 christos
102 1.1.1.2 christos template<>
103 1.1.1.2 christos class future<void>
104 1.1.1.2 christos {
105 1.1.1.2 christos public:
106 1.1.1.2 christos void wait () const { }
107 1.1.1.2.2.1 perseant
108 1.1.1.2.2.1 perseant template<class Rep, class Period>
109 1.1.1.2.2.1 perseant future_status wait_for (const std::chrono::duration<Rep,Period> &duration)
110 1.1.1.2.2.1 perseant const
111 1.1.1.2.2.1 perseant {
112 1.1.1.2.2.1 perseant return future_status::ready;
113 1.1.1.2.2.1 perseant }
114 1.1.1.2.2.1 perseant
115 1.1.1.2 christos void get () { }
116 1.1.1.2 christos };
117 1.1.1.2 christos
118 1.1.1.2 christos #endif /* CXX_STD_THREAD */
119 1.1.1.2 christos
120 1.1.1.2 christos
121 1.1 christos /* A thread pool.
122 1.1 christos
123 1.1 christos There is a single global thread pool, see g_thread_pool. Tasks can
124 1.1 christos be submitted to the thread pool. They will be processed in worker
125 1.1 christos threads as time allows. */
126 1.1 christos class thread_pool
127 1.1 christos {
128 1.1 christos public:
129 1.1 christos /* The sole global thread pool. */
130 1.1 christos static thread_pool *g_thread_pool;
131 1.1 christos
132 1.1 christos ~thread_pool ();
133 1.1 christos DISABLE_COPY_AND_ASSIGN (thread_pool);
134 1.1 christos
135 1.1 christos /* Set the thread count of this thread pool. By default, no threads
136 1.1 christos are created -- the thread count must be set first. */
137 1.1 christos void set_thread_count (size_t num_threads);
138 1.1 christos
139 1.1 christos /* Return the number of executing threads. */
140 1.1 christos size_t thread_count () const
141 1.1 christos {
142 1.1.1.2 christos #if CXX_STD_THREAD
143 1.1 christos return m_thread_count;
144 1.1.1.2 christos #else
145 1.1.1.2 christos return 0;
146 1.1.1.2 christos #endif
147 1.1 christos }
148 1.1 christos
149 1.1 christos /* Post a task to the thread pool. A future is returned, which can
150 1.1 christos be used to wait for the result. */
151 1.1.1.2 christos future<void> post_task (std::function<void ()> &&func)
152 1.1.1.2 christos {
153 1.1.1.2 christos #if CXX_STD_THREAD
154 1.1.1.2 christos std::packaged_task<void ()> task (std::move (func));
155 1.1.1.2 christos future<void> result = task.get_future ();
156 1.1.1.2 christos do_post_task (std::packaged_task<void ()> (std::move (task)));
157 1.1.1.2 christos return result;
158 1.1.1.2 christos #else
159 1.1.1.2 christos func ();
160 1.1.1.2 christos return {};
161 1.1.1.2 christos #endif /* CXX_STD_THREAD */
162 1.1.1.2 christos }
163 1.1.1.2 christos
164 1.1.1.2 christos /* Post a task to the thread pool. A future is returned, which can
165 1.1.1.2 christos be used to wait for the result. */
166 1.1.1.2 christos template<typename T>
167 1.1.1.2 christos future<T> post_task (std::function<T ()> &&func)
168 1.1.1.2 christos {
169 1.1.1.2 christos #if CXX_STD_THREAD
170 1.1.1.2 christos std::packaged_task<T ()> task (std::move (func));
171 1.1.1.2 christos future<T> result = task.get_future ();
172 1.1.1.2 christos do_post_task (std::packaged_task<void ()> (std::move (task)));
173 1.1.1.2 christos return result;
174 1.1.1.2 christos #else
175 1.1.1.2 christos return future<T> (func ());
176 1.1.1.2 christos #endif /* CXX_STD_THREAD */
177 1.1.1.2 christos }
178 1.1 christos
179 1.1 christos private:
180 1.1 christos
181 1.1 christos thread_pool () = default;
182 1.1 christos
183 1.1.1.2 christos #if CXX_STD_THREAD
184 1.1 christos /* The callback for each worker thread. */
185 1.1 christos void thread_function ();
186 1.1 christos
187 1.1.1.2 christos /* Post a task to the thread pool. A future is returned, which can
188 1.1.1.2 christos be used to wait for the result. */
189 1.1.1.2 christos void do_post_task (std::packaged_task<void ()> &&func);
190 1.1.1.2 christos
191 1.1 christos /* The current thread count. */
192 1.1 christos size_t m_thread_count = 0;
193 1.1 christos
194 1.1 christos /* A convenience typedef for the type of a task. */
195 1.1.1.2 christos typedef std::packaged_task<void ()> task_t;
196 1.1 christos
197 1.1 christos /* The tasks that have not been processed yet. An optional is used
198 1.1 christos to represent a task. If the optional is empty, then this means
199 1.1 christos that the receiving thread should terminate. If the optional is
200 1.1 christos non-empty, then it is an actual task to evaluate. */
201 1.1.1.2.2.1 perseant std::queue<std::optional<task_t>> m_tasks;
202 1.1 christos
203 1.1 christos /* A condition variable and mutex that are used for communication
204 1.1 christos between the main thread and the worker threads. */
205 1.1 christos std::condition_variable m_tasks_cv;
206 1.1 christos std::mutex m_tasks_mutex;
207 1.1.1.2.2.1 perseant bool m_sized_at_least_once = false;
208 1.1.1.2 christos #endif /* CXX_STD_THREAD */
209 1.1 christos };
210 1.1 christos
211 1.1 christos }
212 1.1 christos
213 1.1 christos #endif /* GDBSUPPORT_THREAD_POOL_H */
214