[llvm] [lit] Migrate lit to ProcessPoolExecutor (PR #202681)

Aiden Grossman via llvm-commits llvm-commits at lists.llvm.org
Wed Jun 10 06:23:31 PDT 2026


================
@@ -103,59 +113,62 @@ def _execute(self, deadline):
                 % (num_pools, self.workers, workers_per_pool_list)
             )
 
-        # Create multiple pools
-        pools = []
-        for pool_size in workers_per_pool_list:
-            pool = multiprocessing.Pool(
-                pool_size, lit.worker.initialize, (self.lit_config, semaphores)
+        executors = [
+            ProcessPoolExecutor(
+                max_workers=pool_size,
+                initializer=lit.worker.initialize,
+                initargs=(self.lit_config, semaphores),
             )
-            pools.append(pool)
-
-        # Distribute tests across pools
-        tests_per_pool = _ceilDiv(len(self.tests), num_pools)
-        async_results = []
-
-        for pool_idx, pool in enumerate(pools):
-            start_idx = pool_idx * tests_per_pool
-            end_idx = min(start_idx + tests_per_pool, len(self.tests))
-            for test in self.tests[start_idx:end_idx]:
-                ar = pool.apply_async(
-                    lit.worker.execute, args=[test], callback=self.progress_callback
-                )
-                async_results.append(ar)
+            for pool_size in workers_per_pool_list
+        ]
 
-        # Close all pools
-        for pool in pools:
-            pool.close()
+        future_to_test = {}
+        for i, test in enumerate(self.tests):
+            ex = executors[i % len(executors)]
+            future_to_test[ex.submit(lit.worker.execute, test)] = test
 
         try:
-            self._wait_for(async_results, deadline)
-        except:
-            # Terminate all pools on exception
-            for pool in pools:
-                pool.terminate()
-            raise
-        finally:
-            # Join all pools
-            for pool in pools:
-                pool.join()
-
-    def _wait_for(self, async_results, deadline):
-        timeout = deadline - time.time()
-        idx = 0
-        while len(async_results) > 0:
+            self._wait_for(future_to_test, deadline)
+        except BaseException:
             try:
-                ar = async_results.pop(0)
-                test = ar.get(timeout)
-            except multiprocessing.TimeoutError:
-                raise TimeoutError()
-            else:
-                self._update_test(self.tests[idx], test)
-                if test.isFailure():
+                tree_kill_ok, _ = lit.util.killProcessAndChildrenIsSupported()
+
+                # Prevent multiprocessing from joining queue feeder threads at exit
+                for ex in executors:
+                    if hasattr(ex, "_call_queue") and ex._call_queue is not None:
+                        ex._call_queue.cancel_join_thread()
+
+                for ex in executors:
+                    for pid, proc in list((ex._processes or {}).items()):
+                        if tree_kill_ok:
+                            lit.util.killProcessAndChildren(pid)
+                        else:
+                            proc.kill()
+
+                # Skip future.cancel() and ex.shutdown() to prevent
+                # ProcessPoolExecutor deadlocks and InvalidStateErrors on Python 3.8.
----------------
boomanaiden154 wrote:

Is there a python bug link or something that should go here?

Maybe also a TODO to remove the workaround if it's been fixed in more recent versions?

https://github.com/llvm/llvm-project/pull/202681


More information about the llvm-commits mailing list