LCOV - code coverage report
Current view: top level - lib/Support - ThreadPool.cpp (source / functions) Hit Total Coverage
Test: llvm-toolchain.info Lines: 40 40 100.0 %
Date: 2017-09-14 15:23:50 Functions: 7 7 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : //==-- llvm/Support/ThreadPool.cpp - A ThreadPool implementation -*- C++ -*-==//
       2             : //
       3             : //                     The LLVM Compiler Infrastructure
       4             : //
       5             : // This file is distributed under the University of Illinois Open Source
       6             : // License. See LICENSE.TXT for details.
       7             : //
       8             : //===----------------------------------------------------------------------===//
       9             : //
      10             : // This file implements a crude C++11 based thread pool.
      11             : //
      12             : //===----------------------------------------------------------------------===//
      13             : 
      14             : #include "llvm/Support/ThreadPool.h"
      15             : 
      16             : #include "llvm/Config/llvm-config.h"
      17             : #include "llvm/Support/raw_ostream.h"
      18             : 
      19             : using namespace llvm;
      20             : 
      21             : #if LLVM_ENABLE_THREADS
      22             : 
      23             : // Default to std::thread::hardware_concurrency
      24           9 : ThreadPool::ThreadPool() : ThreadPool(std::thread::hardware_concurrency()) {}
      25             : 
      26         137 : ThreadPool::ThreadPool(unsigned ThreadCount)
      27         822 :     : ActiveThreads(0), EnableFlag(true) {
      28             :   // Create ThreadCount threads that will loop forever, wait on QueueCondition
      29             :   // for tasks to be queued or the Pool to be destroyed.
      30         137 :   Threads.reserve(ThreadCount);
      31        1110 :   for (unsigned ThreadID = 0; ThreadID < ThreadCount; ++ThreadID) {
      32        1943 :     Threads.emplace_back([&] {
      33             :       while (true) {
      34        1504 :         PackagedTaskTy Task;
      35             :         {
      36        5594 :           std::unique_lock<std::mutex> LockGuard(QueueLock);
      37             :           // Wait for tasks to be pushed in the queue
      38        2480 :           QueueCondition.wait(LockGuard,
      39        2797 :                               [&] { return !EnableFlag || !Tasks.empty(); });
      40             :           // Exit condition
      41        2259 :           if (!EnableFlag && Tasks.empty())
      42         970 :             return;
      43             :           // Yeah, we have a task, grab it and release the lock on the queue
      44             : 
      45             :           // We first need to signal that we are active before popping the queue
      46             :           // in order for wait() to properly detect that even if the queue is
      47             :           // empty, there is still a task in flight.
      48             :           {
      49         538 :             ++ActiveThreads;
      50         807 :             std::unique_lock<std::mutex> LockGuard(CompletionLock);
      51             :           }
      52         807 :           Task = std::move(Tasks.front());
      53         538 :           Tasks.pop();
      54             :         }
      55             :         // Run the task we just grabbed
      56         269 :         Task();
      57             : 
      58             :         {
      59             :           // Adjust `ActiveThreads`, in case someone waits on ThreadPool::wait()
      60         801 :           std::unique_lock<std::mutex> LockGuard(CompletionLock);
      61         534 :           --ActiveThreads;
      62             :         }
      63             : 
      64             :         // Notify task completion, in case someone waits on ThreadPool::wait()
      65         267 :         CompletionCondition.notify_all();
      66             :       }
      67             :     });
      68             :   }
      69         137 : }
      70             : 
      71         117 : void ThreadPool::wait() {
      72             :   // Wait for all threads to complete and the queue to be empty
      73         351 :   std::unique_lock<std::mutex> LockGuard(CompletionLock);
      74             :   // The order of the checks for ActiveThreads and Tasks.empty() matters because
      75             :   // any active threads might be modifying the Tasks queue, and this would be a
      76             :   // race.
      77         234 :   CompletionCondition.wait(LockGuard,
      78         899 :                            [&] { return !ActiveThreads && Tasks.empty(); });
      79         117 : }
      80             : 
      81         269 : std::shared_future<void> ThreadPool::asyncImpl(TaskTy Task) {
      82             :   /// Wrap the Task in a packaged_task to return a future object.
      83         807 :   PackagedTaskTy PackagedTask(std::move(Task));
      84         538 :   auto Future = PackagedTask.get_future();
      85             :   {
      86             :     // Lock the queue and push the new task
      87         807 :     std::unique_lock<std::mutex> LockGuard(QueueLock);
      88             : 
      89             :     // Don't allow enqueueing after disabling the pool
      90             :     assert(EnableFlag && "Queuing a thread during ThreadPool destruction");
      91             : 
      92         538 :     Tasks.push(std::move(PackagedTask));
      93             :   }
      94         269 :   QueueCondition.notify_one();
      95         538 :   return Future.share();
      96             : }
      97             : 
      98             : // The destructor joins all threads, waiting for completion.
      99         407 : ThreadPool::~ThreadPool() {
     100             :   {
     101         411 :     std::unique_lock<std::mutex> LockGuard(QueueLock);
     102         137 :     EnableFlag = false;
     103             :   }
     104         137 :   QueueCondition.notify_all();
     105        1517 :   for (auto &Worker : Threads)
     106         971 :     Worker.join();
     107         135 : }
     108             : 
     109             : #else // LLVM_ENABLE_THREADS Disabled
     110             : 
     111             : ThreadPool::ThreadPool() : ThreadPool(0) {}
     112             : 
     113             : // No threads are launched, issue a warning if ThreadCount is not 0
     114             : ThreadPool::ThreadPool(unsigned ThreadCount)
     115             :     : ActiveThreads(0) {
     116             :   if (ThreadCount) {
     117             :     errs() << "Warning: request a ThreadPool with " << ThreadCount
     118             :            << " threads, but LLVM_ENABLE_THREADS has been turned off\n";
     119             :   }
     120             : }
     121             : 
     122             : void ThreadPool::wait() {
     123             :   // Sequential implementation running the tasks
     124             :   while (!Tasks.empty()) {
     125             :     auto Task = std::move(Tasks.front());
     126             :     Tasks.pop();
     127             :     Task();
     128             :   }
     129             : }
     130             : 
     131             : std::shared_future<void> ThreadPool::asyncImpl(TaskTy Task) {
     132             :   // Get a Future with launch::deferred execution using std::async
     133             :   auto Future = std::async(std::launch::deferred, std::move(Task)).share();
     134             :   // Wrap the future so that both ThreadPool::wait() can operate and the
     135             :   // returned future can be sync'ed on.
     136             :   PackagedTaskTy PackagedTask([Future]() { Future.get(); });
     137             :   Tasks.push(std::move(PackagedTask));
     138             :   return Future;
     139             : }
     140             : 
     141             : ThreadPool::~ThreadPool() {
     142             :   wait();
     143             : }
     144             : 
     145             : #endif

Generated by: LCOV version 1.13