[llvm] [Support] Join threads when stopping ThreadPoolExecutor (PR #166054)

Maurice Heumann via llvm-commits llvm-commits at lists.llvm.org
Sun Nov 2 03:10:19 PST 2025


https://github.com/momo5502 created https://github.com/llvm/llvm-project/pull/166054

When building LLVM as DLL, the `ThreadPoolExecutor` can cause deadlocks on Windows.

The threads wre previously only joined when the destructor of the `ThreadPoolExecutor` is called. This happens when unloading the DLL, when the global destructors are called.

On Windows, `std::thread`s use `FreeLibraryAndExitThread` to cleanup a thread. This requires access to the loader lock, when a thread terminates.
However, when destroying the pool, the loader lock is also held, as the DLL is being unloaded.
If the threads did not end fast enough, the destructor would wait for them to join. At the same time, the threads would wait for the destructor to release the loader lock.

Joining the threads when stopping the pool fixes that, as it ensures the threads are stopped when calling `llvm_shutdown`, outside the loader lock.


>From f1030d2093508b7da97ba6e051817e407c68d83a Mon Sep 17 00:00:00 2001
From: momo5502 <mauriceheumann at gmail.com>
Date: Sun, 2 Nov 2025 11:56:45 +0100
Subject: [PATCH] [Support] Join threads when stopping ThreadPoolExecutor

---
 llvm/lib/Support/Parallel.cpp | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/llvm/lib/Support/Parallel.cpp b/llvm/lib/Support/Parallel.cpp
index 8e0c724accb36..31b51536aaa9e 100644
--- a/llvm/lib/Support/Parallel.cpp
+++ b/llvm/lib/Support/Parallel.cpp
@@ -82,26 +82,28 @@ class ThreadPoolExecutor : public Executor {
   ThreadPoolExecutor() = delete;
 
   void stop() {
+    std::vector<std::thread> ThreadsToJoin;
+
     {
       std::lock_guard<std::mutex> Lock(Mutex);
-      if (Stop)
+      if (Stop && Threads.empty())
         return;
       Stop = true;
+      ThreadsToJoin.swap(Threads);
     }
     Cond.notify_all();
     ThreadsCreated.get_future().wait();
-  }
 
-  ~ThreadPoolExecutor() override {
-    stop();
     std::thread::id CurrentThreadId = std::this_thread::get_id();
-    for (std::thread &T : Threads)
+    for (std::thread &T : ThreadsToJoin)
       if (T.get_id() == CurrentThreadId)
         T.detach();
       else
         T.join();
   }
 
+  ~ThreadPoolExecutor() override { stop(); }
+
   struct Creator {
     static void *call() { return new ThreadPoolExecutor(strategy); }
   };



More information about the llvm-commits mailing list