Index: include/llvm/Support/Parallel.h =================================================================== --- include/llvm/Support/Parallel.h +++ include/llvm/Support/Parallel.h @@ -19,7 +19,7 @@ #include #include -#if defined(_MSC_VER) && LLVM_ENABLE_THREADS +#if defined(_MSC_VER) && LLVM_ENABLE_THREADS && 0 // Disable use of MS ConcRT. #pragma warning(push) #pragma warning(disable : 4530) #include @@ -81,7 +81,7 @@ void sync() const { L.sync(); } }; -#if defined(_MSC_VER) +#if defined(_MSC_VER) && 0 // Disable use of MS ConcRT. template void parallel_sort(RandomAccessIterator Start, RandomAccessIterator End, const Comparator &Comp) { Index: lib/Support/Parallel.cpp =================================================================== --- lib/Support/Parallel.cpp +++ lib/Support/Parallel.cpp @@ -7,6 +7,7 @@ // //===----------------------------------------------------------------------===// +#include "llvm/Support/ManagedStatic.h" #include "llvm/Support/Parallel.h" #include "llvm/Config/llvm-config.h" #include "llvm/Support/Threading.h" @@ -14,6 +15,7 @@ #include #include #include +#include using namespace llvm; @@ -39,7 +41,7 @@ return &Exec; } -#elif defined(_MSC_VER) +#elif defined(_MSC_VER) && 0 // Disable use of MS ConcRT. /// \brief An Executor that runs tasks via ConcRT. class ConcRTExecutor : public Executor { struct Taskish { @@ -71,26 +73,65 @@ /// 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); + Threads[0] = std::thread([&, ThreadCount] { + for (unsigned i = 1; i < ThreadCount; ++i) { + Threads.emplace_back([=] { work(); }); + if (Stop) + break; } + + std::unique_lock Lock(Mutex); + Started = true; + Lock.unlock(); + CondStarted.notify_one(); + work(); - }).detach(); + }); } - ~ThreadPoolExecutor() override { + void shutdown() { std::unique_lock Lock(Mutex); + if (Stop) + return; + Stop = true; Lock.unlock(); Cond.notify_all(); - // Wait for ~Latch. + + Lock.lock(); + if (!Started) + CondStarted.wait(Lock); + } + + ~ThreadPoolExecutor() override { + shutdown(); + + std::thread::id ThreadId = std::this_thread::get_id(); + for (std::thread &T : Threads) { + if (T.get_id() == ThreadId) + T.detach(); + else + T.join(); + } } + struct Creator { + static void *call() { + return new ThreadPoolExecutor(); + } + }; + + struct Deleter { + static void call(void *Ptr) { + ((ThreadPoolExecutor *) Ptr)->shutdown(); + } + }; + void add(std::function F) override { std::unique_lock Lock(Mutex); WorkStack.push(F); @@ -110,19 +151,39 @@ Lock.unlock(); Task(); } - Done.dec(); } std::atomic Stop{false}; + std::atomic Started{false}; std::stack> WorkStack; std::mutex Mutex; std::condition_variable Cond; - parallel::detail::Latch Done; + std::condition_variable CondStarted; + std::vector Threads; }; Executor *Executor::getDefaultExecutor() { - static ThreadPoolExecutor exec; - return &exec; + // The ManagedStatic enables the ThreadPoolExecutor to be shutdown via + // llvm_shutdown() which allows a "clean" fast exit, e.g. via _exit(). The + // shutdown tells the thread pool to stop and waits for any worker thread + // creation to complete. However, it 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 itself will only be destroyed when the static + // unique_ptr to it is destroyed, i.e. in a normal full exit. The + // ThreadPoolExecutor destructor ensures shutdown and also 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 are particularly noticeable when using the static debug + // CRT. + static ManagedStatic + ManagedExec; + static std::unique_ptr Exec(&(*ManagedExec)); + return Exec.get(); } #endif }