diff --git a/tools/daemon_pool.py b/tools/daemon_pool.py index 2fb5a61d0a24d..6489e9c897fa7 100644 --- a/tools/daemon_pool.py +++ b/tools/daemon_pool.py @@ -38,8 +38,9 @@ class DaemonThreadPoolExecutor(ThreadPoolExecutor): """ThreadPoolExecutor variant whose workers do not block process exit.""" def _adjust_thread_count(self) -> None: - # Mirrors CPython's implementation (3.8–3.13) with two changes: + # Mirrors CPython's implementation (3.8–3.14) with two changes: # daemon=True and no _threads_queues registration. + # CPython 3.14 replaced _initializer/_initargs with WorkerContext. if self._idle_semaphore.acquire(timeout=0): return @@ -49,15 +50,28 @@ def weakref_cb(_, q=self._work_queue): num_threads = len(self._threads) if num_threads < self._max_workers: thread_name = "%s_%d" % (self._thread_name_prefix or self, num_threads) - t = threading.Thread( - name=thread_name, - target=_worker, - args=( + if hasattr(self, "_initializer"): + # CPython 3.8–3.13 + worker_args = ( weakref.ref(self, weakref_cb), self._work_queue, self._initializer, self._initargs, - ), + ) + else: + # CPython 3.14+: initializer/initargs folded into WorkerContext. + # The daemon pool never passes a custom initializer, so None/(). + from concurrent.futures.thread import WorkerContext + create_ctx, _resolve = WorkerContext.prepare(None, ()) + worker_args = ( + weakref.ref(self, weakref_cb), + create_ctx(), + self._work_queue, + ) + t = threading.Thread( + name=thread_name, + target=_worker, + args=worker_args, daemon=True, ) t.start()