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