1 //===- llvm/Support/Parallel.cpp - Parallel algorithms --------------------===//
3 // 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
7 //===----------------------------------------------------------------------===//
9 #include "llvm/Support/Parallel.h"
10 #include "llvm/Config/llvm-config.h"
12 #if LLVM_ENABLE_THREADS
14 #include "llvm/Support/Threading.h"
26 /// An abstract class that takes closures and runs them asynchronously.
29 virtual ~Executor() = default;
30 virtual void add(std::function<void()> func) = 0;
32 static Executor *getDefaultExecutor();
36 /// An Executor that runs tasks via ConcRT.
37 class ConcRTExecutor : public Executor {
39 Taskish(std::function<void()> Task) : Task(Task) {}
41 std::function<void()> Task;
43 static void run(void *P) {
44 Taskish *Self = static_cast<Taskish *>(P);
46 concurrency::Free(Self);
51 virtual void add(std::function<void()> F) {
52 Concurrency::CurrentScheduler::ScheduleTask(
53 Taskish::run, new (concurrency::Alloc(sizeof(Taskish))) Taskish(F));
57 Executor *Executor::getDefaultExecutor() {
58 static ConcRTExecutor exec;
63 /// An implementation of an Executor that runs closures on a thread pool
65 class ThreadPoolExecutor : public Executor {
67 explicit ThreadPoolExecutor(unsigned ThreadCount = hardware_concurrency())
69 // Spawn all but one of the threads in another thread as spawning threads
71 std::thread([&, ThreadCount] {
72 for (size_t i = 1; i < ThreadCount; ++i) {
73 std::thread([=] { work(); }).detach();
79 ~ThreadPoolExecutor() override {
80 std::unique_lock<std::mutex> Lock(Mutex);
87 void add(std::function<void()> F) override {
88 std::unique_lock<std::mutex> Lock(Mutex);
97 std::unique_lock<std::mutex> Lock(Mutex);
98 Cond.wait(Lock, [&] { return Stop || !WorkStack.empty(); });
101 auto Task = WorkStack.top();
109 std::atomic<bool> Stop{false};
110 std::stack<std::function<void()>> WorkStack;
112 std::condition_variable Cond;
113 parallel::detail::Latch Done;
116 Executor *Executor::getDefaultExecutor() {
117 static ThreadPoolExecutor exec;
123 static std::atomic<int> TaskGroupInstances;
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.
129 TaskGroup::TaskGroup() : Parallel(TaskGroupInstances++ == 0) {}
130 TaskGroup::~TaskGroup() { --TaskGroupInstances; }
132 void TaskGroup::spawn(std::function<void()> F) {
135 Executor::getDefaultExecutor()->add([&, F] {
144 } // namespace detail
145 } // namespace parallel
147 #endif // LLVM_ENABLE_THREADS