diff options
Diffstat (limited to 'llvm/lib/Support/Parallel.cpp')
| -rw-r--r-- | llvm/lib/Support/Parallel.cpp | 82 | 
1 files changed, 64 insertions, 18 deletions
diff --git a/llvm/lib/Support/Parallel.cpp b/llvm/lib/Support/Parallel.cpp index 355c64b7d079..523665d14b02 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  | 
