Skip to content

Commit 2b88fa1

Browse files
committed
pythonGH-117536: ensure asyncgens GCd before runner teardown get aclosed
1 parent 1d3225a commit 2b88fa1

File tree

1 file changed

+22
-5
lines changed

1 file changed

+22
-5
lines changed

Lib/asyncio/base_events.py

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,12 @@ def __init__(self):
444444
# A weak set of all asynchronous generators that are
445445
# being iterated by the loop.
446446
self._asyncgens = weakref.WeakSet()
447+
# A strong set of asynchronous generators that are being closed
448+
# by asyncgen_finalizer_hook
449+
self._closing_asyncgens = set()
450+
# A strong set of Handles for callbacks about to schedule async gen
451+
# aclose tasks
452+
self._asyncgens_aclose_handles = set()
447453
# Set to True when `loop.shutdown_asyncgens` is called.
448454
self._asyncgens_shutdown_called = False
449455
# Set to True when `loop.shutdown_default_executor` is called.
@@ -556,9 +562,15 @@ def _check_default_executor(self):
556562
raise RuntimeError('Executor shutdown has been called')
557563

558564
def _asyncgen_finalizer_hook(self, agen):
559-
self._asyncgens.discard(agen)
565+
self._closing_asyncgens.add(agen)
566+
567+
def do_aclose():
568+
self._closing_asyncgens.discard(agen)
569+
self._asyncgens_aclose_handles.discard(handle)
570+
self.create_task(agen.aclose())
571+
560572
if not self.is_closed():
561-
self.call_soon_threadsafe(self.create_task, agen.aclose())
573+
handle = self.call_soon_threadsafe(do_aclose)
562574

563575
def _asyncgen_firstiter_hook(self, agen):
564576
if self._asyncgens_shutdown_called:
@@ -573,13 +585,18 @@ async def shutdown_asyncgens(self):
573585
"""Shutdown all active asynchronous generators."""
574586
self._asyncgens_shutdown_called = True
575587

576-
if not len(self._asyncgens):
588+
closing_agens = list(self._asyncgens)
589+
closing_agens.extend(self._closing_asyncgens.copy())
590+
self._closing_asyncgens.clear()
591+
self._asyncgens.clear()
592+
593+
if not closing_agens:
577594
# If Python version is <3.6 or we don't have any asynchronous
578595
# generators alive.
579596
return
580597

581-
closing_agens = list(self._asyncgens)
582-
self._asyncgens.clear()
598+
for handle in self._asyncgens_aclose_handles.copy():
599+
handle.cancel()
583600

584601
results = await tasks.gather(
585602
*[ag.aclose() for ag in closing_agens],

0 commit comments

Comments
 (0)