123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187 |
- /* Thread pool
- Copyright (C) 2019-2022 Free Software Foundation, Inc.
- This file is part of GDB.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>. */
- #include "common-defs.h"
- #if CXX_STD_THREAD
- #include "gdbsupport/thread-pool.h"
- #include "gdbsupport/alt-stack.h"
- #include "gdbsupport/block-signals.h"
- #include <algorithm>
- #include <system_error>
- /* On the off chance that we have the pthread library on a Windows
- host, but std::thread is not using it, avoid calling
- pthread_setname_np on Windows. */
- #ifndef _WIN32
- #ifdef HAVE_PTHREAD_SETNAME_NP
- #define USE_PTHREAD_SETNAME_NP
- #endif
- #endif
- #ifdef USE_PTHREAD_SETNAME_NP
- #include <pthread.h>
- /* Handle platform discrepancies in pthread_setname_np: macOS uses a
- single-argument form, while Linux uses a two-argument form. NetBSD
- takes a printf-style format and an argument. This wrapper handles the
- difference. */
- ATTRIBUTE_UNUSED static void
- set_thread_name (int (*set_name) (pthread_t, const char *, void *),
- const char *name)
- {
- set_name (pthread_self (), "%s", const_cast<char *> (name));
- }
- ATTRIBUTE_UNUSED static void
- set_thread_name (int (*set_name) (pthread_t, const char *), const char *name)
- {
- set_name (pthread_self (), name);
- }
- /* The macOS man page says that pthread_setname_np returns "void", but
- the headers actually declare it returning "int". */
- ATTRIBUTE_UNUSED static void
- set_thread_name (int (*set_name) (const char *), const char *name)
- {
- set_name (name);
- }
- #endif /* USE_PTHREAD_SETNAME_NP */
- namespace gdb
- {
- /* The thread pool detach()s its threads, so that the threads will not
- prevent the process from exiting. However, it was discovered that
- if any detached threads were still waiting on a condition variable,
- then the condition variable's destructor would wait for the threads
- to exit -- defeating the purpose.
- Allocating the thread pool on the heap and simply "leaking" it
- avoids this problem.
- */
- thread_pool *thread_pool::g_thread_pool = new thread_pool ();
- thread_pool::~thread_pool ()
- {
- /* Because this is a singleton, we don't need to clean up. The
- threads are detached so that they won't prevent process exit.
- And, cleaning up here would be actively harmful in at least one
- case -- see the comment by the definition of g_thread_pool. */
- }
- void
- thread_pool::set_thread_count (size_t num_threads)
- {
- std::lock_guard<std::mutex> guard (m_tasks_mutex);
- /* If the new size is larger, start some new threads. */
- if (m_thread_count < num_threads)
- {
- /* Ensure that signals used by gdb are blocked in the new
- threads. */
- block_signals blocker;
- for (size_t i = m_thread_count; i < num_threads; ++i)
- {
- try
- {
- std::thread thread (&thread_pool::thread_function, this);
- thread.detach ();
- }
- catch (const std::system_error &)
- {
- /* libstdc++ may not implement std::thread, and will
- throw an exception on use. It seems fine to ignore
- this, and any other sort of startup failure here. */
- num_threads = i;
- break;
- }
- }
- }
- /* If the new size is smaller, terminate some existing threads. */
- if (num_threads < m_thread_count)
- {
- for (size_t i = num_threads; i < m_thread_count; ++i)
- m_tasks.emplace ();
- m_tasks_cv.notify_all ();
- }
- m_thread_count = num_threads;
- }
- std::future<void>
- thread_pool::post_task (std::function<void ()> &&func)
- {
- std::packaged_task<void ()> t (std::move (func));
- std::future<void> f = t.get_future ();
- if (m_thread_count == 0)
- {
- /* Just execute it now. */
- t ();
- }
- else
- {
- std::lock_guard<std::mutex> guard (m_tasks_mutex);
- m_tasks.emplace (std::move (t));
- m_tasks_cv.notify_one ();
- }
- return f;
- }
- void
- thread_pool::thread_function ()
- {
- #ifdef USE_PTHREAD_SETNAME_NP
- /* This must be done here, because on macOS one can only set the
- name of the current thread. */
- set_thread_name (pthread_setname_np, "gdb worker");
- #endif
- /* Ensure that SIGSEGV is delivered to an alternate signal
- stack. */
- gdb::alternate_signal_stack signal_stack;
- while (true)
- {
- optional<task> t;
- {
- /* We want to hold the lock while examining the task list, but
- not while invoking the task function. */
- std::unique_lock<std::mutex> guard (m_tasks_mutex);
- while (m_tasks.empty ())
- m_tasks_cv.wait (guard);
- t = std::move (m_tasks.front());
- m_tasks.pop ();
- }
- if (!t.has_value ())
- break;
- (*t) ();
- }
- }
- }
- #endif /* CXX_STD_THREAD */
|