blob: 355c64b7d0793ad578a9b1921e45a1a56b19bf5c [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
Adrian Prantl5f8f34e42018-05-01 15:54:18 +000035/// An implementation of an Executor that runs closures on a thread pool
Zachary Turnerf7ca8fc2017-05-05 21:09:26 +000036/// in filo order.
37class ThreadPoolExecutor : public Executor {
38public:
Rafael Espindola8c0ff952017-10-04 20:27:01 +000039 explicit ThreadPoolExecutor(unsigned ThreadCount = hardware_concurrency())
Zachary Turnerf7ca8fc2017-05-05 21:09:26 +000040 : Done(ThreadCount) {
41 // Spawn all but one of the threads in another thread as spawning threads
42 // can take a while.
43 std::thread([&, ThreadCount] {
44 for (size_t i = 1; i < ThreadCount; ++i) {
45 std::thread([=] { work(); }).detach();
46 }
47 work();
48 }).detach();
49 }
50
51 ~ThreadPoolExecutor() override {
52 std::unique_lock<std::mutex> Lock(Mutex);
53 Stop = true;
54 Lock.unlock();
55 Cond.notify_all();
56 // Wait for ~Latch.
57 }
58
59 void add(std::function<void()> F) override {
60 std::unique_lock<std::mutex> Lock(Mutex);
61 WorkStack.push(F);
62 Lock.unlock();
63 Cond.notify_one();
64 }
65
66private:
67 void work() {
68 while (true) {
69 std::unique_lock<std::mutex> Lock(Mutex);
70 Cond.wait(Lock, [&] { return Stop || !WorkStack.empty(); });
71 if (Stop)
72 break;
73 auto Task = WorkStack.top();
74 WorkStack.pop();
75 Lock.unlock();
76 Task();
77 }
78 Done.dec();
79 }
80
81 std::atomic<bool> Stop{false};
82 std::stack<std::function<void()>> WorkStack;
83 std::mutex Mutex;
84 std::condition_variable Cond;
Zachary Turner7a2d5682017-05-11 00:22:18 +000085 parallel::detail::Latch Done;
Zachary Turnerf7ca8fc2017-05-05 21:09:26 +000086};
87
88Executor *Executor::getDefaultExecutor() {
89 static ThreadPoolExecutor exec;
90 return &exec;
91}
Nico Weberd4960032019-10-10 18:57:23 +000092} // namespace
Zachary Turnerf7ca8fc2017-05-05 21:09:26 +000093
Fangrui Songf6a62902019-04-25 11:33:30 +000094static std::atomic<int> TaskGroupInstances;
95
96// Latch::sync() called by the dtor may cause one thread to block. If is a dead
97// lock if all threads in the default executor are blocked. To prevent the dead
98// lock, only allow the first TaskGroup to run tasks parallelly. In the scenario
99// of nested parallel_for_each(), only the outermost one runs parallelly.
100TaskGroup::TaskGroup() : Parallel(TaskGroupInstances++ == 0) {}
101TaskGroup::~TaskGroup() { --TaskGroupInstances; }
102
103void TaskGroup::spawn(std::function<void()> F) {
104 if (Parallel) {
105 L.inc();
106 Executor::getDefaultExecutor()->add([&, F] {
107 F();
108 L.dec();
109 });
110 } else {
Zachary Turner0a340ce2017-05-10 17:39:18 +0000111 F();
Fangrui Songf6a62902019-04-25 11:33:30 +0000112 }
Zachary Turnerf7ca8fc2017-05-05 21:09:26 +0000113}
Fangrui Songf6a62902019-04-25 11:33:30 +0000114
115} // namespace detail
116} // namespace parallel
117} // namespace llvm
Nico Weber0f2a48c2018-05-11 15:25:38 +0000118#endif // LLVM_ENABLE_THREADS