133a7ea4bSMehdi Amini //==-- llvm/Support/ThreadPool.cpp - A ThreadPool implementation -*- C++ -*-==// 233a7ea4bSMehdi Amini // 32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information. 52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 633a7ea4bSMehdi Amini // 733a7ea4bSMehdi Amini //===----------------------------------------------------------------------===// 833a7ea4bSMehdi Amini // 933a7ea4bSMehdi Amini // This file implements a crude C++11 based thread pool. 1033a7ea4bSMehdi Amini // 1133a7ea4bSMehdi Amini //===----------------------------------------------------------------------===// 1233a7ea4bSMehdi Amini 1333a7ea4bSMehdi Amini #include "llvm/Support/ThreadPool.h" 1433a7ea4bSMehdi Amini 1533a7ea4bSMehdi Amini #include "llvm/Config/llvm-config.h" 16*0984aa70Sserge-sans-paille 17*0984aa70Sserge-sans-paille #if LLVM_ENABLE_THREADS 188c0ff950SRafael Espindola #include "llvm/Support/Threading.h" 19*0984aa70Sserge-sans-paille #else 20*0984aa70Sserge-sans-paille #include "llvm/Support/raw_ostream.h" 21*0984aa70Sserge-sans-paille #endif 2233a7ea4bSMehdi Amini 2333a7ea4bSMehdi Amini using namespace llvm; 2433a7ea4bSMehdi Amini 2533a7ea4bSMehdi Amini #if LLVM_ENABLE_THREADS 2633a7ea4bSMehdi Amini 27b28f317cSMehdi Amini ThreadPool::ThreadPool(ThreadPoolStrategy S) 28b28f317cSMehdi Amini : Strategy(S), MaxThreadCount(S.compute_thread_count()) {} 29b28f317cSMehdi Amini 30e8469718SMehdi Amini void ThreadPool::grow(int requested) { 31e8469718SMehdi Amini std::unique_lock<std::mutex> LockGuard(ThreadsLock); 32728b982bSBenoit Jacob if (Threads.size() >= MaxThreadCount) 33728b982bSBenoit Jacob return; // Already hit the max thread pool size. 34e8469718SMehdi Amini int newThreadCount = std::min<int>(requested, MaxThreadCount); 35e8469718SMehdi Amini while (static_cast<int>(Threads.size()) < newThreadCount) { 36728b982bSBenoit Jacob int ThreadID = Threads.size(); 37728b982bSBenoit Jacob Threads.emplace_back([this, ThreadID] { 38728b982bSBenoit Jacob Strategy.apply_thread_strategy(ThreadID); 3933a7ea4bSMehdi Amini while (true) { 408cb1af73SFlorian Hahn std::function<void()> Task; 4133a7ea4bSMehdi Amini { 4233a7ea4bSMehdi Amini std::unique_lock<std::mutex> LockGuard(QueueLock); 4333a7ea4bSMehdi Amini // Wait for tasks to be pushed in the queue 4433a7ea4bSMehdi Amini QueueCondition.wait(LockGuard, 4533a7ea4bSMehdi Amini [&] { return !EnableFlag || !Tasks.empty(); }); 4633a7ea4bSMehdi Amini // Exit condition 4733a7ea4bSMehdi Amini if (!EnableFlag && Tasks.empty()) 4833a7ea4bSMehdi Amini return; 4933a7ea4bSMehdi Amini // Yeah, we have a task, grab it and release the lock on the queue 5033a7ea4bSMehdi Amini 5133a7ea4bSMehdi Amini // We first need to signal that we are active before popping the queue 5233a7ea4bSMehdi Amini // in order for wait() to properly detect that even if the queue is 5333a7ea4bSMehdi Amini // empty, there is still a task in flight. 54c723f657SJan Korous ++ActiveThreads; 5533a7ea4bSMehdi Amini Task = std::move(Tasks.front()); 5633a7ea4bSMehdi Amini Tasks.pop(); 5733a7ea4bSMehdi Amini } 5833a7ea4bSMehdi Amini // Run the task we just grabbed 599b8b0794SZachary Turner Task(); 6033a7ea4bSMehdi Amini 616f230491SFangrui Song bool Notify; 6233a7ea4bSMehdi Amini { 6333a7ea4bSMehdi Amini // Adjust `ActiveThreads`, in case someone waits on ThreadPool::wait() 646f230491SFangrui Song std::lock_guard<std::mutex> LockGuard(QueueLock); 6533a7ea4bSMehdi Amini --ActiveThreads; 666f230491SFangrui Song Notify = workCompletedUnlocked(); 6733a7ea4bSMehdi Amini } 686f230491SFangrui Song // Notify task completion if this is the last active thread, in case 696f230491SFangrui Song // someone waits on ThreadPool::wait(). 706f230491SFangrui Song if (Notify) 7133a7ea4bSMehdi Amini CompletionCondition.notify_all(); 7233a7ea4bSMehdi Amini } 7333a7ea4bSMehdi Amini }); 7433a7ea4bSMehdi Amini } 75e8469718SMehdi Amini } 7633a7ea4bSMehdi Amini 7733a7ea4bSMehdi Amini void ThreadPool::wait() { 7833a7ea4bSMehdi Amini // Wait for all threads to complete and the queue to be empty 796f230491SFangrui Song std::unique_lock<std::mutex> LockGuard(QueueLock); 806f230491SFangrui Song CompletionCondition.wait(LockGuard, [&] { return workCompletedUnlocked(); }); 8133a7ea4bSMehdi Amini } 8233a7ea4bSMehdi Amini 836569cf2aSRiver Riddle bool ThreadPool::isWorkerThread() const { 84e8469718SMehdi Amini std::unique_lock<std::mutex> LockGuard(ThreadsLock); 8548c68a63STim Northover llvm::thread::id CurrentThreadId = llvm::this_thread::get_id(); 8648c68a63STim Northover for (const llvm::thread &Thread : Threads) 876569cf2aSRiver Riddle if (CurrentThreadId == Thread.get_id()) 886569cf2aSRiver Riddle return true; 896569cf2aSRiver Riddle return false; 906569cf2aSRiver Riddle } 916569cf2aSRiver Riddle 9233a7ea4bSMehdi Amini // The destructor joins all threads, waiting for completion. 9333a7ea4bSMehdi Amini ThreadPool::~ThreadPool() { 9433a7ea4bSMehdi Amini { 9533a7ea4bSMehdi Amini std::unique_lock<std::mutex> LockGuard(QueueLock); 9633a7ea4bSMehdi Amini EnableFlag = false; 9733a7ea4bSMehdi Amini } 9833a7ea4bSMehdi Amini QueueCondition.notify_all(); 99e8469718SMehdi Amini std::unique_lock<std::mutex> LockGuard(ThreadsLock); 10033a7ea4bSMehdi Amini for (auto &Worker : Threads) 10133a7ea4bSMehdi Amini Worker.join(); 10233a7ea4bSMehdi Amini } 10333a7ea4bSMehdi Amini 10433a7ea4bSMehdi Amini #else // LLVM_ENABLE_THREADS Disabled 10533a7ea4bSMehdi Amini 10633a7ea4bSMehdi Amini // No threads are launched, issue a warning if ThreadCount is not 0 107b28f317cSMehdi Amini ThreadPool::ThreadPool(ThreadPoolStrategy S) : MaxThreadCount(1) { 108b28f317cSMehdi Amini int ThreadCount = S.compute_thread_count(); 1098404aeb5SAlexandre Ganea if (ThreadCount != 1) { 11033a7ea4bSMehdi Amini errs() << "Warning: request a ThreadPool with " << ThreadCount 11133a7ea4bSMehdi Amini << " threads, but LLVM_ENABLE_THREADS has been turned off\n"; 11233a7ea4bSMehdi Amini } 11333a7ea4bSMehdi Amini } 11433a7ea4bSMehdi Amini 11533a7ea4bSMehdi Amini void ThreadPool::wait() { 11633a7ea4bSMehdi Amini // Sequential implementation running the tasks 11733a7ea4bSMehdi Amini while (!Tasks.empty()) { 11833a7ea4bSMehdi Amini auto Task = std::move(Tasks.front()); 11933a7ea4bSMehdi Amini Tasks.pop(); 12033a7ea4bSMehdi Amini Task(); 12133a7ea4bSMehdi Amini } 12233a7ea4bSMehdi Amini } 12333a7ea4bSMehdi Amini 124d9547f41SJohn Demme bool ThreadPool::isWorkerThread() const { 125e5c944b4SFangrui Song report_fatal_error("LLVM compiled without multithreading"); 126d9547f41SJohn Demme } 127d9547f41SJohn Demme 1288404aeb5SAlexandre Ganea ThreadPool::~ThreadPool() { wait(); } 12933a7ea4bSMehdi Amini 13033a7ea4bSMehdi Amini #endif 131