1 //===- llvm/Support/Parallel.cpp - Parallel algorithms --------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "llvm/Support/Parallel.h"
10 #include "llvm/Config/llvm-config.h"
11 #include "llvm/Support/ManagedStatic.h"
13 #if LLVM_ENABLE_THREADS
15 #include "llvm/Support/Threading.h"
29 /// An abstract class that takes closures and runs them asynchronously.
32 virtual ~Executor() = default;
33 virtual void add(std::function<void()> func) = 0;
35 static Executor *getDefaultExecutor();
38 /// An implementation of an Executor that runs closures on a thread pool
40 class ThreadPoolExecutor : public Executor {
42 explicit ThreadPoolExecutor(unsigned ThreadCount = hardware_concurrency()) {
43 // Spawn all but one of the threads in another thread as spawning threads
45 Threads.reserve(ThreadCount);
47 std::lock_guard<std::mutex> Lock(Mutex);
48 Threads[0] = std::thread([&, ThreadCount] {
49 for (unsigned i = 1; i < ThreadCount; ++i) {
50 Threads.emplace_back([=] { work(); });
54 ThreadsCreated.set_value();
61 std::lock_guard<std::mutex> Lock(Mutex);
67 ThreadsCreated.get_future().wait();
70 ~ThreadPoolExecutor() override {
72 std::thread::id CurrentThreadId = std::this_thread::get_id();
73 for (std::thread &T : Threads)
74 if (T.get_id() == CurrentThreadId)
81 static void call(void *Ptr) { ((ThreadPoolExecutor *)Ptr)->stop(); }
84 void add(std::function<void()> F) override {
86 std::lock_guard<std::mutex> Lock(Mutex);
95 std::unique_lock<std::mutex> Lock(Mutex);
96 Cond.wait(Lock, [&] { return Stop || !WorkStack.empty(); });
99 auto Task = WorkStack.top();
106 std::atomic<bool> Stop{false};
107 std::stack<std::function<void()>> WorkStack;
109 std::condition_variable Cond;
110 std::promise<void> ThreadsCreated;
111 std::vector<std::thread> Threads;
114 Executor *Executor::getDefaultExecutor() {
115 // The ManagedStatic enables the ThreadPoolExecutor to be stopped via
116 // llvm_shutdown() which allows a "clean" fast exit, e.g. via _exit(). This
117 // stops the thread pool and waits for any worker thread creation to complete
118 // but does not wait for the threads to finish. The wait for worker thread
119 // creation to complete is important as it prevents intermittent crashes on
120 // Windows due to a race condition between thread creation and process exit.
122 // The ThreadPoolExecutor will only be destroyed when the static unique_ptr to
123 // it is destroyed, i.e. in a normal full exit. The ThreadPoolExecutor
124 // destructor ensures it has been stopped and waits for worker threads to
125 // finish. The wait is important as it prevents intermittent crashes on
126 // Windows when the process is doing a full exit.
128 // The Windows crashes appear to only occur with the MSVC static runtimes and
129 // are more frequent with the debug static runtime.
131 // This also prevents intermittent deadlocks on exit with the MinGW runtime.
132 static ManagedStatic<ThreadPoolExecutor, object_creator<ThreadPoolExecutor>,
133 ThreadPoolExecutor::Deleter>
135 static std::unique_ptr<ThreadPoolExecutor> Exec(&(*ManagedExec));
140 static std::atomic<int> TaskGroupInstances;
142 // Latch::sync() called by the dtor may cause one thread to block. If is a dead
143 // lock if all threads in the default executor are blocked. To prevent the dead
144 // lock, only allow the first TaskGroup to run tasks parallelly. In the scenario
145 // of nested parallel_for_each(), only the outermost one runs parallelly.
146 TaskGroup::TaskGroup() : Parallel(TaskGroupInstances++ == 0) {}
147 TaskGroup::~TaskGroup() { --TaskGroupInstances; }
149 void TaskGroup::spawn(std::function<void()> F) {
152 Executor::getDefaultExecutor()->add([&, F] {
161 } // namespace detail
162 } // namespace parallel
164 #endif // LLVM_ENABLE_THREADS