| //===- llvm/Support/Parallel.cpp - Parallel algorithms --------------------===// | |
| // | |
| // The LLVM Compiler Infrastructure | |
| // | |
| // This file is distributed under the University of Illinois Open Source | |
| // License. See LICENSE.TXT for details. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "llvm/Support/Parallel.h" | |
| #include "llvm/Config/llvm-config.h" | |
| #if LLVM_ENABLE_THREADS | |
| #include "llvm/Support/Threading.h" | |
| #include <atomic> | |
| #include <stack> | |
| #include <thread> | |
| using namespace llvm; | |
| namespace { | |
| /// An abstract class that takes closures and runs them asynchronously. | |
| class Executor { | |
| public: | |
| virtual ~Executor() = default; | |
| virtual void add(std::function<void()> func) = 0; | |
| static Executor *getDefaultExecutor(); | |
| }; | |
| #if defined(_MSC_VER) | |
| /// An Executor that runs tasks via ConcRT. | |
| class ConcRTExecutor : public Executor { | |
| struct Taskish { | |
| Taskish(std::function<void()> Task) : Task(Task) {} | |
| std::function<void()> Task; | |
| static void run(void *P) { | |
| Taskish *Self = static_cast<Taskish *>(P); | |
| Self->Task(); | |
| concurrency::Free(Self); | |
| } | |
| }; | |
| public: | |
| virtual void add(std::function<void()> F) { | |
| Concurrency::CurrentScheduler::ScheduleTask( | |
| Taskish::run, new (concurrency::Alloc(sizeof(Taskish))) Taskish(F)); | |
| } | |
| }; | |
| Executor *Executor::getDefaultExecutor() { | |
| static ConcRTExecutor exec; | |
| return &exec; | |
| } | |
| #else | |
| /// An implementation of an Executor that runs closures on a thread pool | |
| /// in filo order. | |
| class ThreadPoolExecutor : public Executor { | |
| public: | |
| explicit ThreadPoolExecutor(unsigned ThreadCount = hardware_concurrency()) | |
| : Done(ThreadCount) { | |
| // Spawn all but one of the threads in another thread as spawning threads | |
| // can take a while. | |
| std::thread([&, ThreadCount] { | |
| for (size_t i = 1; i < ThreadCount; ++i) { | |
| std::thread([=] { work(); }).detach(); | |
| } | |
| work(); | |
| }).detach(); | |
| } | |
| ~ThreadPoolExecutor() override { | |
| std::unique_lock<std::mutex> Lock(Mutex); | |
| Stop = true; | |
| Lock.unlock(); | |
| Cond.notify_all(); | |
| // Wait for ~Latch. | |
| } | |
| void add(std::function<void()> F) override { | |
| std::unique_lock<std::mutex> Lock(Mutex); | |
| WorkStack.push(F); | |
| Lock.unlock(); | |
| Cond.notify_one(); | |
| } | |
| private: | |
| void work() { | |
| while (true) { | |
| std::unique_lock<std::mutex> Lock(Mutex); | |
| Cond.wait(Lock, [&] { return Stop || !WorkStack.empty(); }); | |
| if (Stop) | |
| break; | |
| auto Task = WorkStack.top(); | |
| WorkStack.pop(); | |
| Lock.unlock(); | |
| Task(); | |
| } | |
| Done.dec(); | |
| } | |
| std::atomic<bool> Stop{false}; | |
| std::stack<std::function<void()>> WorkStack; | |
| std::mutex Mutex; | |
| std::condition_variable Cond; | |
| parallel::detail::Latch Done; | |
| }; | |
| Executor *Executor::getDefaultExecutor() { | |
| static ThreadPoolExecutor exec; | |
| return &exec; | |
| } | |
| #endif | |
| } | |
| void parallel::detail::TaskGroup::spawn(std::function<void()> F) { | |
| L.inc(); | |
| Executor::getDefaultExecutor()->add([&, F] { | |
| F(); | |
| L.dec(); | |
| }); | |
| } | |
| #endif // LLVM_ENABLE_THREADS |