Files
clang-p2996/lldb/source/Host/common/TaskPool.cpp
Francis Ricci 7ddfe8ef75 Use ThreadLauncher to launch TaskPool threads
Summary:
This allows for the stack size to be configured, which isn't
possible with std::thread. Prevents overflowing the stack when
performing complex operations in the task pool on darwin,
where the default pthread stack size is only 512kb.

This also moves TaskPool from Utility to Host.

Reviewers: labath, tberghammer, clayborg

Subscribers: lldb-commits

Differential Revision: https://reviews.llvm.org/D37930

llvm-svn: 313637
2017-09-19 15:38:30 +00:00

110 lines
2.9 KiB
C++

//===--------------------- TaskPool.cpp -------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "lldb/Host/TaskPool.h"
#include "lldb/Host/ThreadLauncher.h"
#include <cstdint> // for uint32_t
#include <queue> // for queue
#include <thread> // for thread
namespace {
class TaskPoolImpl {
public:
static TaskPoolImpl &GetInstance();
void AddTask(std::function<void()> &&task_fn);
private:
TaskPoolImpl();
static lldb::thread_result_t WorkerPtr(void *pool);
static void Worker(TaskPoolImpl *pool);
std::queue<std::function<void()>> 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<void()> &&task_fn) {
TaskPoolImpl::GetInstance().AddTask(std::move(task_fn));
}
TaskPoolImpl::TaskPoolImpl() : m_thread_count(0) {}
void TaskPoolImpl::AddTask(std::function<void()> &&task_fn) {
static const uint32_t max_threads = std::thread::hardware_concurrency();
const size_t min_stack_size = 8 * 1024 * 1024;
std::unique_lock<std::mutex> lock(m_tasks_mutex);
m_tasks.emplace(std::move(task_fn));
if (m_thread_count < max_threads) {
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).
lldb_private::ThreadLauncher::LaunchThread("task-pool.worker", WorkerPtr,
this, nullptr, min_stack_size)
.Release();
}
}
lldb::thread_result_t TaskPoolImpl::WorkerPtr(void *pool) {
Worker((TaskPoolImpl *)pool);
return 0;
}
void TaskPoolImpl::Worker(TaskPoolImpl *pool) {
while (true) {
std::unique_lock<std::mutex> lock(pool->m_tasks_mutex);
if (pool->m_tasks.empty()) {
pool->m_thread_count--;
break;
}
std::function<void()> f = pool->m_tasks.front();
pool->m_tasks.pop();
lock.unlock();
f();
}
}
void TaskMapOverInt(size_t begin, size_t end,
const llvm::function_ref<void(size_t)> &func) {
std::atomic<size_t> idx{begin};
size_t num_workers =
std::min<size_t>(end, std::thread::hardware_concurrency());
auto wrapper = [&idx, end, &func]() {
while (true) {
size_t i = idx.fetch_add(1);
if (i >= end)
break;
func(i);
}
};
std::vector<std::future<void>> 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();
}