Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitefdaae5

Browse files
miss-islingtonChristianHrsambv
authored
[3.14]gh-86802: Fix asyncio memory leak; shielded task exceptions log once through the exception handler (gh-134331) (gh-134343)
(cherry picked from commitf695eca)Co-authored-by: Christian Harries <68507104+ChristianHrs@users.noreply.github.com>Co-authored-by: Łukasz Langa <lukasz@langa.pl>
1 parent6ce2045 commitefdaae5

File tree

3 files changed

+72
-7
lines changed

3 files changed

+72
-7
lines changed

‎Lib/asyncio/tasks.py

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -908,6 +908,25 @@ def _done_callback(fut, cur_task=cur_task):
908908
returnouter
909909

910910

911+
def_log_on_exception(fut):
912+
iffut.cancelled():
913+
return
914+
915+
exc=fut.exception()
916+
ifexcisNone:
917+
return
918+
919+
context= {
920+
'message':
921+
f'{exc.__class__.__name__} exception in shielded future',
922+
'exception':exc,
923+
'future':fut,
924+
}
925+
iffut._source_traceback:
926+
context['source_traceback']=fut._source_traceback
927+
fut._loop.call_exception_handler(context)
928+
929+
911930
defshield(arg):
912931
"""Wait for a future, shielding it from cancellation.
913932
@@ -953,14 +972,11 @@ def shield(arg):
953972
else:
954973
cur_task=None
955974

956-
def_inner_done_callback(inner,cur_task=cur_task):
957-
ifcur_taskisnotNone:
958-
futures.future_discard_from_awaited_by(inner,cur_task)
975+
def_clear_awaited_by_callback(inner):
976+
futures.future_discard_from_awaited_by(inner,cur_task)
959977

978+
def_inner_done_callback(inner):
960979
ifouter.cancelled():
961-
ifnotinner.cancelled():
962-
# Mark inner's result as retrieved.
963-
inner.exception()
964980
return
965981

966982
ifinner.cancelled():
@@ -972,10 +988,16 @@ def _inner_done_callback(inner, cur_task=cur_task):
972988
else:
973989
outer.set_result(inner.result())
974990

975-
976991
def_outer_done_callback(outer):
977992
ifnotinner.done():
978993
inner.remove_done_callback(_inner_done_callback)
994+
# Keep only one callback to log on cancel
995+
inner.remove_done_callback(_log_on_exception)
996+
inner.add_done_callback(_log_on_exception)
997+
998+
ifcur_taskisnotNone:
999+
inner.add_done_callback(_clear_awaited_by_callback)
1000+
9791001

9801002
inner.add_done_callback(_inner_done_callback)
9811003
outer.add_done_callback(_outer_done_callback)

‎Lib/test/test_asyncio/test_tasks.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2116,6 +2116,46 @@ def test_shield_cancel_outer(self):
21162116
self.assertTrue(outer.cancelled())
21172117
self.assertEqual(0,0ifouter._callbacksisNoneelselen(outer._callbacks))
21182118

2119+
deftest_shield_cancel_outer_result(self):
2120+
mock_handler=mock.Mock()
2121+
self.loop.set_exception_handler(mock_handler)
2122+
inner=self.new_future(self.loop)
2123+
outer=asyncio.shield(inner)
2124+
test_utils.run_briefly(self.loop)
2125+
outer.cancel()
2126+
test_utils.run_briefly(self.loop)
2127+
inner.set_result(1)
2128+
test_utils.run_briefly(self.loop)
2129+
mock_handler.assert_not_called()
2130+
2131+
deftest_shield_cancel_outer_exception(self):
2132+
mock_handler=mock.Mock()
2133+
self.loop.set_exception_handler(mock_handler)
2134+
inner=self.new_future(self.loop)
2135+
outer=asyncio.shield(inner)
2136+
test_utils.run_briefly(self.loop)
2137+
outer.cancel()
2138+
test_utils.run_briefly(self.loop)
2139+
inner.set_exception(Exception('foo'))
2140+
test_utils.run_briefly(self.loop)
2141+
mock_handler.assert_called_once()
2142+
2143+
deftest_shield_duplicate_log_once(self):
2144+
mock_handler=mock.Mock()
2145+
self.loop.set_exception_handler(mock_handler)
2146+
inner=self.new_future(self.loop)
2147+
outer=asyncio.shield(inner)
2148+
test_utils.run_briefly(self.loop)
2149+
outer.cancel()
2150+
test_utils.run_briefly(self.loop)
2151+
outer=asyncio.shield(inner)
2152+
test_utils.run_briefly(self.loop)
2153+
outer.cancel()
2154+
test_utils.run_briefly(self.loop)
2155+
inner.set_exception(Exception('foo'))
2156+
test_utils.run_briefly(self.loop)
2157+
mock_handler.assert_called_once()
2158+
21192159
deftest_shield_shortcut(self):
21202160
fut=self.new_future(self.loop)
21212161
fut.set_result(42)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fixed asyncio memory leak in cancelled shield tasks. For shielded tasks
2+
where the shield was cancelled, log potential exceptions through the
3+
exception handler. Contributed by Christian Harries.

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp