From: Guido van Rossum Date: Fri, 27 Oct 2023 22:44:12 +0000 (-0700) Subject: gh-110205: Fix asyncio ThreadedChildWatcher._join_threads() (#110884) X-Git-Tag: v3.13.0a2~299 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=c3bb10c9303503e7b55a7bdf9acfa6b3bcb699c6;p=thirdparty%2FPython%2Fcpython.git gh-110205: Fix asyncio ThreadedChildWatcher._join_threads() (#110884) - `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`. --- diff --git a/Lib/asyncio/unix_events.py b/Lib/asyncio/unix_events.py index 809f29eecba1..c9441918ecfc 100644 --- a/Lib/asyncio/unix_events.py +++ b/Lib/asyncio/unix_events.py @@ -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 diff --git a/Lib/test/test_asyncio/utils.py b/Lib/test/test_asyncio/utils.py index 18869b3290a8..44943e1fa7bc 100644 --- a/Lib/test/test_asyncio/utils.py +++ b/Lib/test/test_asyncio/utils.py @@ -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: