Home | History | Annotate | Download | only in Support
      1 //===-- llvm/Support/ThreadPool.h - 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 defines a crude C++11 based thread pool.
     11 //
     12 //===----------------------------------------------------------------------===//
     13 
     14 #ifndef LLVM_SUPPORT_THREAD_POOL_H
     15 #define LLVM_SUPPORT_THREAD_POOL_H
     16 
     17 #include "llvm/Support/thread.h"
     18 
     19 #ifdef _MSC_VER
     20 // concrt.h depends on eh.h for __uncaught_exception declaration
     21 // even if we disable exceptions.
     22 #include <eh.h>
     23 
     24 // Disable warnings from ppltasks.h transitively included by <future>.
     25 #pragma warning(push)
     26 #pragma warning(disable:4530)
     27 #pragma warning(disable:4062)
     28 #endif
     29 
     30 #include <future>
     31 
     32 #ifdef _MSC_VER
     33 #pragma warning(pop)
     34 #endif
     35 
     36 #include <atomic>
     37 #include <condition_variable>
     38 #include <functional>
     39 #include <memory>
     40 #include <mutex>
     41 #include <queue>
     42 #include <utility>
     43 
     44 namespace llvm {
     45 
     46 /// A ThreadPool for asynchronous parallel execution on a defined number of
     47 /// threads.
     48 ///
     49 /// The pool keeps a vector of threads alive, waiting on a condition variable
     50 /// for some work to become available.
     51 class ThreadPool {
     52 public:
     53 #ifndef _MSC_VER
     54   using VoidTy = void;
     55   using TaskTy = std::function<void()>;
     56   using PackagedTaskTy = std::packaged_task<void()>;
     57 #else
     58   // MSVC 2013 has a bug and can't use std::packaged_task<void()>;
     59   // We force it to use bool(bool) instead.
     60   using VoidTy = bool;
     61   using TaskTy = std::function<bool(bool)>;
     62   using PackagedTaskTy = std::packaged_task<bool(bool)>;
     63 #endif
     64 
     65   /// Construct a pool with the number of core available on the system (or
     66   /// whatever the value returned by std::thread::hardware_concurrency() is).
     67   ThreadPool();
     68 
     69   /// Construct a pool of \p ThreadCount threads
     70   ThreadPool(unsigned ThreadCount);
     71 
     72   /// Blocking destructor: the pool will wait for all the threads to complete.
     73   ~ThreadPool();
     74 
     75   /// Asynchronous submission of a task to the pool. The returned future can be
     76   /// used to wait for the task to finish and is *non-blocking* on destruction.
     77   template <typename Function, typename... Args>
     78   inline std::shared_future<VoidTy> async(Function &&F, Args &&... ArgList) {
     79     auto Task =
     80         std::bind(std::forward<Function>(F), std::forward<Args>(ArgList)...);
     81 #ifndef _MSC_VER
     82     return asyncImpl(std::move(Task));
     83 #else
     84     // This lambda has to be marked mutable because MSVC 2013's std::bind call
     85     // operator isn't const qualified.
     86     return asyncImpl([Task](VoidTy) mutable -> VoidTy {
     87       Task();
     88       return VoidTy();
     89     });
     90 #endif
     91   }
     92 
     93   /// Asynchronous submission of a task to the pool. The returned future can be
     94   /// used to wait for the task to finish and is *non-blocking* on destruction.
     95   template <typename Function>
     96   inline std::shared_future<VoidTy> async(Function &&F) {
     97 #ifndef _MSC_VER
     98     return asyncImpl(std::forward<Function>(F));
     99 #else
    100     return asyncImpl([F] (VoidTy) -> VoidTy { F(); return VoidTy(); });
    101 #endif
    102   }
    103 
    104   /// Blocking wait for all the threads to complete and the queue to be empty.
    105   /// It is an error to try to add new tasks while blocking on this call.
    106   void wait();
    107 
    108 private:
    109   /// Asynchronous submission of a task to the pool. The returned future can be
    110   /// used to wait for the task to finish and is *non-blocking* on destruction.
    111   std::shared_future<VoidTy> asyncImpl(TaskTy F);
    112 
    113   /// Threads in flight
    114   std::vector<llvm::thread> Threads;
    115 
    116   /// Tasks waiting for execution in the pool.
    117   std::queue<PackagedTaskTy> Tasks;
    118 
    119   /// Locking and signaling for accessing the Tasks queue.
    120   std::mutex QueueLock;
    121   std::condition_variable QueueCondition;
    122 
    123   /// Locking and signaling for job completion
    124   std::mutex CompletionLock;
    125   std::condition_variable CompletionCondition;
    126 
    127   /// Keep track of the number of thread actually busy
    128   std::atomic<unsigned> ActiveThreads;
    129 
    130 #if LLVM_ENABLE_THREADS // avoids warning for unused variable
    131   /// Signal for the destruction of the pool, asking thread to exit.
    132   bool EnableFlag;
    133 #endif
    134 };
    135 }
    136 
    137 #endif // LLVM_SUPPORT_THREAD_POOL_H
    138