17d62b00eSchristos /* Thread pool 27d62b00eSchristos 3*6881a400Schristos Copyright (C) 2019-2023 Free Software Foundation, Inc. 47d62b00eSchristos 57d62b00eSchristos This file is part of GDB. 67d62b00eSchristos 77d62b00eSchristos This program is free software; you can redistribute it and/or modify 87d62b00eSchristos it under the terms of the GNU General Public License as published by 97d62b00eSchristos the Free Software Foundation; either version 3 of the License, or 107d62b00eSchristos (at your option) any later version. 117d62b00eSchristos 127d62b00eSchristos This program is distributed in the hope that it will be useful, 137d62b00eSchristos but WITHOUT ANY WARRANTY; without even the implied warranty of 147d62b00eSchristos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 157d62b00eSchristos GNU General Public License for more details. 167d62b00eSchristos 177d62b00eSchristos You should have received a copy of the GNU General Public License 187d62b00eSchristos along with this program. If not, see <http://www.gnu.org/licenses/>. */ 197d62b00eSchristos 207d62b00eSchristos #include "common-defs.h" 21*6881a400Schristos #include "gdbsupport/thread-pool.h" 227d62b00eSchristos 237d62b00eSchristos #if CXX_STD_THREAD 247d62b00eSchristos 257d62b00eSchristos #include "gdbsupport/alt-stack.h" 267d62b00eSchristos #include "gdbsupport/block-signals.h" 277d62b00eSchristos #include <algorithm> 28*6881a400Schristos #include <system_error> 297d62b00eSchristos 307d62b00eSchristos /* On the off chance that we have the pthread library on a Windows 317d62b00eSchristos host, but std::thread is not using it, avoid calling 327d62b00eSchristos pthread_setname_np on Windows. */ 337d62b00eSchristos #ifndef _WIN32 347d62b00eSchristos #ifdef HAVE_PTHREAD_SETNAME_NP 357d62b00eSchristos #define USE_PTHREAD_SETNAME_NP 367d62b00eSchristos #endif 377d62b00eSchristos #endif 387d62b00eSchristos 397d62b00eSchristos #ifdef USE_PTHREAD_SETNAME_NP 407d62b00eSchristos 417d62b00eSchristos #include <pthread.h> 427d62b00eSchristos 437d62b00eSchristos /* Handle platform discrepancies in pthread_setname_np: macOS uses a 447d62b00eSchristos single-argument form, while Linux uses a two-argument form. NetBSD 457d62b00eSchristos takes a printf-style format and an argument. This wrapper handles the 467d62b00eSchristos difference. */ 477d62b00eSchristos 487d62b00eSchristos ATTRIBUTE_UNUSED static void 49*6881a400Schristos do_set_thread_name (int (*set_name) (pthread_t, const char *, void *), 507d62b00eSchristos const char *name) 517d62b00eSchristos { 527d62b00eSchristos set_name (pthread_self (), "%s", const_cast<char *> (name)); 537d62b00eSchristos } 547d62b00eSchristos 557d62b00eSchristos ATTRIBUTE_UNUSED static void 56*6881a400Schristos do_set_thread_name (int (*set_name) (pthread_t, const char *), 57*6881a400Schristos const char *name) 587d62b00eSchristos { 597d62b00eSchristos set_name (pthread_self (), name); 607d62b00eSchristos } 617d62b00eSchristos 627d62b00eSchristos /* The macOS man page says that pthread_setname_np returns "void", but 637d62b00eSchristos the headers actually declare it returning "int". */ 647d62b00eSchristos ATTRIBUTE_UNUSED static void 65*6881a400Schristos do_set_thread_name (int (*set_name) (const char *), const char *name) 667d62b00eSchristos { 677d62b00eSchristos set_name (name); 687d62b00eSchristos } 697d62b00eSchristos 70*6881a400Schristos static void 71*6881a400Schristos set_thread_name (const char *name) 72*6881a400Schristos { 73*6881a400Schristos do_set_thread_name (pthread_setname_np, name); 74*6881a400Schristos } 75*6881a400Schristos 76*6881a400Schristos #elif defined (USE_WIN32API) 77*6881a400Schristos 78*6881a400Schristos #include <windows.h> 79*6881a400Schristos 80*6881a400Schristos typedef HRESULT WINAPI (SetThreadDescription_ftype) (HANDLE, PCWSTR); 81*6881a400Schristos static SetThreadDescription_ftype *dyn_SetThreadDescription; 82*6881a400Schristos static bool initialized; 83*6881a400Schristos 84*6881a400Schristos static void 85*6881a400Schristos init_windows () 86*6881a400Schristos { 87*6881a400Schristos initialized = true; 88*6881a400Schristos 89*6881a400Schristos HMODULE hm = LoadLibrary (TEXT ("kernel32.dll")); 90*6881a400Schristos if (hm) 91*6881a400Schristos dyn_SetThreadDescription 92*6881a400Schristos = (SetThreadDescription_ftype *) GetProcAddress (hm, 93*6881a400Schristos "SetThreadDescription"); 94*6881a400Schristos 95*6881a400Schristos /* On some versions of Windows, this function is only available in 96*6881a400Schristos KernelBase.dll, not kernel32.dll. */ 97*6881a400Schristos if (dyn_SetThreadDescription == nullptr) 98*6881a400Schristos { 99*6881a400Schristos hm = LoadLibrary (TEXT ("KernelBase.dll")); 100*6881a400Schristos if (hm) 101*6881a400Schristos dyn_SetThreadDescription 102*6881a400Schristos = (SetThreadDescription_ftype *) GetProcAddress (hm, 103*6881a400Schristos "SetThreadDescription"); 104*6881a400Schristos } 105*6881a400Schristos } 106*6881a400Schristos 107*6881a400Schristos static void 108*6881a400Schristos do_set_thread_name (const wchar_t *name) 109*6881a400Schristos { 110*6881a400Schristos if (!initialized) 111*6881a400Schristos init_windows (); 112*6881a400Schristos 113*6881a400Schristos if (dyn_SetThreadDescription != nullptr) 114*6881a400Schristos dyn_SetThreadDescription (GetCurrentThread (), name); 115*6881a400Schristos } 116*6881a400Schristos 117*6881a400Schristos #define set_thread_name(NAME) do_set_thread_name (L ## NAME) 118*6881a400Schristos 119*6881a400Schristos #else /* USE_WIN32API */ 120*6881a400Schristos 121*6881a400Schristos static void 122*6881a400Schristos set_thread_name (const char *name) 123*6881a400Schristos { 124*6881a400Schristos } 125*6881a400Schristos 126*6881a400Schristos #endif 127*6881a400Schristos 128*6881a400Schristos #endif /* CXX_STD_THREAD */ 1297d62b00eSchristos 1307d62b00eSchristos namespace gdb 1317d62b00eSchristos { 1327d62b00eSchristos 1337d62b00eSchristos /* The thread pool detach()s its threads, so that the threads will not 1347d62b00eSchristos prevent the process from exiting. However, it was discovered that 1357d62b00eSchristos if any detached threads were still waiting on a condition variable, 1367d62b00eSchristos then the condition variable's destructor would wait for the threads 1377d62b00eSchristos to exit -- defeating the purpose. 1387d62b00eSchristos 1397d62b00eSchristos Allocating the thread pool on the heap and simply "leaking" it 1407d62b00eSchristos avoids this problem. 1417d62b00eSchristos */ 1427d62b00eSchristos thread_pool *thread_pool::g_thread_pool = new thread_pool (); 1437d62b00eSchristos 1447d62b00eSchristos thread_pool::~thread_pool () 1457d62b00eSchristos { 1467d62b00eSchristos /* Because this is a singleton, we don't need to clean up. The 1477d62b00eSchristos threads are detached so that they won't prevent process exit. 1487d62b00eSchristos And, cleaning up here would be actively harmful in at least one 1497d62b00eSchristos case -- see the comment by the definition of g_thread_pool. */ 1507d62b00eSchristos } 1517d62b00eSchristos 1527d62b00eSchristos void 1537d62b00eSchristos thread_pool::set_thread_count (size_t num_threads) 1547d62b00eSchristos { 155*6881a400Schristos #if CXX_STD_THREAD 1567d62b00eSchristos std::lock_guard<std::mutex> guard (m_tasks_mutex); 1577d62b00eSchristos 1587d62b00eSchristos /* If the new size is larger, start some new threads. */ 1597d62b00eSchristos if (m_thread_count < num_threads) 1607d62b00eSchristos { 1617d62b00eSchristos /* Ensure that signals used by gdb are blocked in the new 1627d62b00eSchristos threads. */ 1637d62b00eSchristos block_signals blocker; 1647d62b00eSchristos for (size_t i = m_thread_count; i < num_threads; ++i) 1657d62b00eSchristos { 166*6881a400Schristos try 167*6881a400Schristos { 1687d62b00eSchristos std::thread thread (&thread_pool::thread_function, this); 1697d62b00eSchristos thread.detach (); 1707d62b00eSchristos } 171*6881a400Schristos catch (const std::system_error &) 172*6881a400Schristos { 173*6881a400Schristos /* libstdc++ may not implement std::thread, and will 174*6881a400Schristos throw an exception on use. It seems fine to ignore 175*6881a400Schristos this, and any other sort of startup failure here. */ 176*6881a400Schristos num_threads = i; 177*6881a400Schristos break; 178*6881a400Schristos } 179*6881a400Schristos } 1807d62b00eSchristos } 1817d62b00eSchristos /* If the new size is smaller, terminate some existing threads. */ 1827d62b00eSchristos if (num_threads < m_thread_count) 1837d62b00eSchristos { 1847d62b00eSchristos for (size_t i = num_threads; i < m_thread_count; ++i) 1857d62b00eSchristos m_tasks.emplace (); 1867d62b00eSchristos m_tasks_cv.notify_all (); 1877d62b00eSchristos } 1887d62b00eSchristos 1897d62b00eSchristos m_thread_count = num_threads; 190*6881a400Schristos #else 191*6881a400Schristos /* No threads available, simply ignore the request. */ 192*6881a400Schristos #endif /* CXX_STD_THREAD */ 1937d62b00eSchristos } 1947d62b00eSchristos 195*6881a400Schristos #if CXX_STD_THREAD 1967d62b00eSchristos 197*6881a400Schristos void 198*6881a400Schristos thread_pool::do_post_task (std::packaged_task<void ()> &&func) 1997d62b00eSchristos { 200*6881a400Schristos std::packaged_task<void ()> t (std::move (func)); 201*6881a400Schristos 202*6881a400Schristos if (m_thread_count != 0) 2037d62b00eSchristos { 2047d62b00eSchristos std::lock_guard<std::mutex> guard (m_tasks_mutex); 2057d62b00eSchristos m_tasks.emplace (std::move (t)); 2067d62b00eSchristos m_tasks_cv.notify_one (); 2077d62b00eSchristos } 208*6881a400Schristos else 209*6881a400Schristos { 210*6881a400Schristos /* Just execute it now. */ 211*6881a400Schristos t (); 212*6881a400Schristos } 2137d62b00eSchristos } 2147d62b00eSchristos 2157d62b00eSchristos void 2167d62b00eSchristos thread_pool::thread_function () 2177d62b00eSchristos { 2187d62b00eSchristos /* This must be done here, because on macOS one can only set the 2197d62b00eSchristos name of the current thread. */ 220*6881a400Schristos set_thread_name ("gdb worker"); 2217d62b00eSchristos 2227d62b00eSchristos /* Ensure that SIGSEGV is delivered to an alternate signal 2237d62b00eSchristos stack. */ 2247d62b00eSchristos gdb::alternate_signal_stack signal_stack; 2257d62b00eSchristos 2267d62b00eSchristos while (true) 2277d62b00eSchristos { 228*6881a400Schristos optional<task_t> t; 2297d62b00eSchristos 2307d62b00eSchristos { 2317d62b00eSchristos /* We want to hold the lock while examining the task list, but 2327d62b00eSchristos not while invoking the task function. */ 2337d62b00eSchristos std::unique_lock<std::mutex> guard (m_tasks_mutex); 2347d62b00eSchristos while (m_tasks.empty ()) 2357d62b00eSchristos m_tasks_cv.wait (guard); 2367d62b00eSchristos t = std::move (m_tasks.front()); 2377d62b00eSchristos m_tasks.pop (); 2387d62b00eSchristos } 2397d62b00eSchristos 2407d62b00eSchristos if (!t.has_value ()) 2417d62b00eSchristos break; 2427d62b00eSchristos (*t) (); 2437d62b00eSchristos } 2447d62b00eSchristos } 2457d62b00eSchristos 2467d62b00eSchristos #endif /* CXX_STD_THREAD */ 247*6881a400Schristos 248*6881a400Schristos } /* namespace gdb */ 249