From: Miss Islington (bot) <31488909+miss-islington@users.noreply.github.com> Date: Fri, 27 Oct 2023 23:36:05 +0000 (+0200) Subject: [3.12] gh-110205: Fix asyncio ThreadedChildWatcher._join_threads() (GH-110884) (... X-Git-Tag: v3.12.1~206 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=2398036eeafad68d7cc0829e89405cc1974dc012;p=thirdparty%2FPython%2Fcpython.git [3.12] gh-110205: Fix asyncio ThreadedChildWatcher._join_threads() (GH-110884) (#111412) - `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`. (cherry picked from commit c3bb10c9303503e7b55a7bdf9acfa6b3bcb699c6) Co-authored-by: Guido van Rossum --- diff --git a/Lib/asyncio/unix_events.py b/Lib/asyncio/unix_events.py index 0a618c42d2d2..f2e920ada46a 100644 --- a/Lib/asyncio/unix_events.py +++ b/Lib/asyncio/unix_events.py @@ -1367,14 +1367,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 @@ -1393,7 +1386,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 9e8fe2af96b6..ac8a8b449d3f 100644 --- a/Lib/test/test_asyncio/utils.py +++ b/Lib/test/test_asyncio/utils.py @@ -547,6 +547,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: @@ -558,9 +559,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: