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"
16*0984aa70Sserge-sans-paille 
17*0984aa70Sserge-sans-paille #if LLVM_ENABLE_THREADS
188c0ff950SRafael Espindola #include "llvm/Support/Threading.h"
19*0984aa70Sserge-sans-paille #else
20*0984aa70Sserge-sans-paille #include "llvm/Support/raw_ostream.h"
21*0984aa70Sserge-sans-paille #endif
2233a7ea4bSMehdi Amini 
2333a7ea4bSMehdi Amini using namespace llvm;
2433a7ea4bSMehdi Amini 
2533a7ea4bSMehdi Amini #if LLVM_ENABLE_THREADS
2633a7ea4bSMehdi Amini 
27b28f317cSMehdi Amini ThreadPool::ThreadPool(ThreadPoolStrategy S)
28b28f317cSMehdi Amini     : Strategy(S), MaxThreadCount(S.compute_thread_count()) {}
29b28f317cSMehdi Amini 
30e8469718SMehdi Amini void ThreadPool::grow(int requested) {
31e8469718SMehdi Amini   std::unique_lock<std::mutex> LockGuard(ThreadsLock);
32728b982bSBenoit Jacob   if (Threads.size() >= MaxThreadCount)
33728b982bSBenoit Jacob     return; // Already hit the max thread pool size.
34e8469718SMehdi Amini   int newThreadCount = std::min<int>(requested, MaxThreadCount);
35e8469718SMehdi Amini   while (static_cast<int>(Threads.size()) < newThreadCount) {
36728b982bSBenoit Jacob     int ThreadID = Threads.size();
37728b982bSBenoit Jacob     Threads.emplace_back([this, ThreadID] {
38728b982bSBenoit Jacob       Strategy.apply_thread_strategy(ThreadID);
3933a7ea4bSMehdi Amini       while (true) {
408cb1af73SFlorian Hahn         std::function<void()> Task;
4133a7ea4bSMehdi Amini         {
4233a7ea4bSMehdi Amini           std::unique_lock<std::mutex> LockGuard(QueueLock);
4333a7ea4bSMehdi Amini           // Wait for tasks to be pushed in the queue
4433a7ea4bSMehdi Amini           QueueCondition.wait(LockGuard,
4533a7ea4bSMehdi Amini                               [&] { return !EnableFlag || !Tasks.empty(); });
4633a7ea4bSMehdi Amini           // Exit condition
4733a7ea4bSMehdi Amini           if (!EnableFlag && Tasks.empty())
4833a7ea4bSMehdi Amini             return;
4933a7ea4bSMehdi Amini           // Yeah, we have a task, grab it and release the lock on the queue
5033a7ea4bSMehdi Amini 
5133a7ea4bSMehdi Amini           // We first need to signal that we are active before popping the queue
5233a7ea4bSMehdi Amini           // in order for wait() to properly detect that even if the queue is
5333a7ea4bSMehdi Amini           // empty, there is still a task in flight.
54c723f657SJan Korous           ++ActiveThreads;
5533a7ea4bSMehdi Amini           Task = std::move(Tasks.front());
5633a7ea4bSMehdi Amini           Tasks.pop();
5733a7ea4bSMehdi Amini         }
5833a7ea4bSMehdi Amini         // Run the task we just grabbed
599b8b0794SZachary Turner         Task();
6033a7ea4bSMehdi Amini 
616f230491SFangrui Song         bool Notify;
6233a7ea4bSMehdi Amini         {
6333a7ea4bSMehdi Amini           // Adjust `ActiveThreads`, in case someone waits on ThreadPool::wait()
646f230491SFangrui Song           std::lock_guard<std::mutex> LockGuard(QueueLock);
6533a7ea4bSMehdi Amini           --ActiveThreads;
666f230491SFangrui Song           Notify = workCompletedUnlocked();
6733a7ea4bSMehdi Amini         }
686f230491SFangrui Song         // Notify task completion if this is the last active thread, in case
696f230491SFangrui Song         // someone waits on ThreadPool::wait().
706f230491SFangrui Song         if (Notify)
7133a7ea4bSMehdi Amini           CompletionCondition.notify_all();
7233a7ea4bSMehdi Amini       }
7333a7ea4bSMehdi Amini     });
7433a7ea4bSMehdi Amini   }
75e8469718SMehdi Amini }
7633a7ea4bSMehdi Amini 
7733a7ea4bSMehdi Amini void ThreadPool::wait() {
7833a7ea4bSMehdi Amini   // Wait for all threads to complete and the queue to be empty
796f230491SFangrui Song   std::unique_lock<std::mutex> LockGuard(QueueLock);
806f230491SFangrui Song   CompletionCondition.wait(LockGuard, [&] { return workCompletedUnlocked(); });
8133a7ea4bSMehdi Amini }
8233a7ea4bSMehdi Amini 
836569cf2aSRiver Riddle bool ThreadPool::isWorkerThread() const {
84e8469718SMehdi Amini   std::unique_lock<std::mutex> LockGuard(ThreadsLock);
8548c68a63STim Northover   llvm::thread::id CurrentThreadId = llvm::this_thread::get_id();
8648c68a63STim Northover   for (const llvm::thread &Thread : Threads)
876569cf2aSRiver Riddle     if (CurrentThreadId == Thread.get_id())
886569cf2aSRiver Riddle       return true;
896569cf2aSRiver Riddle   return false;
906569cf2aSRiver Riddle }
916569cf2aSRiver Riddle 
9233a7ea4bSMehdi Amini // The destructor joins all threads, waiting for completion.
9333a7ea4bSMehdi Amini ThreadPool::~ThreadPool() {
9433a7ea4bSMehdi Amini   {
9533a7ea4bSMehdi Amini     std::unique_lock<std::mutex> LockGuard(QueueLock);
9633a7ea4bSMehdi Amini     EnableFlag = false;
9733a7ea4bSMehdi Amini   }
9833a7ea4bSMehdi Amini   QueueCondition.notify_all();
99e8469718SMehdi Amini   std::unique_lock<std::mutex> LockGuard(ThreadsLock);
10033a7ea4bSMehdi Amini   for (auto &Worker : Threads)
10133a7ea4bSMehdi Amini     Worker.join();
10233a7ea4bSMehdi Amini }
10333a7ea4bSMehdi Amini 
10433a7ea4bSMehdi Amini #else // LLVM_ENABLE_THREADS Disabled
10533a7ea4bSMehdi Amini 
10633a7ea4bSMehdi Amini // No threads are launched, issue a warning if ThreadCount is not 0
107b28f317cSMehdi Amini ThreadPool::ThreadPool(ThreadPoolStrategy S) : MaxThreadCount(1) {
108b28f317cSMehdi Amini   int ThreadCount = S.compute_thread_count();
1098404aeb5SAlexandre Ganea   if (ThreadCount != 1) {
11033a7ea4bSMehdi Amini     errs() << "Warning: request a ThreadPool with " << ThreadCount
11133a7ea4bSMehdi Amini            << " threads, but LLVM_ENABLE_THREADS has been turned off\n";
11233a7ea4bSMehdi Amini   }
11333a7ea4bSMehdi Amini }
11433a7ea4bSMehdi Amini 
11533a7ea4bSMehdi Amini void ThreadPool::wait() {
11633a7ea4bSMehdi Amini   // Sequential implementation running the tasks
11733a7ea4bSMehdi Amini   while (!Tasks.empty()) {
11833a7ea4bSMehdi Amini     auto Task = std::move(Tasks.front());
11933a7ea4bSMehdi Amini     Tasks.pop();
12033a7ea4bSMehdi Amini     Task();
12133a7ea4bSMehdi Amini   }
12233a7ea4bSMehdi Amini }
12333a7ea4bSMehdi Amini 
124d9547f41SJohn Demme bool ThreadPool::isWorkerThread() const {
125e5c944b4SFangrui Song   report_fatal_error("LLVM compiled without multithreading");
126d9547f41SJohn Demme }
127d9547f41SJohn Demme 
1288404aeb5SAlexandre Ganea ThreadPool::~ThreadPool() { wait(); }
12933a7ea4bSMehdi Amini 
13033a7ea4bSMehdi Amini #endif
131