[gdb/testsuite] Fix another fail and tcl error in gdb.dap/sources.exp
[binutils-gdb.git] / gdbsupport / thread-pool.h
blob85f2348eb74a72b390a64bfa6d0a4719f4942e72
1 /* Thread pool
3 Copyright (C) 2019-2024 Free Software Foundation, Inc.
5 This file is part of GDB.
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.
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.
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/>. */
20 #ifndef GDBSUPPORT_THREAD_POOL_H
21 #define GDBSUPPORT_THREAD_POOL_H
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 <optional>
35 namespace gdb
38 #if CXX_STD_THREAD
40 /* Simply use the standard future. */
41 template<typename T>
42 using future = std::future<T>;
44 /* ... and the standard future_status. */
45 using future_status = std::future_status;
47 #else /* CXX_STD_THREAD */
49 /* A compatibility enum for std::future_status. This is just the
50 subset needed by gdb. */
51 enum class future_status
53 ready,
54 timeout,
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.
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. */
68 template<typename T>
69 class future
71 public:
73 explicit future (T value)
74 : m_value (std::move (value))
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;
84 void wait () const { }
86 template<class Rep, class Period>
87 future_status wait_for (const std::chrono::duration<Rep,Period> &duration)
88 const
90 return future_status::ready;
93 T get () { return std::move (m_value); }
95 private:
97 T m_value;
100 /* A specialization for void. */
102 template<>
103 class future<void>
105 public:
106 void wait () const { }
108 template<class Rep, class Period>
109 future_status wait_for (const std::chrono::duration<Rep,Period> &duration)
110 const
112 return future_status::ready;
115 void get () { }
118 #endif /* CXX_STD_THREAD */
121 /* A thread pool.
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
128 public:
129 /* The sole global thread pool. */
130 static thread_pool *g_thread_pool;
132 ~thread_pool ();
133 DISABLE_COPY_AND_ASSIGN (thread_pool);
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);
139 /* Return the number of executing threads. */
140 size_t thread_count () const
142 #if CXX_STD_THREAD
143 return m_thread_count;
144 #else
145 return 0;
146 #endif
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)
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 */
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)
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 */
179 private:
181 thread_pool () = default;
183 #if CXX_STD_THREAD
184 /* The callback for each worker thread. */
185 void thread_function ();
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);
191 /* The current thread count. */
192 size_t m_thread_count = 0;
194 /* A convenience typedef for the type of a task. */
195 typedef std::packaged_task<void ()> task_t;
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<std::optional<task_t>> m_tasks;
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 bool m_sized_at_least_once = false;
208 #endif /* CXX_STD_THREAD */
213 #endif /* GDBSUPPORT_THREAD_POOL_H */