Skip to content

Commit b35acc5

Browse files
MainRomiss-islington
authored andcommitted
bpo-35125: remove inner callback on outer cancellation in asyncio shield (GH-10340)
When the future returned by shield is cancelled, its completion callback of the inner future is not removed. This makes the callback list of inner inner future grow each time a shield is created and cancelled. This change unregisters the callback from the inner future when the outer future is cancelled. https://bugs.python.org/issue35125
1 parent f7bda5c commit b35acc5

File tree

3 files changed

+19
-3
lines changed

3 files changed

+19
-3
lines changed

Lib/asyncio/tasks.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -818,7 +818,7 @@ def shield(arg, *, loop=None):
818818
loop = futures._get_loop(inner)
819819
outer = loop.create_future()
820820

821-
def _done_callback(inner):
821+
def _inner_done_callback(inner):
822822
if outer.cancelled():
823823
if not inner.cancelled():
824824
# Mark inner's result as retrieved.
@@ -834,7 +834,13 @@ def _done_callback(inner):
834834
else:
835835
outer.set_result(inner.result())
836836

837-
inner.add_done_callback(_done_callback)
837+
838+
def _outer_done_callback(outer):
839+
if not inner.done():
840+
inner.remove_done_callback(_inner_done_callback)
841+
842+
inner.add_done_callback(_inner_done_callback)
843+
outer.add_done_callback(_outer_done_callback)
838844
return outer
839845

840846

Lib/test/test_asyncio/test_tasks.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1777,14 +1777,23 @@ def test_shield_exception(self):
17771777
test_utils.run_briefly(self.loop)
17781778
self.assertIs(outer.exception(), exc)
17791779

1780-
def test_shield_cancel(self):
1780+
def test_shield_cancel_inner(self):
17811781
inner = self.new_future(self.loop)
17821782
outer = asyncio.shield(inner)
17831783
test_utils.run_briefly(self.loop)
17841784
inner.cancel()
17851785
test_utils.run_briefly(self.loop)
17861786
self.assertTrue(outer.cancelled())
17871787

1788+
def test_shield_cancel_outer(self):
1789+
inner = self.new_future(self.loop)
1790+
outer = asyncio.shield(inner)
1791+
test_utils.run_briefly(self.loop)
1792+
outer.cancel()
1793+
test_utils.run_briefly(self.loop)
1794+
self.assertTrue(outer.cancelled())
1795+
self.assertEqual(0, 0 if outer._callbacks is None else len(outer._callbacks))
1796+
17881797
def test_shield_shortcut(self):
17891798
fut = self.new_future(self.loop)
17901799
fut.set_result(42)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Asyncio: Remove inner callback on outer cancellation in shield

0 commit comments

Comments
 (0)