]>
Commit | Line | Data |
---|---|---|
a0b57563 CB |
1 | /* Thread pool |
2 | ||
213516ef | 3 | Copyright (C) 2019-2023 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> | |
0981fe10 TT |
26 | #if CXX_STD_THREAD |
27 | #include <thread> | |
a0b57563 CB |
28 | #include <mutex> |
29 | #include <condition_variable> | |
30 | #include <future> | |
20c4eb42 | 31 | #endif |
a0b57563 CB |
32 | #include "gdbsupport/gdb_optional.h" |
33 | ||
34 | namespace gdb | |
35 | { | |
36 | ||
20c4eb42 TT |
37 | #if CXX_STD_THREAD |
38 | ||
39 | /* Simply use the standard future. */ | |
40 | template<typename T> | |
41 | using future = std::future<T>; | |
42 | ||
43 | #else /* CXX_STD_THREAD */ | |
44 | ||
45 | /* A compatibility wrapper for std::future. Once <thread> and | |
46 | <future> are available in all GCC builds -- should that ever happen | |
47 | -- this can be removed. GCC does not implement threading for | |
48 | MinGW, see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93687. | |
49 | ||
50 | Meanwhile, in this mode, there are no threads. Tasks submitted to | |
51 | the thread pool are invoked immediately and their result is stored | |
52 | here. The base template here simply wraps a T and provides some | |
53 | std::future compatibility methods. The provided methods are chosen | |
54 | based on what GDB needs presently. */ | |
55 | ||
56 | template<typename T> | |
57 | class future | |
58 | { | |
59 | public: | |
60 | ||
61 | explicit future (T value) | |
62 | : m_value (std::move (value)) | |
63 | { | |
64 | } | |
65 | ||
66 | future () = default; | |
67 | future (future &&other) = default; | |
68 | future (const future &other) = delete; | |
69 | future &operator= (future &&other) = default; | |
70 | future &operator= (const future &other) = delete; | |
71 | ||
72 | void wait () const { } | |
73 | ||
74 | T get () { return std::move (m_value); } | |
75 | ||
76 | private: | |
77 | ||
78 | T m_value; | |
79 | }; | |
80 | ||
81 | /* A specialization for void. */ | |
82 | ||
83 | template<> | |
84 | class future<void> | |
85 | { | |
86 | public: | |
87 | void wait () const { } | |
88 | void get () { } | |
89 | }; | |
90 | ||
91 | #endif /* CXX_STD_THREAD */ | |
92 | ||
93 | ||
a0b57563 CB |
94 | /* A thread pool. |
95 | ||
96 | There is a single global thread pool, see g_thread_pool. Tasks can | |
97 | be submitted to the thread pool. They will be processed in worker | |
98 | threads as time allows. */ | |
99 | class thread_pool | |
100 | { | |
101 | public: | |
102 | /* The sole global thread pool. */ | |
103 | static thread_pool *g_thread_pool; | |
104 | ||
105 | ~thread_pool (); | |
106 | DISABLE_COPY_AND_ASSIGN (thread_pool); | |
107 | ||
108 | /* Set the thread count of this thread pool. By default, no threads | |
109 | are created -- the thread count must be set first. */ | |
110 | void set_thread_count (size_t num_threads); | |
111 | ||
112 | /* Return the number of executing threads. */ | |
113 | size_t thread_count () const | |
114 | { | |
0981fe10 | 115 | #if CXX_STD_THREAD |
a0b57563 | 116 | return m_thread_count; |
0981fe10 TT |
117 | #else |
118 | return 0; | |
119 | #endif | |
a0b57563 CB |
120 | } |
121 | ||
122 | /* Post a task to the thread pool. A future is returned, which can | |
123 | be used to wait for the result. */ | |
20c4eb42 | 124 | future<void> post_task (std::function<void ()> &&func) |
f4565e4c | 125 | { |
20c4eb42 | 126 | #if CXX_STD_THREAD |
f4565e4c | 127 | std::packaged_task<void ()> task (std::move (func)); |
20c4eb42 | 128 | future<void> result = task.get_future (); |
f4565e4c TT |
129 | do_post_task (std::packaged_task<void ()> (std::move (task))); |
130 | return result; | |
20c4eb42 TT |
131 | #else |
132 | func (); | |
133 | return {}; | |
134 | #endif /* CXX_STD_THREAD */ | |
f4565e4c TT |
135 | } |
136 | ||
137 | /* Post a task to the thread pool. A future is returned, which can | |
138 | be used to wait for the result. */ | |
139 | template<typename T> | |
20c4eb42 | 140 | future<T> post_task (std::function<T ()> &&func) |
f4565e4c | 141 | { |
20c4eb42 | 142 | #if CXX_STD_THREAD |
f4565e4c | 143 | std::packaged_task<T ()> task (std::move (func)); |
20c4eb42 | 144 | future<T> result = task.get_future (); |
f4565e4c TT |
145 | do_post_task (std::packaged_task<void ()> (std::move (task))); |
146 | return result; | |
20c4eb42 TT |
147 | #else |
148 | return future<T> (func ()); | |
149 | #endif /* CXX_STD_THREAD */ | |
f4565e4c | 150 | } |
a0b57563 CB |
151 | |
152 | private: | |
153 | ||
154 | thread_pool () = default; | |
155 | ||
0981fe10 | 156 | #if CXX_STD_THREAD |
a0b57563 CB |
157 | /* The callback for each worker thread. */ |
158 | void thread_function (); | |
159 | ||
f4565e4c TT |
160 | /* Post a task to the thread pool. A future is returned, which can |
161 | be used to wait for the result. */ | |
162 | void do_post_task (std::packaged_task<void ()> &&func); | |
163 | ||
a0b57563 CB |
164 | /* The current thread count. */ |
165 | size_t m_thread_count = 0; | |
166 | ||
167 | /* A convenience typedef for the type of a task. */ | |
11d7dd33 | 168 | typedef std::packaged_task<void ()> task_t; |
a0b57563 CB |
169 | |
170 | /* The tasks that have not been processed yet. An optional is used | |
171 | to represent a task. If the optional is empty, then this means | |
172 | that the receiving thread should terminate. If the optional is | |
173 | non-empty, then it is an actual task to evaluate. */ | |
11d7dd33 | 174 | std::queue<optional<task_t>> m_tasks; |
a0b57563 CB |
175 | |
176 | /* A condition variable and mutex that are used for communication | |
177 | between the main thread and the worker threads. */ | |
178 | std::condition_variable m_tasks_cv; | |
179 | std::mutex m_tasks_mutex; | |
0981fe10 | 180 | #endif /* CXX_STD_THREAD */ |
a0b57563 CB |
181 | }; |
182 | ||
183 | } | |
184 | ||
185 | #endif /* GDBSUPPORT_THREAD_POOL_H */ |