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"
11*0b57cec5SDimitry Andric 
12*0b57cec5SDimitry Andric #if LLVM_ENABLE_THREADS
13*0b57cec5SDimitry Andric 
14*0b57cec5SDimitry Andric #include "llvm/Support/Threading.h"
15*0b57cec5SDimitry Andric 
16*0b57cec5SDimitry Andric #include <atomic>
17*0b57cec5SDimitry Andric #include <stack>
18*0b57cec5SDimitry Andric #include <thread>
19*0b57cec5SDimitry Andric 
20*0b57cec5SDimitry Andric namespace llvm {
21*0b57cec5SDimitry Andric namespace parallel {
22*0b57cec5SDimitry Andric namespace detail {
23*0b57cec5SDimitry Andric 
24*0b57cec5SDimitry Andric namespace {
25*0b57cec5SDimitry Andric 
26*0b57cec5SDimitry Andric /// An abstract class that takes closures and runs them asynchronously.
27*0b57cec5SDimitry Andric class Executor {
28*0b57cec5SDimitry Andric public:
29*0b57cec5SDimitry Andric   virtual ~Executor() = default;
30*0b57cec5SDimitry Andric   virtual void add(std::function<void()> func) = 0;
31*0b57cec5SDimitry Andric 
32*0b57cec5SDimitry Andric   static Executor *getDefaultExecutor();
33*0b57cec5SDimitry Andric };
34*0b57cec5SDimitry Andric 
35*0b57cec5SDimitry Andric /// An implementation of an Executor that runs closures on a thread pool
36*0b57cec5SDimitry Andric ///   in filo order.
37*0b57cec5SDimitry Andric class ThreadPoolExecutor : public Executor {
38*0b57cec5SDimitry Andric public:
39*0b57cec5SDimitry Andric   explicit ThreadPoolExecutor(unsigned ThreadCount = hardware_concurrency())
40*0b57cec5SDimitry Andric       : Done(ThreadCount) {
41*0b57cec5SDimitry Andric     // Spawn all but one of the threads in another thread as spawning threads
42*0b57cec5SDimitry Andric     // can take a while.
43*0b57cec5SDimitry Andric     std::thread([&, ThreadCount] {
44*0b57cec5SDimitry Andric       for (size_t i = 1; i < ThreadCount; ++i) {
45*0b57cec5SDimitry Andric         std::thread([=] { work(); }).detach();
46*0b57cec5SDimitry Andric       }
47*0b57cec5SDimitry Andric       work();
48*0b57cec5SDimitry Andric     }).detach();
49*0b57cec5SDimitry Andric   }
50*0b57cec5SDimitry Andric 
51*0b57cec5SDimitry Andric   ~ThreadPoolExecutor() override {
52*0b57cec5SDimitry Andric     std::unique_lock<std::mutex> Lock(Mutex);
53*0b57cec5SDimitry Andric     Stop = true;
54*0b57cec5SDimitry Andric     Lock.unlock();
55*0b57cec5SDimitry Andric     Cond.notify_all();
56*0b57cec5SDimitry Andric     // Wait for ~Latch.
57*0b57cec5SDimitry Andric   }
58*0b57cec5SDimitry Andric 
59*0b57cec5SDimitry Andric   void add(std::function<void()> F) override {
60*0b57cec5SDimitry Andric     std::unique_lock<std::mutex> Lock(Mutex);
61*0b57cec5SDimitry Andric     WorkStack.push(F);
62*0b57cec5SDimitry Andric     Lock.unlock();
63*0b57cec5SDimitry Andric     Cond.notify_one();
64*0b57cec5SDimitry Andric   }
65*0b57cec5SDimitry Andric 
66*0b57cec5SDimitry Andric private:
67*0b57cec5SDimitry Andric   void work() {
68*0b57cec5SDimitry Andric     while (true) {
69*0b57cec5SDimitry Andric       std::unique_lock<std::mutex> Lock(Mutex);
70*0b57cec5SDimitry Andric       Cond.wait(Lock, [&] { return Stop || !WorkStack.empty(); });
71*0b57cec5SDimitry Andric       if (Stop)
72*0b57cec5SDimitry Andric         break;
73*0b57cec5SDimitry Andric       auto Task = WorkStack.top();
74*0b57cec5SDimitry Andric       WorkStack.pop();
75*0b57cec5SDimitry Andric       Lock.unlock();
76*0b57cec5SDimitry Andric       Task();
77*0b57cec5SDimitry Andric     }
78*0b57cec5SDimitry Andric     Done.dec();
79*0b57cec5SDimitry Andric   }
80*0b57cec5SDimitry Andric 
81*0b57cec5SDimitry Andric   std::atomic<bool> Stop{false};
82*0b57cec5SDimitry Andric   std::stack<std::function<void()>> WorkStack;
83*0b57cec5SDimitry Andric   std::mutex Mutex;
84*0b57cec5SDimitry Andric   std::condition_variable Cond;
85*0b57cec5SDimitry Andric   parallel::detail::Latch Done;
86*0b57cec5SDimitry Andric };
87*0b57cec5SDimitry Andric 
88*0b57cec5SDimitry Andric Executor *Executor::getDefaultExecutor() {
89*0b57cec5SDimitry Andric   static ThreadPoolExecutor exec;
90*0b57cec5SDimitry Andric   return &exec;
91*0b57cec5SDimitry Andric }
928bcb0991SDimitry Andric } // namespace
93*0b57cec5SDimitry Andric 
94*0b57cec5SDimitry Andric static std::atomic<int> TaskGroupInstances;
95*0b57cec5SDimitry Andric 
96*0b57cec5SDimitry Andric // Latch::sync() called by the dtor may cause one thread to block. If is a dead
97*0b57cec5SDimitry Andric // lock if all threads in the default executor are blocked. To prevent the dead
98*0b57cec5SDimitry Andric // lock, only allow the first TaskGroup to run tasks parallelly. In the scenario
99*0b57cec5SDimitry Andric // of nested parallel_for_each(), only the outermost one runs parallelly.
100*0b57cec5SDimitry Andric TaskGroup::TaskGroup() : Parallel(TaskGroupInstances++ == 0) {}
101*0b57cec5SDimitry Andric TaskGroup::~TaskGroup() { --TaskGroupInstances; }
102*0b57cec5SDimitry Andric 
103*0b57cec5SDimitry Andric void TaskGroup::spawn(std::function<void()> F) {
104*0b57cec5SDimitry Andric   if (Parallel) {
105*0b57cec5SDimitry Andric     L.inc();
106*0b57cec5SDimitry Andric     Executor::getDefaultExecutor()->add([&, F] {
107*0b57cec5SDimitry Andric       F();
108*0b57cec5SDimitry Andric       L.dec();
109*0b57cec5SDimitry Andric     });
110*0b57cec5SDimitry Andric   } else {
111*0b57cec5SDimitry Andric     F();
112*0b57cec5SDimitry Andric   }
113*0b57cec5SDimitry Andric }
114*0b57cec5SDimitry Andric 
115*0b57cec5SDimitry Andric } // namespace detail
116*0b57cec5SDimitry Andric } // namespace parallel
117*0b57cec5SDimitry Andric } // namespace llvm
118*0b57cec5SDimitry Andric #endif // LLVM_ENABLE_THREADS
119