blob: 621bccbf2a4c92fb0949218cff01bc01c8832c3e [file] [log] [blame]
Zachary Turner3a57fbd2017-05-11 00:03:52 +00001//===- llvm/Support/Parallel.cpp - Parallel algorithms --------------------===//
Zachary Turnerf7ca8fc2017-05-05 21:09:26 +00002//
Chandler Carruth2946cd72019-01-19 08:50:56 +00003// 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
Zachary Turnerf7ca8fc2017-05-05 21:09:26 +00006//
7//===----------------------------------------------------------------------===//
8
Zachary Turner3a57fbd2017-05-11 00:03:52 +00009#include "llvm/Support/Parallel.h"
Zachary Turner6083b2b2017-05-05 21:14:55 +000010#include "llvm/Config/llvm-config.h"
Nico Weber0f2a48c2018-05-11 15:25:38 +000011
12#if LLVM_ENABLE_THREADS
13
Rafael Espindola8c0ff952017-10-04 20:27:01 +000014#include "llvm/Support/Threading.h"
Zachary Turnerf7ca8fc2017-05-05 21:09:26 +000015
16#include <atomic>
17#include <stack>
Rui Ueyama552c2902017-05-05 21:27:30 +000018#include <thread>
Zachary Turnerf7ca8fc2017-05-05 21:09:26 +000019
Fangrui Songf6a62902019-04-25 11:33:30 +000020namespace llvm {
21namespace parallel {
22namespace detail {
Zachary Turnerf7ca8fc2017-05-05 21:09:26 +000023
24namespace {
25
Adrian Prantl5f8f34e42018-05-01 15:54:18 +000026/// An abstract class that takes closures and runs them asynchronously.
Zachary Turnerf7ca8fc2017-05-05 21:09:26 +000027class Executor {
28public:
29 virtual ~Executor() = default;
30 virtual void add(std::function<void()> func) = 0;
31
32 static Executor *getDefaultExecutor();
33};
34
Nico Weber0f2a48c2018-05-11 15:25:38 +000035#if defined(_MSC_VER)
Adrian Prantl5f8f34e42018-05-01 15:54:18 +000036/// An Executor that runs tasks via ConcRT.
Zachary Turnerf7ca8fc2017-05-05 21:09:26 +000037class ConcRTExecutor : public Executor {
38 struct Taskish {
39 Taskish(std::function<void()> Task) : Task(Task) {}
40
41 std::function<void()> Task;
42
43 static void run(void *P) {
44 Taskish *Self = static_cast<Taskish *>(P);
45 Self->Task();
46 concurrency::Free(Self);
47 }
48 };
49
50public:
51 virtual void add(std::function<void()> F) {
52 Concurrency::CurrentScheduler::ScheduleTask(
53 Taskish::run, new (concurrency::Alloc(sizeof(Taskish))) Taskish(F));
54 }
55};
56
57Executor *Executor::getDefaultExecutor() {
58 static ConcRTExecutor exec;
59 return &exec;
60}
61
62#else
Adrian Prantl5f8f34e42018-05-01 15:54:18 +000063/// An implementation of an Executor that runs closures on a thread pool
Zachary Turnerf7ca8fc2017-05-05 21:09:26 +000064/// in filo order.
65class ThreadPoolExecutor : public Executor {
66public:
Rafael Espindola8c0ff952017-10-04 20:27:01 +000067 explicit ThreadPoolExecutor(unsigned ThreadCount = hardware_concurrency())
Zachary Turnerf7ca8fc2017-05-05 21:09:26 +000068 : Done(ThreadCount) {
69 // Spawn all but one of the threads in another thread as spawning threads
70 // can take a while.
71 std::thread([&, ThreadCount] {
72 for (size_t i = 1; i < ThreadCount; ++i) {
73 std::thread([=] { work(); }).detach();
74 }
75 work();
76 }).detach();
77 }
78
79 ~ThreadPoolExecutor() override {
80 std::unique_lock<std::mutex> Lock(Mutex);
81 Stop = true;
82 Lock.unlock();
83 Cond.notify_all();
84 // Wait for ~Latch.
85 }
86
87 void add(std::function<void()> F) override {
88 std::unique_lock<std::mutex> Lock(Mutex);
89 WorkStack.push(F);
90 Lock.unlock();
91 Cond.notify_one();
92 }
93
94private:
95 void work() {
96 while (true) {
97 std::unique_lock<std::mutex> Lock(Mutex);
98 Cond.wait(Lock, [&] { return Stop || !WorkStack.empty(); });
99 if (Stop)
100 break;
101 auto Task = WorkStack.top();
102 WorkStack.pop();
103 Lock.unlock();
104 Task();
105 }
106 Done.dec();
107 }
108
109 std::atomic<bool> Stop{false};
110 std::stack<std::function<void()>> WorkStack;
111 std::mutex Mutex;
112 std::condition_variable Cond;
Zachary Turner7a2d5682017-05-11 00:22:18 +0000113 parallel::detail::Latch Done;
Zachary Turnerf7ca8fc2017-05-05 21:09:26 +0000114};
115
116Executor *Executor::getDefaultExecutor() {
117 static ThreadPoolExecutor exec;
118 return &exec;
119}
120#endif
121}
122
Fangrui Songf6a62902019-04-25 11:33:30 +0000123static std::atomic<int> TaskGroupInstances;
124
125// Latch::sync() called by the dtor may cause one thread to block. If is a dead
126// lock if all threads in the default executor are blocked. To prevent the dead
127// lock, only allow the first TaskGroup to run tasks parallelly. In the scenario
128// of nested parallel_for_each(), only the outermost one runs parallelly.
129TaskGroup::TaskGroup() : Parallel(TaskGroupInstances++ == 0) {}
130TaskGroup::~TaskGroup() { --TaskGroupInstances; }
131
132void TaskGroup::spawn(std::function<void()> F) {
133 if (Parallel) {
134 L.inc();
135 Executor::getDefaultExecutor()->add([&, F] {
136 F();
137 L.dec();
138 });
139 } else {
Zachary Turner0a340ce2017-05-10 17:39:18 +0000140 F();
Fangrui Songf6a62902019-04-25 11:33:30 +0000141 }
Zachary Turnerf7ca8fc2017-05-05 21:09:26 +0000142}
Fangrui Songf6a62902019-04-25 11:33:30 +0000143
144} // namespace detail
145} // namespace parallel
146} // namespace llvm
Nico Weber0f2a48c2018-05-11 15:25:38 +0000147#endif // LLVM_ENABLE_THREADS