diff --git a/lldb/include/lldb/Host/TaskPool.h b/lldb/include/lldb/Host/TaskPool.h deleted file mode 100644 --- a/lldb/include/lldb/Host/TaskPool.h +++ /dev/null @@ -1,92 +0,0 @@ -//===--------------------- TaskPool.h ---------------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#ifndef LLDB_HOST_TASKPOOL_H -#define LLDB_HOST_TASKPOOL_H - -#include "llvm/ADT/STLExtras.h" -#include -#include -#include -#include -#include -#include - -namespace lldb_private { - -// Global TaskPool class for running tasks in parallel on a set of worker -// thread created the first time the task pool is used. The TaskPool provide no -// guarantee about the order the task will be run and about what tasks will run -// in parallel. None of the task added to the task pool should block on -// something (mutex, future, condition variable) what will be set only by the -// completion of an other task on the task pool as they may run on the same -// thread sequentally. -class TaskPool { -public: - // Add a new task to the task pool and return a std::future belonging to the - // newly created task. The caller of this function has to wait on the future - // for this task to complete. - template - static std::future::type> - AddTask(F &&f, Args &&... args); - - // Run all of the specified tasks on the task pool and wait until all of them - // are finished before returning. This method is intended to be used for - // small number tasks where listing them as function arguments is acceptable. - // For running large number of tasks you should use AddTask for each task and - // then call wait() on each returned future. - template static void RunTasks(T &&... tasks); - -private: - TaskPool() = delete; - - template struct RunTaskImpl; - - static void AddTaskImpl(std::function &&task_fn); -}; - -template -std::future::type> -TaskPool::AddTask(F &&f, Args &&... args) { - auto task_sp = std::make_shared< - std::packaged_task::type()>>( - std::bind(std::forward(f), std::forward(args)...)); - - AddTaskImpl([task_sp]() { (*task_sp)(); }); - - return task_sp->get_future(); -} - -template void TaskPool::RunTasks(T &&... tasks) { - RunTaskImpl::Run(std::forward(tasks)...); -} - -template -struct TaskPool::RunTaskImpl { - static void Run(Head &&h, Tail &&... t) { - auto f = AddTask(std::forward(h)); - RunTaskImpl::Run(std::forward(t)...); - f.wait(); - } -}; - -template <> struct TaskPool::RunTaskImpl<> { - static void Run() {} -}; - -// Run 'func' on every value from begin .. end-1. Each worker will grab -// 'batch_size' numbers at a time to work on, so for very fast functions, batch -// should be large enough to avoid too much cache line contention. -void TaskMapOverInt(size_t begin, size_t end, - const llvm::function_ref &func); - -unsigned GetHardwareConcurrencyHint(); - -} // namespace lldb_private - -#endif // LLDB_HOST_TASKPOOL_H diff --git a/lldb/include/lldb/module.modulemap b/lldb/include/lldb/module.modulemap --- a/lldb/include/lldb/module.modulemap +++ b/lldb/include/lldb/module.modulemap @@ -49,7 +49,6 @@ module SocketAddress { header "Host/SocketAddress.h" export * } module Socket { header "Host/Socket.h" export * } module StringConvert { textual header "Host/StringConvert.h" export * } - module TaskPool { header "Host/TaskPool.h" export * } module Terminal { header "Host/Terminal.h" export * } module ThreadLauncher { header "Host/ThreadLauncher.h" export * } module Time { header "Host/Time.h" export * } diff --git a/lldb/source/Host/CMakeLists.txt b/lldb/source/Host/CMakeLists.txt --- a/lldb/source/Host/CMakeLists.txt +++ b/lldb/source/Host/CMakeLists.txt @@ -30,7 +30,6 @@ common/SocketAddress.cpp common/Socket.cpp common/StringConvert.cpp - common/TaskPool.cpp common/TCPSocket.cpp common/Terminal.cpp common/ThreadLauncher.cpp diff --git a/lldb/source/Host/common/TaskPool.cpp b/lldb/source/Host/common/TaskPool.cpp deleted file mode 100644 --- a/lldb/source/Host/common/TaskPool.cpp +++ /dev/null @@ -1,126 +0,0 @@ -//===-- TaskPool.cpp ------------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "lldb/Host/TaskPool.h" -#include "lldb/Host/ThreadLauncher.h" -#include "lldb/Utility/Log.h" - -#include -#include -#include - -namespace lldb_private { - -namespace { -class TaskPoolImpl { -public: - static TaskPoolImpl &GetInstance(); - - void AddTask(std::function &&task_fn); - -private: - TaskPoolImpl(); - - static lldb::thread_result_t WorkerPtr(void *pool); - - static void Worker(TaskPoolImpl *pool); - - std::queue> m_tasks; - std::mutex m_tasks_mutex; - uint32_t m_thread_count; -}; - -} // end of anonymous namespace - -TaskPoolImpl &TaskPoolImpl::GetInstance() { - static TaskPoolImpl g_task_pool_impl; - return g_task_pool_impl; -} - -void TaskPool::AddTaskImpl(std::function &&task_fn) { - TaskPoolImpl::GetInstance().AddTask(std::move(task_fn)); -} - -TaskPoolImpl::TaskPoolImpl() : m_thread_count(0) {} - -unsigned GetHardwareConcurrencyHint() { - // std::thread::hardware_concurrency may return 0 if the value is not well - // defined or not computable. - static const unsigned g_hardware_concurrency = - std::max(1u, std::thread::hardware_concurrency()); - return g_hardware_concurrency; -} - -void TaskPoolImpl::AddTask(std::function &&task_fn) { - const size_t min_stack_size = 8 * 1024 * 1024; - - std::unique_lock lock(m_tasks_mutex); - m_tasks.emplace(std::move(task_fn)); - if (m_thread_count < GetHardwareConcurrencyHint()) { - m_thread_count++; - // Note that this detach call needs to happen with the m_tasks_mutex held. - // This prevents the thread from exiting prematurely and triggering a linux - // libc bug (https://sourceware.org/bugzilla/show_bug.cgi?id=19951). - llvm::Expected host_thread = - lldb_private::ThreadLauncher::LaunchThread( - "task-pool.worker", WorkerPtr, this, min_stack_size); - if (host_thread) { - host_thread->Release(); - } else { - LLDB_LOG(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST), - "failed to launch host thread: {}", - llvm::toString(host_thread.takeError())); - } - } -} - -lldb::thread_result_t TaskPoolImpl::WorkerPtr(void *pool) { - Worker((TaskPoolImpl *)pool); - return {}; -} - -void TaskPoolImpl::Worker(TaskPoolImpl *pool) { - while (true) { - std::unique_lock lock(pool->m_tasks_mutex); - if (pool->m_tasks.empty()) { - pool->m_thread_count--; - break; - } - - std::function f = std::move(pool->m_tasks.front()); - pool->m_tasks.pop(); - lock.unlock(); - - f(); - } -} - -void TaskMapOverInt(size_t begin, size_t end, - const llvm::function_ref &func) { - const size_t num_workers = std::min(end, GetHardwareConcurrencyHint()); - std::atomic idx{begin}; - - auto wrapper = [&idx, end, &func]() { - while (true) { - size_t i = idx.fetch_add(1); - if (i >= end) - break; - func(i); - } - }; - - std::vector> futures; - futures.reserve(num_workers); - for (size_t i = 0; i < num_workers; i++) - futures.push_back(TaskPool::AddTask(wrapper)); - for (size_t i = 0; i < num_workers; i++) - futures[i].wait(); -} - -} // namespace lldb_private - diff --git a/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp b/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp --- a/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp @@ -13,10 +13,10 @@ #include "Plugins/SymbolFile/DWARF/LogChannelDWARF.h" #include "Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h" #include "lldb/Core/Module.h" -#include "lldb/Host/TaskPool.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Utility/Stream.h" #include "lldb/Utility/Timer.h" +#include "llvm/Support/ThreadPool.h" using namespace lldb_private; using namespace lldb; @@ -71,20 +71,27 @@ clear_cu_dies[cu_idx] = units_to_index[cu_idx]->ExtractDIEsScoped(); }; + // Share one thread pool across operations to avoid the overhead of + // recreating the threads. + llvm::ThreadPool pool; + // Create a task runner that extracts dies for each DWARF unit in a - // separate thread + // separate thread. // First figure out which units didn't have their DIEs already // parsed and remember this. If no DIEs were parsed prior to this index // function call, we are going to want to clear the CU dies after we are // done indexing to make sure we don't pull in all DWARF dies, but we need // to wait until all units have been indexed in case a DIE in one // unit refers to another and the indexes accesses those DIEs. - TaskMapOverInt(0, units_to_index.size(), extract_fn); + for (size_t i = 0; i < units_to_index.size(); ++i) + pool.async(extract_fn, i); + pool.wait(); // Now create a task runner that can index each DWARF unit in a // separate thread so we can index quickly. - - TaskMapOverInt(0, units_to_index.size(), parser_fn); + for (size_t i = 0; i < units_to_index.size(); ++i) + pool.async(parser_fn, i); + pool.wait(); auto finalize_fn = [this, &sets](NameToDIE(IndexSet::*index)) { NameToDIE &result = m_set.*index; @@ -93,14 +100,15 @@ result.Finalize(); }; - TaskPool::RunTasks([&]() { finalize_fn(&IndexSet::function_basenames); }, - [&]() { finalize_fn(&IndexSet::function_fullnames); }, - [&]() { finalize_fn(&IndexSet::function_methods); }, - [&]() { finalize_fn(&IndexSet::function_selectors); }, - [&]() { finalize_fn(&IndexSet::objc_class_selectors); }, - [&]() { finalize_fn(&IndexSet::globals); }, - [&]() { finalize_fn(&IndexSet::types); }, - [&]() { finalize_fn(&IndexSet::namespaces); }); + pool.async(finalize_fn, &IndexSet::function_basenames); + pool.async(finalize_fn, &IndexSet::function_fullnames); + pool.async(finalize_fn, &IndexSet::function_methods); + pool.async(finalize_fn, &IndexSet::function_selectors); + pool.async(finalize_fn, &IndexSet::objc_class_selectors); + pool.async(finalize_fn, &IndexSet::globals); + pool.async(finalize_fn, &IndexSet::types); + pool.async(finalize_fn, &IndexSet::namespaces); + pool.wait(); } void ManualDWARFIndex::IndexUnit(DWARFUnit &unit, SymbolFileDWARFDwo *dwp, diff --git a/lldb/unittests/Host/CMakeLists.txt b/lldb/unittests/Host/CMakeLists.txt --- a/lldb/unittests/Host/CMakeLists.txt +++ b/lldb/unittests/Host/CMakeLists.txt @@ -11,7 +11,6 @@ SocketAddressTest.cpp SocketTest.cpp SocketTestUtilities.cpp - TaskPoolTest.cpp ) if (CMAKE_SYSTEM_NAME MATCHES "Linux|Android") diff --git a/lldb/unittests/Host/TaskPoolTest.cpp b/lldb/unittests/Host/TaskPoolTest.cpp deleted file mode 100644 --- a/lldb/unittests/Host/TaskPoolTest.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include "gtest/gtest.h" - -#include "lldb/Host/TaskPool.h" - -using namespace lldb_private; - -TEST(TaskPoolTest, AddTask) { - auto fn = [](int x) { return x * x + 1; }; - - auto f1 = TaskPool::AddTask(fn, 1); - auto f2 = TaskPool::AddTask(fn, 2); - auto f3 = TaskPool::AddTask(fn, 3); - auto f4 = TaskPool::AddTask(fn, 4); - - ASSERT_EQ(10, f3.get()); - ASSERT_EQ(2, f1.get()); - ASSERT_EQ(17, f4.get()); - ASSERT_EQ(5, f2.get()); -} - -TEST(TaskPoolTest, RunTasks) { - std::vector r(4); - - auto fn = [](int x, int &y) { y = x * x + 1; }; - - TaskPool::RunTasks([fn, &r]() { fn(1, r[0]); }, [fn, &r]() { fn(2, r[1]); }, - [fn, &r]() { fn(3, r[2]); }, [fn, &r]() { fn(4, r[3]); }); - - ASSERT_EQ(2, r[0]); - ASSERT_EQ(5, r[1]); - ASSERT_EQ(10, r[2]); - ASSERT_EQ(17, r[3]); -} - -TEST(TaskPoolTest, TaskMap) { - int data[4]; - auto fn = [&data](int x) { data[x] = x * x; }; - - TaskMapOverInt(0, 4, fn); - - ASSERT_EQ(data[0], 0); - ASSERT_EQ(data[1], 1); - ASSERT_EQ(data[2], 4); - ASSERT_EQ(data[3], 9); -}