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 23*b28f317cSMehdi Amini ThreadPool::ThreadPool(ThreadPoolStrategy S) 24*b28f317cSMehdi Amini : Strategy(S), MaxThreadCount(S.compute_thread_count()) {} 25*b28f317cSMehdi Amini 26728b982bSBenoit Jacob void ThreadPool::grow() { 27728b982bSBenoit Jacob if (Threads.size() >= MaxThreadCount) 28728b982bSBenoit Jacob return; // Already hit the max thread pool size. 29728b982bSBenoit Jacob if (ActiveThreads + Tasks.size() <= Threads.size()) 30728b982bSBenoit Jacob return; // We have enough threads for now. 31728b982bSBenoit Jacob int ThreadID = Threads.size(); 32728b982bSBenoit Jacob Threads.emplace_back([this, ThreadID] { 33728b982bSBenoit Jacob Strategy.apply_thread_strategy(ThreadID); 3433a7ea4bSMehdi Amini while (true) { 358cb1af73SFlorian Hahn std::function<void()> Task; 3633a7ea4bSMehdi Amini { 3733a7ea4bSMehdi Amini std::unique_lock<std::mutex> LockGuard(QueueLock); 3833a7ea4bSMehdi Amini // Wait for tasks to be pushed in the queue 3933a7ea4bSMehdi Amini QueueCondition.wait(LockGuard, 4033a7ea4bSMehdi Amini [&] { return !EnableFlag || !Tasks.empty(); }); 4133a7ea4bSMehdi Amini // Exit condition 4233a7ea4bSMehdi Amini if (!EnableFlag && Tasks.empty()) 4333a7ea4bSMehdi Amini return; 4433a7ea4bSMehdi Amini // Yeah, we have a task, grab it and release the lock on the queue 4533a7ea4bSMehdi Amini 4633a7ea4bSMehdi Amini // We first need to signal that we are active before popping the queue 4733a7ea4bSMehdi Amini // in order for wait() to properly detect that even if the queue is 4833a7ea4bSMehdi Amini // empty, there is still a task in flight. 49c723f657SJan Korous ++ActiveThreads; 5033a7ea4bSMehdi Amini Task = std::move(Tasks.front()); 5133a7ea4bSMehdi Amini Tasks.pop(); 5233a7ea4bSMehdi Amini } 5333a7ea4bSMehdi Amini // Run the task we just grabbed 549b8b0794SZachary Turner Task(); 5533a7ea4bSMehdi Amini 566f230491SFangrui Song bool Notify; 5733a7ea4bSMehdi Amini { 5833a7ea4bSMehdi Amini // Adjust `ActiveThreads`, in case someone waits on ThreadPool::wait() 596f230491SFangrui Song std::lock_guard<std::mutex> LockGuard(QueueLock); 6033a7ea4bSMehdi Amini --ActiveThreads; 616f230491SFangrui Song Notify = workCompletedUnlocked(); 6233a7ea4bSMehdi Amini } 636f230491SFangrui Song // Notify task completion if this is the last active thread, in case 646f230491SFangrui Song // someone waits on ThreadPool::wait(). 656f230491SFangrui Song if (Notify) 6633a7ea4bSMehdi Amini CompletionCondition.notify_all(); 6733a7ea4bSMehdi Amini } 6833a7ea4bSMehdi Amini }); 6933a7ea4bSMehdi Amini } 7033a7ea4bSMehdi Amini 7133a7ea4bSMehdi Amini void ThreadPool::wait() { 7233a7ea4bSMehdi Amini // Wait for all threads to complete and the queue to be empty 736f230491SFangrui Song std::unique_lock<std::mutex> LockGuard(QueueLock); 746f230491SFangrui Song CompletionCondition.wait(LockGuard, [&] { return workCompletedUnlocked(); }); 7533a7ea4bSMehdi Amini } 7633a7ea4bSMehdi Amini 776569cf2aSRiver Riddle bool ThreadPool::isWorkerThread() const { 78728b982bSBenoit Jacob std::unique_lock<std::mutex> LockGuard(QueueLock); 7948c68a63STim Northover llvm::thread::id CurrentThreadId = llvm::this_thread::get_id(); 8048c68a63STim Northover for (const llvm::thread &Thread : Threads) 816569cf2aSRiver Riddle if (CurrentThreadId == Thread.get_id()) 826569cf2aSRiver Riddle return true; 836569cf2aSRiver Riddle return false; 846569cf2aSRiver Riddle } 856569cf2aSRiver Riddle 8633a7ea4bSMehdi Amini // The destructor joins all threads, waiting for completion. 8733a7ea4bSMehdi Amini ThreadPool::~ThreadPool() { 8833a7ea4bSMehdi Amini { 8933a7ea4bSMehdi Amini std::unique_lock<std::mutex> LockGuard(QueueLock); 9033a7ea4bSMehdi Amini EnableFlag = false; 9133a7ea4bSMehdi Amini } 9233a7ea4bSMehdi Amini QueueCondition.notify_all(); 9333a7ea4bSMehdi Amini for (auto &Worker : Threads) 9433a7ea4bSMehdi Amini Worker.join(); 9533a7ea4bSMehdi Amini } 9633a7ea4bSMehdi Amini 9733a7ea4bSMehdi Amini #else // LLVM_ENABLE_THREADS Disabled 9833a7ea4bSMehdi Amini 9933a7ea4bSMehdi Amini // No threads are launched, issue a warning if ThreadCount is not 0 100*b28f317cSMehdi Amini ThreadPool::ThreadPool(ThreadPoolStrategy S) : MaxThreadCount(1) { 101*b28f317cSMehdi Amini int ThreadCount = S.compute_thread_count(); 1028404aeb5SAlexandre Ganea if (ThreadCount != 1) { 10333a7ea4bSMehdi Amini errs() << "Warning: request a ThreadPool with " << ThreadCount 10433a7ea4bSMehdi Amini << " threads, but LLVM_ENABLE_THREADS has been turned off\n"; 10533a7ea4bSMehdi Amini } 10633a7ea4bSMehdi Amini } 10733a7ea4bSMehdi Amini 10833a7ea4bSMehdi Amini void ThreadPool::wait() { 10933a7ea4bSMehdi Amini // Sequential implementation running the tasks 11033a7ea4bSMehdi Amini while (!Tasks.empty()) { 11133a7ea4bSMehdi Amini auto Task = std::move(Tasks.front()); 11233a7ea4bSMehdi Amini Tasks.pop(); 11333a7ea4bSMehdi Amini Task(); 11433a7ea4bSMehdi Amini } 11533a7ea4bSMehdi Amini } 11633a7ea4bSMehdi Amini 1178404aeb5SAlexandre Ganea ThreadPool::~ThreadPool() { wait(); } 11833a7ea4bSMehdi Amini 11933a7ea4bSMehdi Amini #endif 120