8000 [3.11] gh-110205: Fix asyncio ThreadedChildWatcher._join_threads() (G… · python/cpython@1a01ca4 · GitHub
[go: up one dir, main page]

Skip to content

Commit 1a01ca4

Browse files
[3.11] gh-110205: Fix asyncio ThreadedChildWatcher._join_threads() (GH-110884) (#111413)
- `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 c3bb10c) Co-authored-by: Guido van Rossum <guido@python.org>
1 parent e84b06c commit 1a01ca4

File tree

2 files changed

+10
-12
lines changed

2 files changed

+10
-12
lines changed

Lib/asyncio/unix_events.py

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1363,14 +1363,7 @@ def is_active(self):
13631363
return True
13641364

13651365
def close(self):
1366-
self._join_threads()
1367-
1368-
def _join_threads(self):
1369-
"""Internal: Join all non-daemon threads"""
1370-
threads = [thread for thread in list(self._threads.values())
1371-
if thread.is_alive() and not thread.daemon]
1372-
for thread in threads:
1373-
thread.join()
1366+
pass
13741367

13751368
def __enter__(self):
13761369
return self
@@ -1389,7 +1382,7 @@ def __del__(self, _warn=warnings.warn):
13891382
def add_child_handler(self, pid, callback, *args):
13901383
loop = events.get_running_loop()
13911384
thread = threading.Thread(target=self._do_waitpid,
1392-
name=f"waitpid-{next(self._pid_counter)}",
1385+
name=f"asyncio-waitpid-{next(self._pid_counter)}",
13931386
args=(loop, pid, callback, args),
13941387
daemon=True)
13951388
self._threads[pid] = thread

Lib/test/test_asyncio/utils.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,7 @@ def close_loop(loop):
548548
else:
549549
loop._default_executor.shutdown(wait=True)
550550
loop.close()
551+
551552
policy = support.maybe_get_event_loop_policy()
552553
if policy is not None:
553554
try:
@@ -557,9 +558,13 @@ def close_loop(loop):
557558
pass
558559
else:
559560
if isinstance(watcher, asyncio.ThreadedChildWatcher):
560-
threads = list(watcher._threads.values())
561-
for thread in threads:
562-
8072 thread.join()
561+
# Wait for subprocess to finish, but not forever
562+
for thread in list(watcher._threads.values()):
563+
thread.join(timeout=support.SHORT_TIMEOUT)
564+
if thread.is_alive():
565+
raise RuntimeError(f"thread {thread} still alive: "
566+
"subprocess still running")
567+
563568

564569
def set_event_loop(self, loop, *, cleanup=True):
565570
if loop is None:

0 commit comments

Comments
 (0)
0