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 204b169a6bSchristos #include "gdbsupport/thread-pool.h" 218dffb485Schristos 228dffb485Schristos #if CXX_STD_THREAD 238dffb485Schristos 248dffb485Schristos #include "gdbsupport/alt-stack.h" 258dffb485Schristos #include "gdbsupport/block-signals.h" 268dffb485Schristos #include <algorithm> 274b169a6bSchristos #include <system_error> 288dffb485Schristos 298dffb485Schristos /* On the off chance that we have the pthread library on a Windows 308dffb485Schristos host, but std::thread is not using it, avoid calling 318dffb485Schristos pthread_setname_np on Windows. */ 328dffb485Schristos #ifndef _WIN32 338dffb485Schristos #ifdef HAVE_PTHREAD_SETNAME_NP 348dffb485Schristos #define USE_PTHREAD_SETNAME_NP 358dffb485Schristos #endif 368dffb485Schristos #endif 378dffb485Schristos 388dffb485Schristos #ifdef USE_PTHREAD_SETNAME_NP 398dffb485Schristos 408dffb485Schristos #include <pthread.h> 418dffb485Schristos 428dffb485Schristos /* Handle platform discrepancies in pthread_setname_np: macOS uses a 438dffb485Schristos single-argument form, while Linux uses a two-argument form. NetBSD 448dffb485Schristos takes a printf-style format and an argument. This wrapper handles the 458dffb485Schristos difference. */ 468dffb485Schristos 478dffb485Schristos ATTRIBUTE_UNUSED static void 484b169a6bSchristos do_set_thread_name (int (*set_name) (pthread_t, const char *, void *), 498dffb485Schristos const char *name) 508dffb485Schristos { 518dffb485Schristos set_name (pthread_self (), "%s", const_cast<char *> (name)); 528dffb485Schristos } 538dffb485Schristos 548dffb485Schristos ATTRIBUTE_UNUSED static void 554b169a6bSchristos do_set_thread_name (int (*set_name) (pthread_t, const char *), 564b169a6bSchristos const char *name) 578dffb485Schristos { 588dffb485Schristos set_name (pthread_self (), name); 598dffb485Schristos } 608dffb485Schristos 618dffb485Schristos /* The macOS man page says that pthread_setname_np returns "void", but 628dffb485Schristos the headers actually declare it returning "int". */ 638dffb485Schristos ATTRIBUTE_UNUSED static void 644b169a6bSchristos do_set_thread_name (int (*set_name) (const char *), const char *name) 658dffb485Schristos { 668dffb485Schristos set_name (name); 678dffb485Schristos } 688dffb485Schristos 694b169a6bSchristos static void 704b169a6bSchristos set_thread_name (const char *name) 714b169a6bSchristos { 724b169a6bSchristos do_set_thread_name (pthread_setname_np, name); 734b169a6bSchristos } 744b169a6bSchristos 754b169a6bSchristos #elif defined (USE_WIN32API) 764b169a6bSchristos 774b169a6bSchristos #include <windows.h> 784b169a6bSchristos 794b169a6bSchristos typedef HRESULT WINAPI (SetThreadDescription_ftype) (HANDLE, PCWSTR); 804b169a6bSchristos static SetThreadDescription_ftype *dyn_SetThreadDescription; 814b169a6bSchristos static bool initialized; 824b169a6bSchristos 834b169a6bSchristos static void 844b169a6bSchristos init_windows () 854b169a6bSchristos { 864b169a6bSchristos initialized = true; 874b169a6bSchristos 884b169a6bSchristos HMODULE hm = LoadLibrary (TEXT ("kernel32.dll")); 894b169a6bSchristos if (hm) 904b169a6bSchristos dyn_SetThreadDescription 914b169a6bSchristos = (SetThreadDescription_ftype *) GetProcAddress (hm, 924b169a6bSchristos "SetThreadDescription"); 934b169a6bSchristos 944b169a6bSchristos /* On some versions of Windows, this function is only available in 954b169a6bSchristos KernelBase.dll, not kernel32.dll. */ 964b169a6bSchristos if (dyn_SetThreadDescription == nullptr) 974b169a6bSchristos { 984b169a6bSchristos hm = LoadLibrary (TEXT ("KernelBase.dll")); 994b169a6bSchristos if (hm) 1004b169a6bSchristos dyn_SetThreadDescription 1014b169a6bSchristos = (SetThreadDescription_ftype *) GetProcAddress (hm, 1024b169a6bSchristos "SetThreadDescription"); 1034b169a6bSchristos } 1044b169a6bSchristos } 1054b169a6bSchristos 1064b169a6bSchristos static void 1074b169a6bSchristos do_set_thread_name (const wchar_t *name) 1084b169a6bSchristos { 1094b169a6bSchristos if (!initialized) 1104b169a6bSchristos init_windows (); 1114b169a6bSchristos 1124b169a6bSchristos if (dyn_SetThreadDescription != nullptr) 1134b169a6bSchristos dyn_SetThreadDescription (GetCurrentThread (), name); 1144b169a6bSchristos } 1154b169a6bSchristos 1164b169a6bSchristos #define set_thread_name(NAME) do_set_thread_name (L ## NAME) 1174b169a6bSchristos 1184b169a6bSchristos #else /* USE_WIN32API */ 1194b169a6bSchristos 1204b169a6bSchristos static void 1214b169a6bSchristos set_thread_name (const char *name) 1224b169a6bSchristos { 1234b169a6bSchristos } 1244b169a6bSchristos 1254b169a6bSchristos #endif 1264b169a6bSchristos 1274b169a6bSchristos #endif /* CXX_STD_THREAD */ 1288dffb485Schristos 1298dffb485Schristos namespace gdb 1308dffb485Schristos { 1318dffb485Schristos 1328dffb485Schristos /* The thread pool detach()s its threads, so that the threads will not 1338dffb485Schristos prevent the process from exiting. However, it was discovered that 1348dffb485Schristos if any detached threads were still waiting on a condition variable, 1358dffb485Schristos then the condition variable's destructor would wait for the threads 1368dffb485Schristos to exit -- defeating the purpose. 1378dffb485Schristos 1388dffb485Schristos Allocating the thread pool on the heap and simply "leaking" it 1398dffb485Schristos avoids this problem. 1408dffb485Schristos */ 1418dffb485Schristos thread_pool *thread_pool::g_thread_pool = new thread_pool (); 1428dffb485Schristos 1438dffb485Schristos thread_pool::~thread_pool () 1448dffb485Schristos { 1458dffb485Schristos /* Because this is a singleton, we don't need to clean up. The 1468dffb485Schristos threads are detached so that they won't prevent process exit. 1478dffb485Schristos And, cleaning up here would be actively harmful in at least one 1488dffb485Schristos case -- see the comment by the definition of g_thread_pool. */ 1498dffb485Schristos } 1508dffb485Schristos 1518dffb485Schristos void 1528dffb485Schristos thread_pool::set_thread_count (size_t num_threads) 1538dffb485Schristos { 1544b169a6bSchristos #if CXX_STD_THREAD 1558dffb485Schristos std::lock_guard<std::mutex> guard (m_tasks_mutex); 156*5ba1f45fSchristos m_sized_at_least_once = true; 1578dffb485Schristos 1588dffb485Schristos /* If the new size is larger, start some new threads. */ 1598dffb485Schristos if (m_thread_count < num_threads) 1608dffb485Schristos { 1618dffb485Schristos /* Ensure that signals used by gdb are blocked in the new 1628dffb485Schristos threads. */ 1638dffb485Schristos block_signals blocker; 1648dffb485Schristos for (size_t i = m_thread_count; i < num_threads; ++i) 1658dffb485Schristos { 1664b169a6bSchristos try 1674b169a6bSchristos { 1688dffb485Schristos std::thread thread (&thread_pool::thread_function, this); 1698dffb485Schristos thread.detach (); 1708dffb485Schristos } 1714b169a6bSchristos catch (const std::system_error &) 1724b169a6bSchristos { 1734b169a6bSchristos /* libstdc++ may not implement std::thread, and will 1744b169a6bSchristos throw an exception on use. It seems fine to ignore 1754b169a6bSchristos this, and any other sort of startup failure here. */ 1764b169a6bSchristos num_threads = i; 1774b169a6bSchristos break; 1784b169a6bSchristos } 1794b169a6bSchristos } 1808dffb485Schristos } 1818dffb485Schristos /* If the new size is smaller, terminate some existing threads. */ 1828dffb485Schristos if (num_threads < m_thread_count) 1838dffb485Schristos { 1848dffb485Schristos for (size_t i = num_threads; i < m_thread_count; ++i) 1858dffb485Schristos m_tasks.emplace (); 1868dffb485Schristos m_tasks_cv.notify_all (); 1878dffb485Schristos } 1888dffb485Schristos 1898dffb485Schristos m_thread_count = num_threads; 1904b169a6bSchristos #else 1914b169a6bSchristos /* No threads available, simply ignore the request. */ 1924b169a6bSchristos #endif /* CXX_STD_THREAD */ 1938dffb485Schristos } 1948dffb485Schristos 1954b169a6bSchristos #if CXX_STD_THREAD 1968dffb485Schristos 1974b169a6bSchristos void 1984b169a6bSchristos thread_pool::do_post_task (std::packaged_task<void ()> &&func) 1998dffb485Schristos { 200*5ba1f45fSchristos /* This assert is here to check that no tasks are posted to the pool between 201*5ba1f45fSchristos its initialization and sizing. */ 202*5ba1f45fSchristos gdb_assert (m_sized_at_least_once); 2034b169a6bSchristos std::packaged_task<void ()> t (std::move (func)); 2044b169a6bSchristos 2054b169a6bSchristos if (m_thread_count != 0) 2068dffb485Schristos { 2078dffb485Schristos std::lock_guard<std::mutex> guard (m_tasks_mutex); 2088dffb485Schristos m_tasks.emplace (std::move (t)); 2098dffb485Schristos m_tasks_cv.notify_one (); 2108dffb485Schristos } 2114b169a6bSchristos else 2124b169a6bSchristos { 2134b169a6bSchristos /* Just execute it now. */ 2144b169a6bSchristos t (); 2154b169a6bSchristos } 2168dffb485Schristos } 2178dffb485Schristos 2188dffb485Schristos void 2198dffb485Schristos thread_pool::thread_function () 2208dffb485Schristos { 2218dffb485Schristos /* This must be done here, because on macOS one can only set the 2228dffb485Schristos name of the current thread. */ 2234b169a6bSchristos set_thread_name ("gdb worker"); 2248dffb485Schristos 2258dffb485Schristos /* Ensure that SIGSEGV is delivered to an alternate signal 2268dffb485Schristos stack. */ 2278dffb485Schristos gdb::alternate_signal_stack signal_stack; 2288dffb485Schristos 2298dffb485Schristos while (true) 2308dffb485Schristos { 231*5ba1f45fSchristos std::optional<task_t> t; 2328dffb485Schristos 2338dffb485Schristos { 2348dffb485Schristos /* We want to hold the lock while examining the task list, but 2358dffb485Schristos not while invoking the task function. */ 2368dffb485Schristos std::unique_lock<std::mutex> guard (m_tasks_mutex); 2378dffb485Schristos while (m_tasks.empty ()) 2388dffb485Schristos m_tasks_cv.wait (guard); 2398dffb485Schristos t = std::move (m_tasks.front()); 2408dffb485Schristos m_tasks.pop (); 2418dffb485Schristos } 2428dffb485Schristos 2438dffb485Schristos if (!t.has_value ()) 2448dffb485Schristos break; 2458dffb485Schristos (*t) (); 2468dffb485Schristos } 2478dffb485Schristos } 2488dffb485Schristos 2498dffb485Schristos #endif /* CXX_STD_THREAD */ 2504b169a6bSchristos 2514b169a6bSchristos } /* namespace gdb */ 252