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 #if defined(_MSC_VER)
36*0b57cec5SDimitry Andric /// An Executor that runs tasks via ConcRT.
37*0b57cec5SDimitry Andric class ConcRTExecutor : public Executor {
38*0b57cec5SDimitry Andric   struct Taskish {
39*0b57cec5SDimitry Andric     Taskish(std::function<void()> Task) : Task(Task) {}
40*0b57cec5SDimitry Andric 
41*0b57cec5SDimitry Andric     std::function<void()> Task;
42*0b57cec5SDimitry Andric 
43*0b57cec5SDimitry Andric     static void run(void *P) {
44*0b57cec5SDimitry Andric       Taskish *Self = static_cast<Taskish *>(P);
45*0b57cec5SDimitry Andric       Self->Task();
46*0b57cec5SDimitry Andric       concurrency::Free(Self);
47*0b57cec5SDimitry Andric     }
48*0b57cec5SDimitry Andric   };
49*0b57cec5SDimitry Andric 
50*0b57cec5SDimitry Andric public:
51*0b57cec5SDimitry Andric   virtual void add(std::function<void()> F) {
52*0b57cec5SDimitry Andric     Concurrency::CurrentScheduler::ScheduleTask(
53*0b57cec5SDimitry Andric         Taskish::run, new (concurrency::Alloc(sizeof(Taskish))) Taskish(F));
54*0b57cec5SDimitry Andric   }
55*0b57cec5SDimitry Andric };
56*0b57cec5SDimitry Andric 
57*0b57cec5SDimitry Andric Executor *Executor::getDefaultExecutor() {
58*0b57cec5SDimitry Andric   static ConcRTExecutor exec;
59*0b57cec5SDimitry Andric   return &exec;
60*0b57cec5SDimitry Andric }
61*0b57cec5SDimitry Andric 
62*0b57cec5SDimitry Andric #else
63*0b57cec5SDimitry Andric /// An implementation of an Executor that runs closures on a thread pool
64*0b57cec5SDimitry Andric ///   in filo order.
65*0b57cec5SDimitry Andric class ThreadPoolExecutor : public Executor {
66*0b57cec5SDimitry Andric public:
67*0b57cec5SDimitry Andric   explicit ThreadPoolExecutor(unsigned ThreadCount = hardware_concurrency())
68*0b57cec5SDimitry Andric       : Done(ThreadCount) {
69*0b57cec5SDimitry Andric     // Spawn all but one of the threads in another thread as spawning threads
70*0b57cec5SDimitry Andric     // can take a while.
71*0b57cec5SDimitry Andric     std::thread([&, ThreadCount] {
72*0b57cec5SDimitry Andric       for (size_t i = 1; i < ThreadCount; ++i) {
73*0b57cec5SDimitry Andric         std::thread([=] { work(); }).detach();
74*0b57cec5SDimitry Andric       }
75*0b57cec5SDimitry Andric       work();
76*0b57cec5SDimitry Andric     }).detach();
77*0b57cec5SDimitry Andric   }
78*0b57cec5SDimitry Andric 
79*0b57cec5SDimitry Andric   ~ThreadPoolExecutor() override {
80*0b57cec5SDimitry Andric     std::unique_lock<std::mutex> Lock(Mutex);
81*0b57cec5SDimitry Andric     Stop = true;
82*0b57cec5SDimitry Andric     Lock.unlock();
83*0b57cec5SDimitry Andric     Cond.notify_all();
84*0b57cec5SDimitry Andric     // Wait for ~Latch.
85*0b57cec5SDimitry Andric   }
86*0b57cec5SDimitry Andric 
87*0b57cec5SDimitry Andric   void add(std::function<void()> F) override {
88*0b57cec5SDimitry Andric     std::unique_lock<std::mutex> Lock(Mutex);
89*0b57cec5SDimitry Andric     WorkStack.push(F);
90*0b57cec5SDimitry Andric     Lock.unlock();
91*0b57cec5SDimitry Andric     Cond.notify_one();
92*0b57cec5SDimitry Andric   }
93*0b57cec5SDimitry Andric 
94*0b57cec5SDimitry Andric private:
95*0b57cec5SDimitry Andric   void work() {
96*0b57cec5SDimitry Andric     while (true) {
97*0b57cec5SDimitry Andric       std::unique_lock<std::mutex> Lock(Mutex);
98*0b57cec5SDimitry Andric       Cond.wait(Lock, [&] { return Stop || !WorkStack.empty(); });
99*0b57cec5SDimitry Andric       if (Stop)
100*0b57cec5SDimitry Andric         break;
101*0b57cec5SDimitry Andric       auto Task = WorkStack.top();
102*0b57cec5SDimitry Andric       WorkStack.pop();
103*0b57cec5SDimitry Andric       Lock.unlock();
104*0b57cec5SDimitry Andric       Task();
105*0b57cec5SDimitry Andric     }
106*0b57cec5SDimitry Andric     Done.dec();
107*0b57cec5SDimitry Andric   }
108*0b57cec5SDimitry Andric 
109*0b57cec5SDimitry Andric   std::atomic<bool> Stop{false};
110*0b57cec5SDimitry Andric   std::stack<std::function<void()>> WorkStack;
111*0b57cec5SDimitry Andric   std::mutex Mutex;
112*0b57cec5SDimitry Andric   std::condition_variable Cond;
113*0b57cec5SDimitry Andric   parallel::detail::Latch Done;
114*0b57cec5SDimitry Andric };
115*0b57cec5SDimitry Andric 
116*0b57cec5SDimitry Andric Executor *Executor::getDefaultExecutor() {
117*0b57cec5SDimitry Andric   static ThreadPoolExecutor exec;
118*0b57cec5SDimitry Andric   return &exec;
119*0b57cec5SDimitry Andric }
120*0b57cec5SDimitry Andric #endif
121*0b57cec5SDimitry Andric }
122*0b57cec5SDimitry Andric 
123*0b57cec5SDimitry Andric static std::atomic<int> TaskGroupInstances;
124*0b57cec5SDimitry Andric 
125*0b57cec5SDimitry Andric // Latch::sync() called by the dtor may cause one thread to block. If is a dead
126*0b57cec5SDimitry Andric // lock if all threads in the default executor are blocked. To prevent the dead
127*0b57cec5SDimitry Andric // lock, only allow the first TaskGroup to run tasks parallelly. In the scenario
128*0b57cec5SDimitry Andric // of nested parallel_for_each(), only the outermost one runs parallelly.
129*0b57cec5SDimitry Andric TaskGroup::TaskGroup() : Parallel(TaskGroupInstances++ == 0) {}
130*0b57cec5SDimitry Andric TaskGroup::~TaskGroup() { --TaskGroupInstances; }
131*0b57cec5SDimitry Andric 
132*0b57cec5SDimitry Andric void TaskGroup::spawn(std::function<void()> F) {
133*0b57cec5SDimitry Andric   if (Parallel) {
134*0b57cec5SDimitry Andric     L.inc();
135*0b57cec5SDimitry Andric     Executor::getDefaultExecutor()->add([&, F] {
136*0b57cec5SDimitry Andric       F();
137*0b57cec5SDimitry Andric       L.dec();
138*0b57cec5SDimitry Andric     });
139*0b57cec5SDimitry Andric   } else {
140*0b57cec5SDimitry Andric     F();
141*0b57cec5SDimitry Andric   }
142*0b57cec5SDimitry Andric }
143*0b57cec5SDimitry Andric 
144*0b57cec5SDimitry Andric } // namespace detail
145*0b57cec5SDimitry Andric } // namespace parallel
146*0b57cec5SDimitry Andric } // namespace llvm
147*0b57cec5SDimitry Andric #endif // LLVM_ENABLE_THREADS
148