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