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> async(Function && F,Args &&...ArgList)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> async(Function && F)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