From patchwork Sun Oct 20 03:55:12 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Simon Marchi (Code Review)" X-Patchwork-Id: 35170 Received: (qmail 10647 invoked by alias); 20 Oct 2019 03:55:25 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 10508 invoked by uid 89); 20 Oct 2019 03:55:25 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-20.4 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_SHORT autolearn=ham version=3.3.1 spammy=namely, singleton, evaluate, pool X-HELO: mx1.osci.io Received: from polly.osci.io (HELO mx1.osci.io) (8.43.85.229) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Sun, 20 Oct 2019 03:55:21 +0000 Received: by mx1.osci.io (Postfix, from userid 994) id 68027203AE; Sat, 19 Oct 2019 23:55:21 -0400 (EDT) Received: from gnutoolchain-gerrit.osci.io (gnutoolchain-gerrit.osci.io [8.43.85.239]) by mx1.osci.io (Postfix) with ESMTP id D25F82058D; Sat, 19 Oct 2019 23:55:12 -0400 (EDT) Received: from localhost (localhost [127.0.0.1]) by gnutoolchain-gerrit.osci.io (Postfix) with ESMTP id BBD9721926; Sat, 19 Oct 2019 23:55:12 -0400 (EDT) X-Gerrit-PatchSet: 1 Date: Sat, 19 Oct 2019 23:55:12 -0400 From: "Tom Tromey (Code Review)" To: Christian Biesinger , gdb-patches@sourceware.org Message-ID: Auto-Submitted: auto-generated X-Gerrit-MessageType: newchange Subject: [review] Implement a thread pool X-Gerrit-Change-Id: I597bb642780cb9d578ca92373d2a638efb44fe52 X-Gerrit-Change-Number: 172 X-Gerrit-ChangeURL: X-Gerrit-Commit: e381edb283559bdb432a786e38db74a143d9d76f References: Reply-To: tromey@sourceware.org, cbiesinger@google.com, gdb-patches@sourceware.org MIME-Version: 1.0 Content-Disposition: inline User-Agent: Gerrit/3.0.3 Change URL: https://gnutoolchain-gerrit.osci.io/r/c/binutils-gdb/+/172 ...................................................................... Implement a thread pool This adds a simple thread pool to gdb. In the end, this seemed preferable to the approach taken in an earlier version of this series; namely, starting threads in the parallel-foreach implementation. This approach reduces the overhead of starting new threads, and also lets the user control (in a subsequent patch) exactly how many worker threads are running. gdb/ChangeLog 2019-10-19 Christian Biesinger Tom Tromey * gdbsupport/thread-pool.h: New file. * gdbsupport/thread-pool.c: New file. * Makefile.in (COMMON_SFILES): Add thread-pool.c. (HFILES_NO_SRCDIR): Add thread-pool.h. Change-Id: I597bb642780cb9d578ca92373d2a638efb44fe52 --- M gdb/ChangeLog M gdb/Makefile.in A gdb/gdbsupport/thread-pool.c A gdb/gdbsupport/thread-pool.h 4 files changed, 228 insertions(+), 0 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index f04d2bd..ab67332 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,11 @@ +2019-10-19 Christian Biesinger + Tom Tromey + + * gdbsupport/thread-pool.h: New file. + * gdbsupport/thread-pool.c: New file. + * Makefile.in (COMMON_SFILES): Add thread-pool.c. + (HFILES_NO_SRCDIR): Add thread-pool.h. + 2019-10-19 Tom Tromey * event-top.h (thread_local_segv_handler): Declare. diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 5e01c32..3be798a 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -984,6 +984,7 @@ gdbsupport/signals.c \ gdbsupport/signals-state-save-restore.c \ gdbsupport/tdesc.c \ + gdbsupport/thread-pool.c \ gdbsupport/xml-utils.c \ complaints.c \ completer.c \ @@ -1485,6 +1486,7 @@ gdbsupport/signals-state-save-restore.h \ gdbsupport/symbol.h \ gdbsupport/tdesc.h \ + gdbsupport/thread-pool.h \ gdbsupport/version.h \ gdbsupport/x86-xstate.h \ gdbsupport/xml-utils.h \ diff --git a/gdb/gdbsupport/thread-pool.c b/gdb/gdbsupport/thread-pool.c new file mode 100644 index 0000000..993e097 --- /dev/null +++ b/gdb/gdbsupport/thread-pool.c @@ -0,0 +1,119 @@ +/* Thread pool + + Copyright (C) 2019 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 . */ + +#include "common-defs.h" +#include "gdbsupport/thread-pool.h" +#include "gdbsupport/alt-stack.h" +#include "gdbsupport/block-signals.h" +#include + +namespace gdb +{ + +thread_pool thread_pool::g_thread_pool; + +thread_pool::thread_pool () + : m_tasks_cv (new std::condition_variable) +{ +} + +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 m_tasks_cv. */ +} + +void +thread_pool::set_thread_count (size_t num_threads) +{ + std::lock_guard guard (m_tasks_mutex); + + /* If the new size is larger, start some new threads. */ + if (m_count < num_threads) + { + /* Ensure that signals used by gdb are blocked in the new + threads. */ + block_signals blocker; + for (size_t i = m_count; i < num_threads; ++i) + { + std::thread thread (&thread_pool::thread_function, this); + thread.detach (); + } + } + /* If the new size is smaller, terminate some existing threads. */ + if (num_threads < m_count) + { + for (size_t i = num_threads; i < m_count; ++i) + m_tasks.emplace (); + m_tasks_cv->notify_all (); + } + + m_count = num_threads; +} + +std::future +thread_pool::post_task (std::function func) +{ + std::packaged_task t (func); + std::future f = t.get_future (); + + if (m_count == 0) + { + /* Just execute it now. */ + t (); + } + else + { + std::lock_guard guard (m_tasks_mutex); + m_tasks.emplace (std::move (t)); + m_tasks_cv->notify_one (); + } + return f; +} + +void +thread_pool::thread_function () +{ + /* Ensure that SIGSEGV is delivered to an alternate signal + stack. */ + gdb::alternate_signal_stack signal_stack; + + while (true) + { + task t; + + { + /* We want to hold the lock while examining the task list, but + not while invoking the task function. */ + std::unique_lock 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) (); + } +} + +} diff --git a/gdb/gdbsupport/thread-pool.h b/gdb/gdbsupport/thread-pool.h new file mode 100644 index 0000000..d760ad1 --- /dev/null +++ b/gdb/gdbsupport/thread-pool.h @@ -0,0 +1,99 @@ +/* Thread pool + + Copyright (C) 2019 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 . */ + +#ifndef GDBSUPPORT_THREAD_POOL_H +#define GDBSUPPORT_THREAD_POOL_H + +#include +#include +#include +#include +#include +#include +#include +#include "gdbsupport/gdb_optional.h" + +namespace gdb +{ + +/* A thread pool. + + There is a single global thread pool, see g_thread_pool. Tasks can + be submitted to the thread pool. They will be processed in worker + threads as time allows. */ +class thread_pool +{ +public: + /* The sole global thread pool. */ + static thread_pool g_thread_pool; + + ~thread_pool (); + DISABLE_COPY_AND_ASSIGN (thread_pool); + + /* Set the thread count of this thread pool. By default, no threads + are created -- the thread count must be set first. */ + void set_thread_count (size_t num_threads); + + /* Return the number of executing threads. */ + size_t count () const + { + return m_count; + } + + /* Post a task to the thread pool. A future is returned, which can + be used to wait for the result. */ + std::future post_task (std::function func); + +private: + + thread_pool (); + + /* The callback for each worker thread. */ + void thread_function (); + + /* An optional is used to represent a task. If the optional is + empty, then this means that the receiving thread should + terminate. If the optional is non-empty, then it is an actual + task to evaluate. */ + typedef optional> task; + + /* The current thread count. */ + size_t m_count = 0; + + /* The tasks that have not been processed yet. */ + std::queue m_tasks; + /* A condition variable and mutex that are used for communication + between the main thread and the worker threads. + + Note that this is a pointer. 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 condition variable on the heap and simply + "leaking" it avoids this problem. */ + std::condition_variable *m_tasks_cv; + std::mutex m_tasks_mutex; +}; + +} + +#endif /* GDBSUPPORT_THREAD_POOL_H */