1*0b57cec5SDimitry Andric //===- llvm/Support/Parallel.cpp - Parallel algorithms --------------------===//
2*0b57cec5SDimitry Andric //
3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*0b57cec5SDimitry Andric //
7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
8*0b57cec5SDimitry Andric 
9*0b57cec5SDimitry Andric #include "llvm/Support/Parallel.h"
10*0b57cec5SDimitry Andric #include "llvm/Config/llvm-config.h"
11480093f4SDimitry Andric #include "llvm/Support/ManagedStatic.h"
12*0b57cec5SDimitry Andric #include "llvm/Support/Threading.h"
13*0b57cec5SDimitry Andric 
14*0b57cec5SDimitry Andric #include <atomic>
15480093f4SDimitry Andric #include <future>
16*0b57cec5SDimitry Andric #include <stack>
17*0b57cec5SDimitry Andric #include <thread>
18480093f4SDimitry Andric #include <vector>
19*0b57cec5SDimitry Andric 
205ffd83dbSDimitry Andric llvm::ThreadPoolStrategy llvm::parallel::strategy;
215ffd83dbSDimitry Andric 
225ffd83dbSDimitry Andric #if LLVM_ENABLE_THREADS
235ffd83dbSDimitry Andric 
24*0b57cec5SDimitry Andric namespace llvm {
25*0b57cec5SDimitry Andric namespace parallel {
26*0b57cec5SDimitry Andric namespace detail {
27*0b57cec5SDimitry Andric 
28*0b57cec5SDimitry Andric namespace {
29*0b57cec5SDimitry Andric 
30*0b57cec5SDimitry Andric /// An abstract class that takes closures and runs them asynchronously.
31*0b57cec5SDimitry Andric class Executor {
32*0b57cec5SDimitry Andric public:
33*0b57cec5SDimitry Andric   virtual ~Executor() = default;
34*0b57cec5SDimitry Andric   virtual void add(std::function<void()> func) = 0;
35*0b57cec5SDimitry Andric 
36*0b57cec5SDimitry Andric   static Executor *getDefaultExecutor();
37*0b57cec5SDimitry Andric };
38*0b57cec5SDimitry Andric 
39*0b57cec5SDimitry Andric /// An implementation of an Executor that runs closures on a thread pool
40*0b57cec5SDimitry Andric ///   in filo order.
41*0b57cec5SDimitry Andric class ThreadPoolExecutor : public Executor {
42*0b57cec5SDimitry Andric public:
435ffd83dbSDimitry Andric   explicit ThreadPoolExecutor(ThreadPoolStrategy S = hardware_concurrency()) {
445ffd83dbSDimitry Andric     unsigned ThreadCount = S.compute_thread_count();
45*0b57cec5SDimitry Andric     // Spawn all but one of the threads in another thread as spawning threads
46*0b57cec5SDimitry Andric     // can take a while.
47480093f4SDimitry Andric     Threads.reserve(ThreadCount);
48480093f4SDimitry Andric     Threads.resize(1);
49480093f4SDimitry Andric     std::lock_guard<std::mutex> Lock(Mutex);
505ffd83dbSDimitry Andric     Threads[0] = std::thread([this, ThreadCount, S] {
515ffd83dbSDimitry Andric       for (unsigned I = 1; I < ThreadCount; ++I) {
525ffd83dbSDimitry Andric         Threads.emplace_back([=] { work(S, I); });
53480093f4SDimitry Andric         if (Stop)
54480093f4SDimitry Andric           break;
55*0b57cec5SDimitry Andric       }
56480093f4SDimitry Andric       ThreadsCreated.set_value();
575ffd83dbSDimitry Andric       work(S, 0);
58480093f4SDimitry Andric     });
59480093f4SDimitry Andric   }
60480093f4SDimitry Andric 
61480093f4SDimitry Andric   void stop() {
62480093f4SDimitry Andric     {
63480093f4SDimitry Andric       std::lock_guard<std::mutex> Lock(Mutex);
64480093f4SDimitry Andric       if (Stop)
65480093f4SDimitry Andric         return;
66480093f4SDimitry Andric       Stop = true;
67480093f4SDimitry Andric     }
68480093f4SDimitry Andric     Cond.notify_all();
69480093f4SDimitry Andric     ThreadsCreated.get_future().wait();
70*0b57cec5SDimitry Andric   }
71*0b57cec5SDimitry Andric 
72*0b57cec5SDimitry Andric   ~ThreadPoolExecutor() override {
73480093f4SDimitry Andric     stop();
74480093f4SDimitry Andric     std::thread::id CurrentThreadId = std::this_thread::get_id();
75480093f4SDimitry Andric     for (std::thread &T : Threads)
76480093f4SDimitry Andric       if (T.get_id() == CurrentThreadId)
77480093f4SDimitry Andric         T.detach();
78480093f4SDimitry Andric       else
79480093f4SDimitry Andric         T.join();
80*0b57cec5SDimitry Andric   }
81*0b57cec5SDimitry Andric 
825ffd83dbSDimitry Andric   struct Creator {
835ffd83dbSDimitry Andric     static void *call() { return new ThreadPoolExecutor(strategy); }
845ffd83dbSDimitry Andric   };
85480093f4SDimitry Andric   struct Deleter {
86480093f4SDimitry Andric     static void call(void *Ptr) { ((ThreadPoolExecutor *)Ptr)->stop(); }
87480093f4SDimitry Andric   };
88480093f4SDimitry Andric 
89*0b57cec5SDimitry Andric   void add(std::function<void()> F) override {
90480093f4SDimitry Andric     {
91480093f4SDimitry Andric       std::lock_guard<std::mutex> Lock(Mutex);
92*0b57cec5SDimitry Andric       WorkStack.push(F);
93480093f4SDimitry Andric     }
94*0b57cec5SDimitry Andric     Cond.notify_one();
95*0b57cec5SDimitry Andric   }
96*0b57cec5SDimitry Andric 
97*0b57cec5SDimitry Andric private:
985ffd83dbSDimitry Andric   void work(ThreadPoolStrategy S, unsigned ThreadID) {
995ffd83dbSDimitry Andric     S.apply_thread_strategy(ThreadID);
100*0b57cec5SDimitry Andric     while (true) {
101*0b57cec5SDimitry Andric       std::unique_lock<std::mutex> Lock(Mutex);
102*0b57cec5SDimitry Andric       Cond.wait(Lock, [&] { return Stop || !WorkStack.empty(); });
103*0b57cec5SDimitry Andric       if (Stop)
104*0b57cec5SDimitry Andric         break;
105*0b57cec5SDimitry Andric       auto Task = WorkStack.top();
106*0b57cec5SDimitry Andric       WorkStack.pop();
107*0b57cec5SDimitry Andric       Lock.unlock();
108*0b57cec5SDimitry Andric       Task();
109*0b57cec5SDimitry Andric     }
110*0b57cec5SDimitry Andric   }
111*0b57cec5SDimitry Andric 
112*0b57cec5SDimitry Andric   std::atomic<bool> Stop{false};
113*0b57cec5SDimitry Andric   std::stack<std::function<void()>> WorkStack;
114*0b57cec5SDimitry Andric   std::mutex Mutex;
115*0b57cec5SDimitry Andric   std::condition_variable Cond;
116480093f4SDimitry Andric   std::promise<void> ThreadsCreated;
117480093f4SDimitry Andric   std::vector<std::thread> Threads;
118*0b57cec5SDimitry Andric };
119*0b57cec5SDimitry Andric 
120*0b57cec5SDimitry Andric Executor *Executor::getDefaultExecutor() {
121480093f4SDimitry Andric   // The ManagedStatic enables the ThreadPoolExecutor to be stopped via
122480093f4SDimitry Andric   // llvm_shutdown() which allows a "clean" fast exit, e.g. via _exit(). This
123480093f4SDimitry Andric   // stops the thread pool and waits for any worker thread creation to complete
124480093f4SDimitry Andric   // but does not wait for the threads to finish. The wait for worker thread
125480093f4SDimitry Andric   // creation to complete is important as it prevents intermittent crashes on
126480093f4SDimitry Andric   // Windows due to a race condition between thread creation and process exit.
127480093f4SDimitry Andric   //
128480093f4SDimitry Andric   // The ThreadPoolExecutor will only be destroyed when the static unique_ptr to
129480093f4SDimitry Andric   // it is destroyed, i.e. in a normal full exit. The ThreadPoolExecutor
130480093f4SDimitry Andric   // destructor ensures it has been stopped and waits for worker threads to
131480093f4SDimitry Andric   // finish. The wait is important as it prevents intermittent crashes on
132480093f4SDimitry Andric   // Windows when the process is doing a full exit.
133480093f4SDimitry Andric   //
134480093f4SDimitry Andric   // The Windows crashes appear to only occur with the MSVC static runtimes and
135480093f4SDimitry Andric   // are more frequent with the debug static runtime.
136480093f4SDimitry Andric   //
137480093f4SDimitry Andric   // This also prevents intermittent deadlocks on exit with the MinGW runtime.
1385ffd83dbSDimitry Andric 
1395ffd83dbSDimitry Andric   static ManagedStatic<ThreadPoolExecutor, ThreadPoolExecutor::Creator,
140480093f4SDimitry Andric                        ThreadPoolExecutor::Deleter>
141480093f4SDimitry Andric       ManagedExec;
142480093f4SDimitry Andric   static std::unique_ptr<ThreadPoolExecutor> Exec(&(*ManagedExec));
143480093f4SDimitry Andric   return Exec.get();
144*0b57cec5SDimitry Andric }
1458bcb0991SDimitry Andric } // namespace
146*0b57cec5SDimitry Andric 
147*0b57cec5SDimitry Andric static std::atomic<int> TaskGroupInstances;
148*0b57cec5SDimitry Andric 
149*0b57cec5SDimitry Andric // Latch::sync() called by the dtor may cause one thread to block. If is a dead
150*0b57cec5SDimitry Andric // lock if all threads in the default executor are blocked. To prevent the dead
151*0b57cec5SDimitry Andric // lock, only allow the first TaskGroup to run tasks parallelly. In the scenario
152*0b57cec5SDimitry Andric // of nested parallel_for_each(), only the outermost one runs parallelly.
153*0b57cec5SDimitry Andric TaskGroup::TaskGroup() : Parallel(TaskGroupInstances++ == 0) {}
154349cc55cSDimitry Andric TaskGroup::~TaskGroup() {
155349cc55cSDimitry Andric   // We must ensure that all the workloads have finished before decrementing the
156349cc55cSDimitry Andric   // instances count.
157349cc55cSDimitry Andric   L.sync();
158349cc55cSDimitry Andric   --TaskGroupInstances;
159349cc55cSDimitry Andric }
160*0b57cec5SDimitry Andric 
161*0b57cec5SDimitry Andric void TaskGroup::spawn(std::function<void()> F) {
162*0b57cec5SDimitry Andric   if (Parallel) {
163*0b57cec5SDimitry Andric     L.inc();
164*0b57cec5SDimitry Andric     Executor::getDefaultExecutor()->add([&, F] {
165*0b57cec5SDimitry Andric       F();
166*0b57cec5SDimitry Andric       L.dec();
167*0b57cec5SDimitry Andric     });
168*0b57cec5SDimitry Andric   } else {
169*0b57cec5SDimitry Andric     F();
170*0b57cec5SDimitry Andric   }
171*0b57cec5SDimitry Andric }
172*0b57cec5SDimitry Andric 
173*0b57cec5SDimitry Andric } // namespace detail
174*0b57cec5SDimitry Andric } // namespace parallel
175*0b57cec5SDimitry Andric } // namespace llvm
176*0b57cec5SDimitry Andric #endif // LLVM_ENABLE_THREADS
177