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 44namespace 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. 51class ThreadPool { 52public: 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 108private: 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