Index: lld/include/lld/Core/Executor.h =================================================================== --- /dev/null +++ lld/include/lld/Core/Executor.h @@ -0,0 +1,79 @@ +//===- lld/Core/Executor.h - Thread Executor ------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_CORE_EXECUTOR_H +#define LLD_CORE_EXECUTOR_H + +#include +#include +#include +#include + +namespace lld { +/// \brief Allows one or more threads to wait on a potentially unknown number of +/// events. +/// +/// A latch starts at \p count. inc() increments this, and dec() decrements it. +/// All calls to sync() will block while the count is not 0. +/// +/// Calling dec() on a Latch with a count of 0 has undefined behaivor. +class Latch { + uint32_t _count; + mutable std::mutex _condMut; + mutable std::condition_variable _cond; + +public: + explicit Latch(uint32_t count = 0) : _count(count) {} + ~Latch() { sync(); } + + void inc() { + std::unique_lock lock(_condMut); + ++_count; + } + + void dec() { + std::unique_lock lock(_condMut); + if (--_count == 0) + _cond.notify_all(); + } + + void sync() const { + std::unique_lock lock(_condMut); + _cond.wait(lock, [&] { return _count == 0; }); + } +}; + +/// \brief An abstract class that takes closures and runs them asynchronously. +class Executor { +public: + virtual ~Executor() = default; + virtual void add(std::function func) = 0; + + static Executor *getDefaultExecutor(); +}; + +/// \brief Allows launching a number of tasks and waiting for them to finish +/// either explicitly via sync() or implicitly on destruction. +class TaskGroup { + Latch _latch; + +public: + void spawn(std::function f) { + _latch.inc(); + Executor::getDefaultExecutor()->add([&, f] { + f(); + _latch.dec(); + }); + } + + void sync() const { _latch.sync(); } +}; +} + +#endif Index: lld/include/lld/Core/Parallel.h =================================================================== --- lld/include/lld/Core/Parallel.h +++ lld/include/lld/Core/Parallel.h @@ -10,16 +10,11 @@ #ifndef LLD_CORE_PARALLEL_H #define LLD_CORE_PARALLEL_H -#include "lld/Core/Instrumentation.h" +#include "lld/Core/Executor.h" #include "lld/Core/LLVM.h" #include "llvm/Support/MathExtras.h" -#include "llvm/Support/thread.h" #include -#include -#include -#include -#include #if defined(_MSC_VER) && LLVM_ENABLE_THREADS #include @@ -27,172 +22,6 @@ #endif namespace lld { -/// \brief Allows one or more threads to wait on a potentially unknown number of -/// events. -/// -/// A latch starts at \p count. inc() increments this, and dec() decrements it. -/// All calls to sync() will block while the count is not 0. -/// -/// Calling dec() on a Latch with a count of 0 has undefined behaivor. -class Latch { - uint32_t _count; - mutable std::mutex _condMut; - mutable std::condition_variable _cond; - -public: - explicit Latch(uint32_t count = 0) : _count(count) {} - ~Latch() { sync(); } - - void inc() { - std::unique_lock lock(_condMut); - ++_count; - } - - void dec() { - std::unique_lock lock(_condMut); - if (--_count == 0) - _cond.notify_all(); - } - - void sync() const { - std::unique_lock lock(_condMut); - _cond.wait(lock, [&] { - return _count == 0; - }); - } -}; - -// Classes in this namespace are implementation details of this header. -namespace internal { - -/// \brief An abstract class that takes closures and runs them asynchronously. -class Executor { -public: - virtual ~Executor() = default; - virtual void add(std::function func) = 0; -}; - -#if !defined(LLVM_ENABLE_THREADS) || LLVM_ENABLE_THREADS == 0 -class SyncExecutor : public Executor { -public: - virtual void add(std::function func) { - func(); - } -}; - -inline Executor *getDefaultExecutor() { - static SyncExecutor exec; - return &exec; -} -#elif defined(_MSC_VER) -/// \brief An Executor that runs tasks via ConcRT. -class ConcRTExecutor : public Executor { - struct Taskish { - Taskish(std::function task) : _task(task) {} - - std::function _task; - - static void run(void *p) { - Taskish *self = static_cast(p); - self->_task(); - concurrency::Free(self); - } - }; - -public: - virtual void add(std::function func) { - Concurrency::CurrentScheduler::ScheduleTask(Taskish::run, - new (concurrency::Alloc(sizeof(Taskish))) Taskish(func)); - } -}; - -inline Executor *getDefaultExecutor() { - static ConcRTExecutor exec; - return &exec; -} -#else -/// \brief An implementation of an Executor that runs closures on a thread pool -/// in filo order. -class ThreadPoolExecutor : public Executor { -public: - explicit ThreadPoolExecutor(unsigned threadCount = - std::thread::hardware_concurrency()) - : _stop(false), _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 lock(_mutex); - _stop = true; - lock.unlock(); - _cond.notify_all(); - // Wait for ~Latch. - } - - void add(std::function f) override { - std::unique_lock lock(_mutex); - _workStack.push(f); - lock.unlock(); - _cond.notify_one(); - } - -private: - void work() { - while (true) { - std::unique_lock 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 _stop; - std::stack> _workStack; - std::mutex _mutex; - std::condition_variable _cond; - Latch _done; -}; - -inline Executor *getDefaultExecutor() { - static ThreadPoolExecutor exec; - return &exec; -} -#endif - -} // namespace internal - -/// \brief Allows launching a number of tasks and waiting for them to finish -/// either explicitly via sync() or implicitly on destruction. -class TaskGroup { - Latch _latch; - -public: - void spawn(std::function f) { - _latch.inc(); - internal::getDefaultExecutor()->add([&, f] { - f(); - _latch.dec(); - }); - } - - void sync() const { _latch.sync(); } -}; #if !defined(LLVM_ENABLE_THREADS) || LLVM_ENABLE_THREADS == 0 template Index: lld/lib/Core/CMakeLists.txt =================================================================== --- lld/lib/Core/CMakeLists.txt +++ lld/lib/Core/CMakeLists.txt @@ -5,6 +5,7 @@ add_lld_library(lldCore DefinedAtom.cpp Error.cpp + Executor.cpp File.cpp LinkingContext.cpp Reader.cpp Index: lld/lib/Core/Executor.cpp =================================================================== --- /dev/null +++ lld/lib/Core/Executor.cpp @@ -0,0 +1,117 @@ +//===- lld/Core/Executor.cpp - Thread Executor ----------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lld/Core/Executor.h" + +#if defined(_MSC_VER) && LLVM_ENABLE_THREADS +#include +#include +#endif + +using namespace lld; + +#if !defined(LLVM_ENABLE_THREADS) || LLVM_ENABLE_THREADS == 0 +namespace { +class SyncExecutor : public Executor { +public: + virtual void add(std::function func) { func(); } +}; +} + +Executor *Executor::getDefaultExecutor() { + static SyncExecutor exec; + return &exec; +} + +#elif defined(_MSC_VER) +namespace { +/// \brief An Executor that runs tasks via ConcRT. +class ConcRTExecutor : public Executor { + struct Taskish { + Taskish(std::function task) : _task(task) {} + + std::function _task; + + static void run(void *p) { + Taskish *self = static_cast(p); + self->_task(); + concurrency::Free(self); + } + }; + +public: + virtual void add(std::function func) { + Concurrency::CurrentScheduler::ScheduleTask( + Taskish::run, new (concurrency::Alloc(sizeof(Taskish))) Taskish(func)); + } +}; +} + +Executor *Executor::getDefaultExecutor() { + static ConcRTExecutor exec; + return &exec; +} + +#else +namespace { +/// \brief An implementation of an Executor that runs closures on a thread pool +/// in filo order. +class ThreadPoolExecutor : public Executor { +public: + explicit ThreadPoolExecutor( + unsigned threadCount = std::thread::hardware_concurrency()) + : _stop(false), _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 lock(_mutex); + _stop = true; + lock.unlock(); + _cond.notify_all(); + // Wait for ~Latch. + } + + void add(std::function f) override { + std::unique_lock lock(_mutex); + _workStack.push(f); + lock.unlock(); + _cond.notify_one(); + } + +private: + void work() { + while (true) { + std::unique_lock 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 _stop; + std::stack> _workStack; + std::mutex _mutex; + std::condition_variable _cond; + Latch _done; +}; +} +#endif