mirror of
https://github.com/python/cpython.git
synced 2025-08-01 23:53:15 +00:00
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`.
This commit is contained in:
parent
1c9a0c4079
commit
c3bb10c930
2 changed files with 10 additions and 12 deletions
|
@ -1371,14 +1371,7 @@ class ThreadedChildWatcher(AbstractChildWatcher):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
self._join_threads()
|
pass
|
||||||
|
|
||||||
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()
|
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
return self
|
return self
|
||||||
|
@ -1397,7 +1390,7 @@ class ThreadedChildWatcher(AbstractChildWatcher):
|
||||||
def add_child_handler(self, pid, callback, *args):
|
def add_child_handler(self, pid, callback, *args):
|
||||||
loop = events.get_running_loop()
|
loop = events.get_running_loop()
|
||||||
thread = threading.Thread(target=self._do_waitpid,
|
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),
|
args=(loop, pid, callback, args),
|
||||||
daemon=True)
|
daemon=True)
|
||||||
self._threads[pid] = thread
|
self._threads[pid] = thread
|
||||||
|
|
|
@ -546,6 +546,7 @@ class TestCase(unittest.TestCase):
|
||||||
else:
|
else:
|
||||||
loop._default_executor.shutdown(wait=True)
|
loop._default_executor.shutdown(wait=True)
|
||||||
loop.close()
|
loop.close()
|
||||||
|
|
||||||
policy = support.maybe_get_event_loop_policy()
|
policy = support.maybe_get_event_loop_policy()
|
||||||
if policy is not None:
|
if policy is not None:
|
||||||
try:
|
try:
|
||||||
|
@ -557,9 +558,13 @@ class TestCase(unittest.TestCase):
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
if isinstance(watcher, asyncio.ThreadedChildWatcher):
|
if isinstance(watcher, asyncio.ThreadedChildWatcher):
|
||||||
threads = list(watcher._threads.values())
|
# Wait for subprocess to finish, but not forever
|
||||||
for thread in threads:
|
for thread in list(watcher._threads.values()):
|
||||||
thread.join()
|
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):
|
def set_event_loop(self, loop, *, cleanup=True):
|
||||||
if loop is None:
|
if loop is None:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue