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" 168c0ff950SRafael Espindola #include "llvm/Support/Threading.h" 1786f0b70fSHans Wennborg #include "llvm/Support/raw_ostream.h" 1833a7ea4bSMehdi Amini 1933a7ea4bSMehdi Amini using namespace llvm; 2033a7ea4bSMehdi Amini 2133a7ea4bSMehdi Amini #if LLVM_ENABLE_THREADS 2233a7ea4bSMehdi Amini 23b28f317cSMehdi Amini ThreadPool::ThreadPool(ThreadPoolStrategy S) 24b28f317cSMehdi Amini : Strategy(S), MaxThreadCount(S.compute_thread_count()) {} 25b28f317cSMehdi Amini 26*e8469718SMehdi Amini void ThreadPool::grow(int requested) { 27*e8469718SMehdi Amini std::unique_lock<std::mutex> LockGuard(ThreadsLock); 28728b982bSBenoit Jacob if (Threads.size() >= MaxThreadCount) 29728b982bSBenoit Jacob return; // Already hit the max thread pool size. 30*e8469718SMehdi Amini int newThreadCount = std::min<int>(requested, MaxThreadCount); 31*e8469718SMehdi Amini while (static_cast<int>(Threads.size()) < newThreadCount) { 32728b982bSBenoit Jacob int ThreadID = Threads.size(); 33728b982bSBenoit Jacob Threads.emplace_back([this, ThreadID] { 34728b982bSBenoit Jacob Strategy.apply_thread_strategy(ThreadID); 3533a7ea4bSMehdi Amini while (true) { 368cb1af73SFlorian Hahn std::function<void()> Task; 3733a7ea4bSMehdi Amini { 3833a7ea4bSMehdi Amini std::unique_lock<std::mutex> LockGuard(QueueLock); 3933a7ea4bSMehdi Amini // Wait for tasks to be pushed in the queue 4033a7ea4bSMehdi Amini QueueCondition.wait(LockGuard, 4133a7ea4bSMehdi Amini [&] { return !EnableFlag || !Tasks.empty(); }); 4233a7ea4bSMehdi Amini // Exit condition 4333a7ea4bSMehdi Amini if (!EnableFlag && Tasks.empty()) 4433a7ea4bSMehdi Amini return; 4533a7ea4bSMehdi Amini // Yeah, we have a task, grab it and release the lock on the queue 4633a7ea4bSMehdi Amini 4733a7ea4bSMehdi Amini // We first need to signal that we are active before popping the queue 4833a7ea4bSMehdi Amini // in order for wait() to properly detect that even if the queue is 4933a7ea4bSMehdi Amini // empty, there is still a task in flight. 50c723f657SJan Korous ++ActiveThreads; 5133a7ea4bSMehdi Amini Task = std::move(Tasks.front()); 5233a7ea4bSMehdi Amini Tasks.pop(); 5333a7ea4bSMehdi Amini } 5433a7ea4bSMehdi Amini // Run the task we just grabbed 559b8b0794SZachary Turner Task(); 5633a7ea4bSMehdi Amini 576f230491SFangrui Song bool Notify; 5833a7ea4bSMehdi Amini { 5933a7ea4bSMehdi Amini // Adjust `ActiveThreads`, in case someone waits on ThreadPool::wait() 606f230491SFangrui Song std::lock_guard<std::mutex> LockGuard(QueueLock); 6133a7ea4bSMehdi Amini --ActiveThreads; 626f230491SFangrui Song Notify = workCompletedUnlocked(); 6333a7ea4bSMehdi Amini } 646f230491SFangrui Song // Notify task completion if this is the last active thread, in case 656f230491SFangrui Song // someone waits on ThreadPool::wait(). 666f230491SFangrui Song if (Notify) 6733a7ea4bSMehdi Amini CompletionCondition.notify_all(); 6833a7ea4bSMehdi Amini } 6933a7ea4bSMehdi Amini }); 7033a7ea4bSMehdi Amini } 71*e8469718SMehdi Amini } 7233a7ea4bSMehdi Amini 7333a7ea4bSMehdi Amini void ThreadPool::wait() { 7433a7ea4bSMehdi Amini // Wait for all threads to complete and the queue to be empty 756f230491SFangrui Song std::unique_lock<std::mutex> LockGuard(QueueLock); 766f230491SFangrui Song CompletionCondition.wait(LockGuard, [&] { return workCompletedUnlocked(); }); 7733a7ea4bSMehdi Amini } 7833a7ea4bSMehdi Amini 796569cf2aSRiver Riddle bool ThreadPool::isWorkerThread() const { 80*e8469718SMehdi Amini std::unique_lock<std::mutex> LockGuard(ThreadsLock); 8148c68a63STim Northover llvm::thread::id CurrentThreadId = llvm::this_thread::get_id(); 8248c68a63STim Northover for (const llvm::thread &Thread : Threads) 836569cf2aSRiver Riddle if (CurrentThreadId == Thread.get_id()) 846569cf2aSRiver Riddle return true; 856569cf2aSRiver Riddle return false; 866569cf2aSRiver Riddle } 876569cf2aSRiver Riddle 8833a7ea4bSMehdi Amini // The destructor joins all threads, waiting for completion. 8933a7ea4bSMehdi Amini ThreadPool::~ThreadPool() { 9033a7ea4bSMehdi Amini { 9133a7ea4bSMehdi Amini std::unique_lock<std::mutex> LockGuard(QueueLock); 9233a7ea4bSMehdi Amini EnableFlag = false; 9333a7ea4bSMehdi Amini } 9433a7ea4bSMehdi Amini QueueCondition.notify_all(); 95*e8469718SMehdi Amini std::unique_lock<std::mutex> LockGuard(ThreadsLock); 9633a7ea4bSMehdi Amini for (auto &Worker : Threads) 9733a7ea4bSMehdi Amini Worker.join(); 9833a7ea4bSMehdi Amini } 9933a7ea4bSMehdi Amini 10033a7ea4bSMehdi Amini #else // LLVM_ENABLE_THREADS Disabled 10133a7ea4bSMehdi Amini 10233a7ea4bSMehdi Amini // No threads are launched, issue a warning if ThreadCount is not 0 103b28f317cSMehdi Amini ThreadPool::ThreadPool(ThreadPoolStrategy S) : MaxThreadCount(1) { 104b28f317cSMehdi Amini int ThreadCount = S.compute_thread_count(); 1058404aeb5SAlexandre Ganea if (ThreadCount != 1) { 10633a7ea4bSMehdi Amini errs() << "Warning: request a ThreadPool with " << ThreadCount 10733a7ea4bSMehdi Amini << " threads, but LLVM_ENABLE_THREADS has been turned off\n"; 10833a7ea4bSMehdi Amini } 10933a7ea4bSMehdi Amini } 11033a7ea4bSMehdi Amini 11133a7ea4bSMehdi Amini void ThreadPool::wait() { 11233a7ea4bSMehdi Amini // Sequential implementation running the tasks 11333a7ea4bSMehdi Amini while (!Tasks.empty()) { 11433a7ea4bSMehdi Amini auto Task = std::move(Tasks.front()); 11533a7ea4bSMehdi Amini Tasks.pop(); 11633a7ea4bSMehdi Amini Task(); 11733a7ea4bSMehdi Amini } 11833a7ea4bSMehdi Amini } 11933a7ea4bSMehdi Amini 1208404aeb5SAlexandre Ganea ThreadPool::~ThreadPool() { wait(); } 12133a7ea4bSMehdi Amini 12233a7ea4bSMehdi Amini #endif 123