10000 gh-110205: Fix asyncio ThreadedChildWatcher._join_threads() · vstinner/cpython@33e6569 · GitHub
[go: up one dir, main page]

Skip to content

Commit 33e6569

Browse files
committed
pythongh-110205: Fix asyncio ThreadedChildWatcher._join_threads()
ThreadedChildWatcher._join_threads() now clears references to completed threads. test_asyncio.utils.TestCase now calls _join_threads() of the watcher, uses SHORT_TIMEOUT to join a thread, and then raises an exception if there are still running threads.
1 parent 2c472a8 commit 33e6569

File tree

2 files changed

+13
-6
lines changed

2 files changed

+13
-6
lines changed

Lib/asyncio/unix_events.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1373,12 +1373,16 @@ def is_active(self):
13731373
def close(self):
13741374
self._join_threads()
13751375

1376-
def _join_threads(self):
1376+
def _join_threads(self, timeout=None):
13771377
"""Internal: Join all non-daemon threads"""
13781378
threads = [thread for thread in list(self._threads.values())
13791379
if thread.is_alive() and not thread.daemon]
13801380
for thread in threads:
1381-
thread.join()
1381+
thread.join(timeout)
1382+
1383+
# Clear references to terminated threads
1384+
self.threads = [thread for thread in list(self._threads.values())
1385+
if thread.is_alive() and not thread.daemon]
13821386

13831387
def __enter__(self):
13841388
return self
@@ -1397,7 +1401,7 @@ def __del__(self, _warn=warnings.warn):
13971401
def add_child_handler(self, pid, callback, *args):
13981402
loop = events.get_running_loop()
13991403
thread = threading.Thread(target=self._do_waitpid,
1400-
name=f"waitpid-{next(self._pid_counter)}",
1404+
name=f"asyncio-waitpid-{next(self._pid_counter)}",
14011405
args=(loop, pid, callback, args),
14021406
daemon=True)
14031407
self._threads[pid] = thread

Lib/test/test_asyncio/utils.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -546,6 +546,7 @@ def close_loop(loop):
546546
else:
547547
loop._default_executor.shutdown(wait=True)
548548
loop.close()
549+
549550
policy = support.maybe_get_event_loop_policy()
550551
if policy is not None:
551552
try:
@@ -557,9 +558,11 @@ 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-
thread.join()
561+
watcher._join_threads(timeout=support.SHORT_TIMEOUT)
562+
threads = watcher._threads
563+
if threads:
564+
self.fail(f"watcher still has running threads: "
565+
f"{threads}")
563566

564567
def set_event_loop(self, loop, *, cleanup=True):
565568
if loop is None:

0 commit comments

Comments
 (0)
0