Remove path name from test case
[binutils-gdb.git] / gdbsupport / thread-pool.h
1 /* Thread pool
2
3 Copyright (C) 2019-2023 Free Software Foundation, Inc.
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>
24 #include <vector>
25 #include <functional>
26 #include <chrono>
27 #if CXX_STD_THREAD
28 #include <thread>
29 #include <mutex>
30 #include <condition_variable>
31 #include <future>
32 #endif
33 #include "gdbsupport/gdb_optional.h"
34
35 namespace gdb
36 {
37
38 #if CXX_STD_THREAD
39
40 /* Simply use the standard future. */
41 template<typename T>
42 using future = std::future<T>;
43
44 /* ... and the standard future_status. */
45 using future_status = std::future_status;
46
47 #else /* CXX_STD_THREAD */
48
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
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
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
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 { }
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
115 void get () { }
116 };
117
118 #endif /* CXX_STD_THREAD */
119
120
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 {
142 #if CXX_STD_THREAD
143 return m_thread_count;
144 #else
145 return 0;
146 #endif
147 }
148
149 /* Post a task to the thread pool. A future is returned, which can
150 be used to wait for the result. */
151 future<void> post_task (std::function<void ()> &&func)
152 {
153 #if CXX_STD_THREAD
154 std::packaged_task<void ()> task (std::move (func));
155 future<void> result = task.get_future ();
156 do_post_task (std::packaged_task<void ()> (std::move (task)));
157 return result;
158 #else
159 func ();
160 return {};
161 #endif /* CXX_STD_THREAD */
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>
167 future<T> post_task (std::function<T ()> &&func)
168 {
169 #if CXX_STD_THREAD
170 std::packaged_task<T ()> task (std::move (func));
171 future<T> result = task.get_future ();
172 do_post_task (std::packaged_task<void ()> (std::move (task)));
173 return result;
174 #else
175 return future<T> (func ());
176 #endif /* CXX_STD_THREAD */
177 }
178
179 private:
180
181 thread_pool () = default;
182
183 #if CXX_STD_THREAD
184 /* The callback for each worker thread. */
185 void thread_function ();
186
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
191 /* The current thread count. */
192 size_t m_thread_count = 0;
193
194 /* A convenience typedef for the type of a task. */
195 typedef std::packaged_task<void ()> task_t;
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. */
201 std::queue<optional<task_t>> m_tasks;
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;
207 #endif /* CXX_STD_THREAD */
208 };
209
210 }
211
212 #endif /* GDBSUPPORT_THREAD_POOL_H */