]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-110205: Fix asyncio ThreadedChildWatcher._join_threads() (#110884)
authorGuido van Rossum <guido@python.org>
Fri, 27 Oct 2023 22:44:12 +0000 (15:44 -0700)
committerGitHub <noreply@github.com>
Fri, 27 Oct 2023 22:44:12 +0000 (15:44 -0700)
- `ThreadedChildWatcher.close()` is now *officially* a no-op; `_join_threads()` never did anything.
- Threads created by that class are now named `asyncio-waitpid-NNN`.
- `test.test_asyncio.utils.TestCase.close_loop()` now waits for the child watcher's threads, but not forever; if a thread hangs, it raises `RuntimeError`.

Lib/asyncio/unix_events.py
Lib/test/test_asyncio/utils.py

index 809f29eecba198294f9ef3bcbeec9a69e1d3fe59..c9441918ecfcfd43f6abcdfe800bc48f4462b116 100644 (file)
@@ -1371,14 +1371,7 @@ class ThreadedChildWatcher(AbstractChildWatcher):
         return True
 
     def close(self):
-        self._join_threads()
-
-    def _join_threads(self):
-        """Internal: Join all non-daemon threads"""
-        threads = [thread for thread in list(self._threads.values())
-                   if thread.is_alive() and not thread.daemon]
-        for thread in threads:
-            thread.join()
+        pass
 
     def __enter__(self):
         return self
@@ -1397,7 +1390,7 @@ class ThreadedChildWatcher(AbstractChildWatcher):
     def add_child_handler(self, pid, callback, *args):
         loop = events.get_running_loop()
         thread = threading.Thread(target=self._do_waitpid,
-                                  name=f"waitpid-{next(self._pid_counter)}",
+                                  name=f"asyncio-waitpid-{next(self._pid_counter)}",
                                   args=(loop, pid, callback, args),
                                   daemon=True)
         self._threads[pid] = thread
index 18869b3290a8aef517ba6b27d935db14aa4a6f0f..44943e1fa7bc4e0a2a9961360ffce48a31383fa2 100644 (file)
@@ -546,6 +546,7 @@ class TestCase(unittest.TestCase):
             else:
                 loop._default_executor.shutdown(wait=True)
         loop.close()
+
         policy = support.maybe_get_event_loop_policy()
         if policy is not None:
             try:
@@ -557,9 +558,13 @@ class TestCase(unittest.TestCase):
                 pass
             else:
                 if isinstance(watcher, asyncio.ThreadedChildWatcher):
-                    threads = list(watcher._threads.values())
-                    for thread in threads:
-                        thread.join()
+                    # Wait for subprocess to finish, but not forever
+                    for thread in list(watcher._threads.values()):
+                        thread.join(timeout=support.SHORT_TIMEOUT)
+                        if thread.is_alive():
+                            raise RuntimeError(f"thread {thread} still alive: "
+                                               "subprocess still running")
+
 
     def set_event_loop(self, loop, *, cleanup=True):
         if loop is None: