summaryrefslogtreecommitdiff
path: root/llvm/lib/Support/Parallel.cpp
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2020-01-17 20:45:01 +0000
committerDimitry Andric <dim@FreeBSD.org>2020-01-17 20:45:01 +0000
commit706b4fc47bbc608932d3b491ae19a3b9cde9497b (patch)
tree4adf86a776049cbf7f69a1929c4babcbbef925eb /llvm/lib/Support/Parallel.cpp
parent7cc9cf2bf09f069cb2dd947ead05d0b54301fb71 (diff)
Notes
Diffstat (limited to 'llvm/lib/Support/Parallel.cpp')
-rw-r--r--llvm/lib/Support/Parallel.cpp82
1 files changed, 64 insertions, 18 deletions
diff --git a/llvm/lib/Support/Parallel.cpp b/llvm/lib/Support/Parallel.cpp
index 355c64b7d0793..523665d14b029 100644
--- a/llvm/lib/Support/Parallel.cpp
+++ b/llvm/lib/Support/Parallel.cpp
@@ -8,14 +8,17 @@
#include "llvm/Support/Parallel.h"
#include "llvm/Config/llvm-config.h"
+#include "llvm/Support/ManagedStatic.h"
#if LLVM_ENABLE_THREADS
#include "llvm/Support/Threading.h"
#include <atomic>
+#include <future>
#include <stack>
#include <thread>
+#include <vector>
namespace llvm {
namespace parallel {
@@ -36,30 +39,53 @@ public:
/// in filo order.
class ThreadPoolExecutor : public Executor {
public:
- explicit ThreadPoolExecutor(unsigned ThreadCount = hardware_concurrency())
- : Done(ThreadCount) {
+ explicit ThreadPoolExecutor(unsigned ThreadCount = hardware_concurrency()) {
// 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();
+ Threads.reserve(ThreadCount);
+ Threads.resize(1);
+ std::lock_guard<std::mutex> Lock(Mutex);
+ Threads[0] = std::thread([&, ThreadCount] {
+ for (unsigned i = 1; i < ThreadCount; ++i) {
+ Threads.emplace_back([=] { work(); });
+ if (Stop)
+ break;
}
+ ThreadsCreated.set_value();
work();
- }).detach();
+ });
}
- ~ThreadPoolExecutor() override {
- std::unique_lock<std::mutex> Lock(Mutex);
- Stop = true;
- Lock.unlock();
+ void stop() {
+ {
+ std::lock_guard<std::mutex> Lock(Mutex);
+ if (Stop)
+ return;
+ Stop = true;
+ }
Cond.notify_all();
- // Wait for ~Latch.
+ ThreadsCreated.get_future().wait();
}
+ ~ThreadPoolExecutor() override {
+ stop();
+ std::thread::id CurrentThreadId = std::this_thread::get_id();
+ for (std::thread &T : Threads)
+ if (T.get_id() == CurrentThreadId)
+ T.detach();
+ else
+ T.join();
+ }
+
+ struct Deleter {
+ static void call(void *Ptr) { ((ThreadPoolExecutor *)Ptr)->stop(); }
+ };
+
void add(std::function<void()> F) override {
- std::unique_lock<std::mutex> Lock(Mutex);
- WorkStack.push(F);
- Lock.unlock();
+ {
+ std::lock_guard<std::mutex> Lock(Mutex);
+ WorkStack.push(F);
+ }
Cond.notify_one();
}
@@ -75,19 +101,39 @@ private:
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;
+ std::promise<void> ThreadsCreated;
+ std::vector<std::thread> Threads;
};
Executor *Executor::getDefaultExecutor() {
- static ThreadPoolExecutor exec;
- return &exec;
+ // The ManagedStatic enables the ThreadPoolExecutor to be stopped via
+ // llvm_shutdown() which allows a "clean" fast exit, e.g. via _exit(). This
+ // stops the thread pool and waits for any worker thread creation to complete
+ // but does not wait for the threads to finish. The wait for worker thread
+ // creation to complete is important as it prevents intermittent crashes on
+ // Windows due to a race condition between thread creation and process exit.
+ //
+ // The ThreadPoolExecutor will only be destroyed when the static unique_ptr to
+ // it is destroyed, i.e. in a normal full exit. The ThreadPoolExecutor
+ // destructor ensures it has been stopped and waits for worker threads to
+ // finish. The wait is important as it prevents intermittent crashes on
+ // Windows when the process is doing a full exit.
+ //
+ // The Windows crashes appear to only occur with the MSVC static runtimes and
+ // are more frequent with the debug static runtime.
+ //
+ // This also prevents intermittent deadlocks on exit with the MinGW runtime.
+ static ManagedStatic<ThreadPoolExecutor, object_creator<ThreadPoolExecutor>,
+ ThreadPoolExecutor::Deleter>
+ ManagedExec;
+ static std::unique_ptr<ThreadPoolExecutor> Exec(&(*ManagedExec));
+ return Exec.get();
}
} // namespace