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