18dffb485Schristos /* Thread pool 28dffb485Schristos 3*5ba1f45fSchristos Copyright (C) 2019-2024 Free Software Foundation, Inc. 48dffb485Schristos 58dffb485Schristos This file is part of GDB. 68dffb485Schristos 78dffb485Schristos This program is free software; you can redistribute it and/or modify 88dffb485Schristos it under the terms of the GNU General Public License as published by 98dffb485Schristos the Free Software Foundation; either version 3 of the License, or 108dffb485Schristos (at your option) any later version. 118dffb485Schristos 128dffb485Schristos This program is distributed in the hope that it will be useful, 138dffb485Schristos but WITHOUT ANY WARRANTY; without even the implied warranty of 148dffb485Schristos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 158dffb485Schristos GNU General Public License for more details. 168dffb485Schristos 178dffb485Schristos You should have received a copy of the GNU General Public License 188dffb485Schristos along with this program. If not, see <http://www.gnu.org/licenses/>. */ 198dffb485Schristos 208dffb485Schristos #ifndef GDBSUPPORT_THREAD_POOL_H 218dffb485Schristos #define GDBSUPPORT_THREAD_POOL_H 228dffb485Schristos 238dffb485Schristos #include <queue> 248dffb485Schristos #include <vector> 258dffb485Schristos #include <functional> 26*5ba1f45fSchristos #include <chrono> 274b169a6bSchristos #if CXX_STD_THREAD 284b169a6bSchristos #include <thread> 298dffb485Schristos #include <mutex> 308dffb485Schristos #include <condition_variable> 318dffb485Schristos #include <future> 324b169a6bSchristos #endif 33*5ba1f45fSchristos #include <optional> 348dffb485Schristos 358dffb485Schristos namespace gdb 368dffb485Schristos { 378dffb485Schristos 384b169a6bSchristos #if CXX_STD_THREAD 394b169a6bSchristos 404b169a6bSchristos /* Simply use the standard future. */ 414b169a6bSchristos template<typename T> 424b169a6bSchristos using future = std::future<T>; 434b169a6bSchristos 44*5ba1f45fSchristos /* ... and the standard future_status. */ 45*5ba1f45fSchristos using future_status = std::future_status; 46*5ba1f45fSchristos 474b169a6bSchristos #else /* CXX_STD_THREAD */ 484b169a6bSchristos 49*5ba1f45fSchristos /* A compatibility enum for std::future_status. This is just the 50*5ba1f45fSchristos subset needed by gdb. */ 51*5ba1f45fSchristos enum class future_status 52*5ba1f45fSchristos { 53*5ba1f45fSchristos ready, 54*5ba1f45fSchristos timeout, 55*5ba1f45fSchristos }; 56*5ba1f45fSchristos 574b169a6bSchristos /* A compatibility wrapper for std::future. Once <thread> and 584b169a6bSchristos <future> are available in all GCC builds -- should that ever happen 594b169a6bSchristos -- this can be removed. GCC does not implement threading for 604b169a6bSchristos MinGW, see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93687. 614b169a6bSchristos 624b169a6bSchristos Meanwhile, in this mode, there are no threads. Tasks submitted to 634b169a6bSchristos the thread pool are invoked immediately and their result is stored 644b169a6bSchristos here. The base template here simply wraps a T and provides some 654b169a6bSchristos std::future compatibility methods. The provided methods are chosen 664b169a6bSchristos based on what GDB needs presently. */ 674b169a6bSchristos 684b169a6bSchristos template<typename T> 694b169a6bSchristos class future 704b169a6bSchristos { 714b169a6bSchristos public: 724b169a6bSchristos 734b169a6bSchristos explicit future (T value) 744b169a6bSchristos : m_value (std::move (value)) 754b169a6bSchristos { 764b169a6bSchristos } 774b169a6bSchristos 784b169a6bSchristos future () = default; 794b169a6bSchristos future (future &&other) = default; 804b169a6bSchristos future (const future &other) = delete; 814b169a6bSchristos future &operator= (future &&other) = default; 824b169a6bSchristos future &operator= (const future &other) = delete; 834b169a6bSchristos 844b169a6bSchristos void wait () const { } 854b169a6bSchristos 86*5ba1f45fSchristos template<class Rep, class Period> 87*5ba1f45fSchristos future_status wait_for (const std::chrono::duration<Rep,Period> &duration) 88*5ba1f45fSchristos const 89*5ba1f45fSchristos { 90*5ba1f45fSchristos return future_status::ready; 91*5ba1f45fSchristos } 92*5ba1f45fSchristos 934b169a6bSchristos T get () { return std::move (m_value); } 944b169a6bSchristos 954b169a6bSchristos private: 964b169a6bSchristos 974b169a6bSchristos T m_value; 984b169a6bSchristos }; 994b169a6bSchristos 1004b169a6bSchristos /* A specialization for void. */ 1014b169a6bSchristos 1024b169a6bSchristos template<> 1034b169a6bSchristos class future<void> 1044b169a6bSchristos { 1054b169a6bSchristos public: 1064b169a6bSchristos void wait () const { } 107*5ba1f45fSchristos 108*5ba1f45fSchristos template<class Rep, class Period> 109*5ba1f45fSchristos future_status wait_for (const std::chrono::duration<Rep,Period> &duration) 110*5ba1f45fSchristos const 111*5ba1f45fSchristos { 112*5ba1f45fSchristos return future_status::ready; 113*5ba1f45fSchristos } 114*5ba1f45fSchristos 1154b169a6bSchristos void get () { } 1164b169a6bSchristos }; 1174b169a6bSchristos 1184b169a6bSchristos #endif /* CXX_STD_THREAD */ 1194b169a6bSchristos 1204b169a6bSchristos 1218dffb485Schristos /* A thread pool. 1228dffb485Schristos 1238dffb485Schristos There is a single global thread pool, see g_thread_pool. Tasks can 1248dffb485Schristos be submitted to the thread pool. They will be processed in worker 1258dffb485Schristos threads as time allows. */ 1268dffb485Schristos class thread_pool 1278dffb485Schristos { 1288dffb485Schristos public: 1298dffb485Schristos /* The sole global thread pool. */ 1308dffb485Schristos static thread_pool *g_thread_pool; 1318dffb485Schristos 1328dffb485Schristos ~thread_pool (); 1338dffb485Schristos DISABLE_COPY_AND_ASSIGN (thread_pool); 1348dffb485Schristos 1358dffb485Schristos /* Set the thread count of this thread pool. By default, no threads 1368dffb485Schristos are created -- the thread count must be set first. */ 1378dffb485Schristos void set_thread_count (size_t num_threads); 1388dffb485Schristos 1398dffb485Schristos /* Return the number of executing threads. */ 1408dffb485Schristos size_t thread_count () const 1418dffb485Schristos { 1424b169a6bSchristos #if CXX_STD_THREAD 1438dffb485Schristos return m_thread_count; 1444b169a6bSchristos #else 1454b169a6bSchristos return 0; 1464b169a6bSchristos #endif 1478dffb485Schristos } 1488dffb485Schristos 1498dffb485Schristos /* Post a task to the thread pool. A future is returned, which can 1508dffb485Schristos be used to wait for the result. */ 1514b169a6bSchristos future<void> post_task (std::function<void ()> &&func) 1524b169a6bSchristos { 1534b169a6bSchristos #if CXX_STD_THREAD 1544b169a6bSchristos std::packaged_task<void ()> task (std::move (func)); 1554b169a6bSchristos future<void> result = task.get_future (); 1564b169a6bSchristos do_post_task (std::packaged_task<void ()> (std::move (task))); 1574b169a6bSchristos return result; 1584b169a6bSchristos #else 1594b169a6bSchristos func (); 1604b169a6bSchristos return {}; 1614b169a6bSchristos #endif /* CXX_STD_THREAD */ 1624b169a6bSchristos } 1634b169a6bSchristos 1644b169a6bSchristos /* Post a task to the thread pool. A future is returned, which can 1654b169a6bSchristos be used to wait for the result. */ 1664b169a6bSchristos template<typename T> 1674b169a6bSchristos future<T> post_task (std::function<T ()> &&func) 1684b169a6bSchristos { 1694b169a6bSchristos #if CXX_STD_THREAD 1704b169a6bSchristos std::packaged_task<T ()> task (std::move (func)); 1714b169a6bSchristos future<T> result = task.get_future (); 1724b169a6bSchristos do_post_task (std::packaged_task<void ()> (std::move (task))); 1734b169a6bSchristos return result; 1744b169a6bSchristos #else 1754b169a6bSchristos return future<T> (func ()); 1764b169a6bSchristos #endif /* CXX_STD_THREAD */ 1774b169a6bSchristos } 1788dffb485Schristos 1798dffb485Schristos private: 1808dffb485Schristos 1818dffb485Schristos thread_pool () = default; 1828dffb485Schristos 1834b169a6bSchristos #if CXX_STD_THREAD 1848dffb485Schristos /* The callback for each worker thread. */ 1858dffb485Schristos void thread_function (); 1868dffb485Schristos 1874b169a6bSchristos /* Post a task to the thread pool. A future is returned, which can 1884b169a6bSchristos be used to wait for the result. */ 1894b169a6bSchristos void do_post_task (std::packaged_task<void ()> &&func); 1904b169a6bSchristos 1918dffb485Schristos /* The current thread count. */ 1928dffb485Schristos size_t m_thread_count = 0; 1938dffb485Schristos 1948dffb485Schristos /* A convenience typedef for the type of a task. */ 1954b169a6bSchristos typedef std::packaged_task<void ()> task_t; 1968dffb485Schristos 1978dffb485Schristos /* The tasks that have not been processed yet. An optional is used 1988dffb485Schristos to represent a task. If the optional is empty, then this means 1998dffb485Schristos that the receiving thread should terminate. If the optional is 2008dffb485Schristos non-empty, then it is an actual task to evaluate. */ 201*5ba1f45fSchristos std::queue<std::optional<task_t>> m_tasks; 2028dffb485Schristos 2038dffb485Schristos /* A condition variable and mutex that are used for communication 2048dffb485Schristos between the main thread and the worker threads. */ 2058dffb485Schristos std::condition_variable m_tasks_cv; 2068dffb485Schristos std::mutex m_tasks_mutex; 207*5ba1f45fSchristos bool m_sized_at_least_once = false; 2084b169a6bSchristos #endif /* CXX_STD_THREAD */ 2098dffb485Schristos }; 2108dffb485Schristos 2118dffb485Schristos } 2128dffb485Schristos 2138dffb485Schristos #endif /* GDBSUPPORT_THREAD_POOL_H */ 214