From patchwork Wed Oct 30 22:53:52 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: 35497 Received: (qmail 103059 invoked by alias); 30 Oct 2019 22:54:13 -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 102966 invoked by uid 89); 30 Oct 2019 22:54:12 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-26.9 required=5.0 tests=BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_SHORT autolearn=ham version=3.3.1 spammy=tasks, leaking 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; Wed, 30 Oct 2019 22:54:09 +0000 Received: by mx1.osci.io (Postfix, from userid 994) id D612020555; Wed, 30 Oct 2019 18:54:07 -0400 (EDT) Received: from gnutoolchain-gerrit.osci.io (gnutoolchain-gerrit.osci.io [IPv6:2620:52:3:1:5054:ff:fe06:16ca]) by mx1.osci.io (Postfix) with ESMTP id 0836920C7E; Wed, 30 Oct 2019 18:53:54 -0400 (EDT) Received: from localhost (localhost [127.0.0.1]) by gnutoolchain-gerrit.osci.io (Postfix) with ESMTP id D8B152A7D9; Wed, 30 Oct 2019 18:53:54 -0400 (EDT) X-Gerrit-PatchSet: 3 Date: Wed, 30 Oct 2019 18:53:52 -0400 From: "Tom Tromey (Code Review)" To: Christian Biesinger , gdb-patches@sourceware.org Auto-Submitted: auto-generated X-Gerrit-MessageType: newpatchset Subject: [review v3] Implement a thread pool X-Gerrit-Change-Id: I597bb642780cb9d578ca92373d2a638efb44fe52 X-Gerrit-Change-Number: 172 X-Gerrit-ChangeURL: X-Gerrit-Commit: 52c846a131f8019af2f4b5f901eee66b0343d4a6 In-Reply-To: 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-75-g9005159e5d Message-Id: <20191030225354.D8B152A7D9@gnutoolchain-gerrit.osci.io> 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. 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, 223 insertions(+), 0 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 1cc0b83..273345b 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 e3433b2..b42e8d1 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -993,6 +993,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 \ @@ -1495,6 +1496,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..a3ebf42 --- /dev/null +++ b/gdb/gdbsupport/thread-pool.c @@ -0,0 +1,123 @@ +/* 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 +{ + +/* 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 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..06b0774 --- /dev/null +++ b/gdb/gdbsupport/thread-pool.h @@ -0,0 +1,90 @@ +/* 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 () = default; + + /* 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. */ + std::condition_variable m_tasks_cv; + std::mutex m_tasks_mutex; +}; + +} + +#endif /* GDBSUPPORT_THREAD_POOL_H */