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(int requested) { 27 std::unique_lock<std::mutex> LockGuard(ThreadsLock); 28 if (Threads.size() >= MaxThreadCount) 29 return; // Already hit the max thread pool size. 30 int newThreadCount = std::min<int>(requested, MaxThreadCount); 31 while (static_cast<int>(Threads.size()) < newThreadCount) { 32 int ThreadID = Threads.size(); 33 Threads.emplace_back([this, ThreadID] { 34 Strategy.apply_thread_strategy(ThreadID); 35 while (true) { 36 std::function<void()> Task; 37 { 38 std::unique_lock<std::mutex> LockGuard(QueueLock); 39 // Wait for tasks to be pushed in the queue 40 QueueCondition.wait(LockGuard, 41 [&] { return !EnableFlag || !Tasks.empty(); }); 42 // Exit condition 43 if (!EnableFlag && Tasks.empty()) 44 return; 45 // Yeah, we have a task, grab it and release the lock on the queue 46 47 // We first need to signal that we are active before popping the queue 48 // in order for wait() to properly detect that even if the queue is 49 // empty, there is still a task in flight. 50 ++ActiveThreads; 51 Task = std::move(Tasks.front()); 52 Tasks.pop(); 53 } 54 // Run the task we just grabbed 55 Task(); 56 57 bool Notify; 58 { 59 // Adjust `ActiveThreads`, in case someone waits on ThreadPool::wait() 60 std::lock_guard<std::mutex> LockGuard(QueueLock); 61 --ActiveThreads; 62 Notify = workCompletedUnlocked(); 63 } 64 // Notify task completion if this is the last active thread, in case 65 // someone waits on ThreadPool::wait(). 66 if (Notify) 67 CompletionCondition.notify_all(); 68 } 69 }); 70 } 71 } 72 73 void ThreadPool::wait() { 74 // Wait for all threads to complete and the queue to be empty 75 std::unique_lock<std::mutex> LockGuard(QueueLock); 76 CompletionCondition.wait(LockGuard, [&] { return workCompletedUnlocked(); }); 77 } 78 79 bool ThreadPool::isWorkerThread() const { 80 std::unique_lock<std::mutex> LockGuard(ThreadsLock); 81 llvm::thread::id CurrentThreadId = llvm::this_thread::get_id(); 82 for (const llvm::thread &Thread : Threads) 83 if (CurrentThreadId == Thread.get_id()) 84 return true; 85 return false; 86 } 87 88 // The destructor joins all threads, waiting for completion. 89 ThreadPool::~ThreadPool() { 90 { 91 std::unique_lock<std::mutex> LockGuard(QueueLock); 92 EnableFlag = false; 93 } 94 QueueCondition.notify_all(); 95 std::unique_lock<std::mutex> LockGuard(ThreadsLock); 96 for (auto &Worker : Threads) 97 Worker.join(); 98 } 99 100 #else // LLVM_ENABLE_THREADS Disabled 101 102 // No threads are launched, issue a warning if ThreadCount is not 0 103 ThreadPool::ThreadPool(ThreadPoolStrategy S) : MaxThreadCount(1) { 104 int ThreadCount = S.compute_thread_count(); 105 if (ThreadCount != 1) { 106 errs() << "Warning: request a ThreadPool with " << ThreadCount 107 << " threads, but LLVM_ENABLE_THREADS has been turned off\n"; 108 } 109 } 110 111 void ThreadPool::wait() { 112 // Sequential implementation running the tasks 113 while (!Tasks.empty()) { 114 auto Task = std::move(Tasks.front()); 115 Tasks.pop(); 116 Task(); 117 } 118 } 119 120 bool ThreadPool::isWorkerThread() const { 121 report_fatal_error("LLVM compiled without multithreading"); 122 } 123 124 ThreadPool::~ThreadPool() { wait(); } 125 126 #endif 127