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 18 using namespace llvm; 19 20 #if LLVM_ENABLE_THREADS 21 22 ThreadPool::ThreadPool(ThreadPoolStrategy S) 23 : Strategy(S), MaxThreadCount(S.compute_thread_count()) {} 24 25 void ThreadPool::grow(int requested) { 26 std::unique_lock<std::mutex> LockGuard(ThreadsLock); 27 if (Threads.size() >= MaxThreadCount) 28 return; // Already hit the max thread pool size. 29 int newThreadCount = std::min<int>(requested, MaxThreadCount); 30 while (static_cast<int>(Threads.size()) < newThreadCount) { 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 72 void ThreadPool::wait() { 73 // Wait for all threads to complete and the queue to be empty 74 std::unique_lock<std::mutex> LockGuard(QueueLock); 75 CompletionCondition.wait(LockGuard, [&] { return workCompletedUnlocked(); }); 76 } 77 78 bool ThreadPool::isWorkerThread() const { 79 std::unique_lock<std::mutex> LockGuard(ThreadsLock); 80 llvm::thread::id CurrentThreadId = llvm::this_thread::get_id(); 81 for (const llvm::thread &Thread : Threads) 82 if (CurrentThreadId == Thread.get_id()) 83 return true; 84 return false; 85 } 86 87 // The destructor joins all threads, waiting for completion. 88 ThreadPool::~ThreadPool() { 89 { 90 std::unique_lock<std::mutex> LockGuard(QueueLock); 91 EnableFlag = false; 92 } 93 QueueCondition.notify_all(); 94 std::unique_lock<std::mutex> LockGuard(ThreadsLock); 95 for (auto &Worker : Threads) 96 Worker.join(); 97 } 98 99 #else // LLVM_ENABLE_THREADS Disabled 100 101 // No threads are launched, issue a warning if ThreadCount is not 0 102 ThreadPool::ThreadPool(ThreadPoolStrategy S) : MaxThreadCount(1) { 103 int ThreadCount = S.compute_thread_count(); 104 if (ThreadCount != 1) { 105 errs() << "Warning: request a ThreadPool with " << ThreadCount 106 << " threads, but LLVM_ENABLE_THREADS has been turned off\n"; 107 } 108 } 109 110 void ThreadPool::wait() { 111 // Sequential implementation running the tasks 112 while (!Tasks.empty()) { 113 auto Task = std::move(Tasks.front()); 114 Tasks.pop(); 115 Task(); 116 } 117 } 118 119 bool ThreadPool::isWorkerThread() const { 120 report_fatal_error("LLVM compiled without multithreading"); 121 } 122 123 ThreadPool::~ThreadPool() { wait(); } 124 125 #endif 126