1 //==-- llvm/Support/ThreadPool.cpp - A ThreadPool implementation -*- C++ -*-==// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file implements a crude C++11 based thread pool. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "llvm/Support/ThreadPool.h" 14 15 #include "llvm/Config/llvm-config.h" 16 #include "llvm/Support/Threading.h" 17 #include "llvm/Support/raw_ostream.h" 18 19 using namespace llvm; 20 21 #if LLVM_ENABLE_THREADS 22 23 ThreadPool::ThreadPool(ThreadPoolStrategy S) 24 : Strategy(S), MaxThreadCount(S.compute_thread_count()) {} 25 26 void ThreadPool::grow() { 27 if (Threads.size() >= MaxThreadCount) 28 return; // Already hit the max thread pool size. 29 if (ActiveThreads + Tasks.size() <= Threads.size()) 30 return; // We have enough threads for now. 31 int ThreadID = Threads.size(); 32 Threads.emplace_back([this, ThreadID] { 33 Strategy.apply_thread_strategy(ThreadID); 34 while (true) { 35 std::function<void()> Task; 36 { 37 std::unique_lock<std::mutex> LockGuard(QueueLock); 38 // Wait for tasks to be pushed in the queue 39 QueueCondition.wait(LockGuard, 40 [&] { return !EnableFlag || !Tasks.empty(); }); 41 // Exit condition 42 if (!EnableFlag && Tasks.empty()) 43 return; 44 // Yeah, we have a task, grab it and release the lock on the queue 45 46 // We first need to signal that we are active before popping the queue 47 // in order for wait() to properly detect that even if the queue is 48 // empty, there is still a task in flight. 49 ++ActiveThreads; 50 Task = std::move(Tasks.front()); 51 Tasks.pop(); 52 } 53 // Run the task we just grabbed 54 Task(); 55 56 bool Notify; 57 { 58 // Adjust `ActiveThreads`, in case someone waits on ThreadPool::wait() 59 std::lock_guard<std::mutex> LockGuard(QueueLock); 60 --ActiveThreads; 61 Notify = workCompletedUnlocked(); 62 } 63 // Notify task completion if this is the last active thread, in case 64 // someone waits on ThreadPool::wait(). 65 if (Notify) 66 CompletionCondition.notify_all(); 67 } 68 }); 69 } 70 71 void ThreadPool::wait() { 72 // Wait for all threads to complete and the queue to be empty 73 std::unique_lock<std::mutex> LockGuard(QueueLock); 74 CompletionCondition.wait(LockGuard, [&] { return workCompletedUnlocked(); }); 75 } 76 77 bool ThreadPool::isWorkerThread() const { 78 std::unique_lock<std::mutex> LockGuard(QueueLock); 79 llvm::thread::id CurrentThreadId = llvm::this_thread::get_id(); 80 for (const llvm::thread &Thread : Threads) 81 if (CurrentThreadId == Thread.get_id()) 82 return true; 83 return false; 84 } 85 86 // The destructor joins all threads, waiting for completion. 87 ThreadPool::~ThreadPool() { 88 { 89 std::unique_lock<std::mutex> LockGuard(QueueLock); 90 EnableFlag = false; 91 } 92 QueueCondition.notify_all(); 93 for (auto &Worker : Threads) 94 Worker.join(); 95 } 96 97 #else // LLVM_ENABLE_THREADS Disabled 98 99 // No threads are launched, issue a warning if ThreadCount is not 0 100 ThreadPool::ThreadPool(ThreadPoolStrategy S) : MaxThreadCount(1) { 101 int ThreadCount = S.compute_thread_count(); 102 if (ThreadCount != 1) { 103 errs() << "Warning: request a ThreadPool with " << ThreadCount 104 << " threads, but LLVM_ENABLE_THREADS has been turned off\n"; 105 } 106 } 107 108 void ThreadPool::wait() { 109 // Sequential implementation running the tasks 110 while (!Tasks.empty()) { 111 auto Task = std::move(Tasks.front()); 112 Tasks.pop(); 113 Task(); 114 } 115 } 116 117 ThreadPool::~ThreadPool() { wait(); } 118 119 #endif 120