Skip to content

Commit 42d2bed

Browse files
gh-142556: fix crash when a task gets re-registered during finalization in asyncio (#142565)
1 parent da8199f commit 42d2bed

File tree

3 files changed

+29
-9
lines changed

3 files changed

+29
-9
lines changed

Lib/test/test_asyncio/test_tasks.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3045,6 +3045,26 @@ class BaseTaskIntrospectionTests:
30453045
_enter_task = None
30463046
_leave_task = None
30473047
all_tasks = None
3048+
Task = None
3049+
3050+
def test_register_task_resurrection(self):
3051+
register_task = self._register_task
3052+
class EvilLoop:
3053+
def get_debug(self):
3054+
return False
3055+
3056+
def call_exception_handler(self, context):
3057+
register_task(context["task"])
3058+
3059+
async def coro_fn ():
3060+
pass
3061+
3062+
coro = coro_fn()
3063+
self.addCleanup(coro.close)
3064+
loop = EvilLoop()
3065+
with self.assertRaises(AttributeError):
3066+
self.Task(coro, loop=loop)
3067+
30483068

30493069
def test__register_task_1(self):
30503070
class TaskLike:
@@ -3175,6 +3195,7 @@ class PyIntrospectionTests(test_utils.TestCase, BaseTaskIntrospectionTests):
31753195
_leave_task = staticmethod(tasks._py_leave_task)
31763196
all_tasks = staticmethod(tasks._py_all_tasks)
31773197
current_task = staticmethod(tasks._py_current_task)
3198+
Task = tasks._PyTask
31783199

31793200

31803201
@unittest.skipUnless(hasattr(tasks, '_c_register_task'),
@@ -3187,10 +3208,12 @@ class CIntrospectionTests(test_utils.TestCase, BaseTaskIntrospectionTests):
31873208
_leave_task = staticmethod(tasks._c_leave_task)
31883209
all_tasks = staticmethod(tasks._c_all_tasks)
31893210
current_task = staticmethod(tasks._c_current_task)
3211+
Task = tasks._CTask
31903212
else:
31913213
_register_task = _unregister_task = _enter_task = _leave_task = None
31923214

31933215

3216+
31943217
class BaseCurrentLoopTests:
31953218
current_task = None
31963219

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix crash when a task gets re-registered during finalization in :mod:`asyncio`. Patch by Kumar Aditya.

Modules/_asynciomodule.c

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2990,16 +2990,12 @@ static PyType_Spec Task_spec = {
29902990
static void
29912991
TaskObj_dealloc(PyObject *self)
29922992
{
2993-
_PyObject_ResurrectStart(self);
2994-
// Unregister the task here so that even if any subclass of Task
2995-
// which doesn't end up calling TaskObj_finalize not crashes.
2996-
unregister_task((TaskObj *)self);
2997-
2998-
PyObject_CallFinalizer(self);
2999-
3000-
if (_PyObject_ResurrectEnd(self)) {
3001-
return;
2993+
if (PyObject_CallFinalizerFromDealloc(self) < 0) {
2994+
return; // resurrected
30022995
}
2996+
// unregister the task after finalization so that
2997+
// if the task gets resurrected, it remains registered
2998+
unregister_task((TaskObj *)self);
30032999

30043000
PyTypeObject *tp = Py_TYPE(self);
30053001
PyObject_GC_UnTrack(self);

0 commit comments

Comments
 (0)