xref: /netbsd-src/external/gpl3/gdb/dist/gdbsupport/thread-pool.h (revision 5ba1f45f2a09259cc846f20c7c5501604d633c90)
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