Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 94cd2e0

Browse files
gh-129289: fix crash when task finalizer is not called in asyncio (#129840)
1 parent 7c156a6 commit 94cd2e0

File tree

3 files changed

+27
-13
lines changed

3 files changed

+27
-13
lines changed

Include/internal/pycore_object.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ PyAPI_DATA(Py_ssize_t) _Py_RefTotal;
121121

122122
extern void _Py_AddRefTotal(PyThreadState *, Py_ssize_t);
123123
extern PyAPI_FUNC(void) _Py_IncRefTotal(PyThreadState *);
124-
extern void _Py_DecRefTotal(PyThreadState *);
124+
extern PyAPI_FUNC(void) _Py_DecRefTotal(PyThreadState *);
125125

126126
# define _Py_DEC_REFTOTAL(interp) \
127127
interp->object_state.reftotal--
@@ -710,7 +710,7 @@ _PyObject_SetMaybeWeakref(PyObject *op)
710710
}
711711
}
712712

713-
extern int _PyObject_ResurrectEndSlow(PyObject *op);
713+
extern PyAPI_FUNC(int) _PyObject_ResurrectEndSlow(PyObject *op);
714714
#endif
715715

716716
// Temporarily resurrects an object during deallocation. The refcount is set

Lib/test/test_asyncio/test_tasks.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2296,6 +2296,22 @@ async def kill_me(loop):
22962296

22972297
self.assertEqual(self.all_tasks(loop=self.loop), set())
22982298

2299+
def test_task_not_crash_without_finalization(self):
2300+
Task = self.__class__.Task
2301+
2302+
class Subclass(Task):
2303+
def __del__(self):
2304+
pass
2305+
2306+
async def coro():
2307+
await asyncio.sleep(0.01)
2308+
2309+
task = Subclass(coro(), loop = self.loop)
2310+
task._log_destroy_pending = False
2311+
2312+
del task
2313+
2314+
support.gc_collect()
22992315

23002316
@mock.patch('asyncio.base_events.logger')
23012317
def test_tb_logger_not_called_after_cancel(self, m_log):

Modules/_asynciomodule.c

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2938,15 +2938,6 @@ _asyncio_Task_set_name_impl(TaskObj *self, PyObject *value)
29382938
static void
29392939
TaskObj_finalize(TaskObj *task)
29402940
{
2941-
asyncio_state *state = get_asyncio_state_by_def((PyObject *)task);
2942-
// Unregister the task from the linked list of tasks.
2943-
// Since task is a native task, we directly call the
2944-
// unregister_task function. Third party event loops
2945-
// should use the asyncio._unregister_task function.
2946-
// See https://docs.python.org/3/library/asyncio-extending.html#task-lifetime-support
2947-
2948-
unregister_task(state, task);
2949-
29502941
PyObject *context;
29512942
PyObject *message = NULL;
29522943
PyObject *func;
@@ -3071,8 +3062,15 @@ TaskObj_dealloc(PyObject *self)
30713062
{
30723063
TaskObj *task = (TaskObj *)self;
30733064

3074-
if (PyObject_CallFinalizerFromDealloc(self) < 0) {
3075-
// resurrected.
3065+
_PyObject_ResurrectStart(self);
3066+
// Unregister the task here so that even if any subclass of Task
3067+
// which doesn't end up calling TaskObj_finalize not crashes.
3068+
asyncio_state *state = get_asyncio_state_by_def(self);
3069+
unregister_task(state, task);
3070+
3071+
PyObject_CallFinalizer(self);
3072+
3073+
if (_PyObject_ResurrectEnd(self)) {
30763074
return;
30773075
}
30783076

0 commit comments

Comments
 (0)