From 05186ecf07db43cbfc57f708260d86fe698a5b49 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Sat, 8 Feb 2025 05:49:50 +0000 Subject: [PATCH 1/2] fix crash --- Include/internal/pycore_object.h | 4 ++-- Lib/test/test_asyncio/test_tasks.py | 15 +++++++++++++++ Modules/_asynciomodule.c | 18 +++++++----------- 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h index 0b1df7e68b8dfa..49ddfd5b43b00c 100644 --- a/Include/internal/pycore_object.h +++ b/Include/internal/pycore_object.h @@ -121,7 +121,7 @@ PyAPI_DATA(Py_ssize_t) _Py_RefTotal; extern void _Py_AddRefTotal(PyThreadState *, Py_ssize_t); extern PyAPI_FUNC(void) _Py_IncRefTotal(PyThreadState *); -extern void _Py_DecRefTotal(PyThreadState *); +extern PyAPI_FUNC(void) _Py_DecRefTotal(PyThreadState *); # define _Py_DEC_REFTOTAL(interp) \ interp->object_state.reftotal-- @@ -710,7 +710,7 @@ _PyObject_SetMaybeWeakref(PyObject *op) } } -extern int _PyObject_ResurrectEndSlow(PyObject *op); +extern PyAPI_FUNC(int) _PyObject_ResurrectEndSlow(PyObject *op); #endif // Temporarily resurrects an object during deallocation. The refcount is set diff --git a/Lib/test/test_asyncio/test_tasks.py b/Lib/test/test_asyncio/test_tasks.py index 881a7aeeb2fbe5..87df746bcd23fe 100644 --- a/Lib/test/test_asyncio/test_tasks.py +++ b/Lib/test/test_asyncio/test_tasks.py @@ -2296,6 +2296,21 @@ async def kill_me(loop): self.assertEqual(self.all_tasks(loop=self.loop), set()) + def test_task_not_crash_without_finalization(self): + Task = self.__class__.Task + + class Subclass(Task): + def __del__(self): + pass + + async def coro(): + await asyncio.sleep(0.01) + + task = Subclass(coro(), loop = self.loop) + + del task + + support.gc_collect() @mock.patch('asyncio.base_events.logger') def test_tb_logger_not_called_after_cancel(self, m_log): diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 656c03a98d73b2..819a745b4dd68a 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -2938,15 +2938,6 @@ _asyncio_Task_set_name_impl(TaskObj *self, PyObject *value) static void TaskObj_finalize(TaskObj *task) { - asyncio_state *state = get_asyncio_state_by_def((PyObject *)task); - // Unregister the task from the linked list of tasks. - // Since task is a native task, we directly call the - // unregister_task function. Third party event loops - // should use the asyncio._unregister_task function. - // See https://docs.python.org/3/library/asyncio-extending.html#task-lifetime-support - - unregister_task(state, task); - PyObject *context; PyObject *message = NULL; PyObject *func; @@ -3071,8 +3062,13 @@ TaskObj_dealloc(PyObject *self) { TaskObj *task = (TaskObj *)self; - if (PyObject_CallFinalizerFromDealloc(self) < 0) { - // resurrected. + _PyObject_ResurrectStart(self); + asyncio_state *state = get_asyncio_state_by_def(self); + unregister_task(state, task); + + PyObject_CallFinalizer(self); + + if (_PyObject_ResurrectEnd(self)) { return; } From 21d129d99ad19a9df1f616f93e794b96bd8803c4 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Sat, 8 Feb 2025 06:17:33 +0000 Subject: [PATCH 2/2] comment --- Lib/test/test_asyncio/test_tasks.py | 1 + Modules/_asynciomodule.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/Lib/test/test_asyncio/test_tasks.py b/Lib/test/test_asyncio/test_tasks.py index 87df746bcd23fe..e43a7f9f3c818d 100644 --- a/Lib/test/test_asyncio/test_tasks.py +++ b/Lib/test/test_asyncio/test_tasks.py @@ -2307,6 +2307,7 @@ async def coro(): await asyncio.sleep(0.01) task = Subclass(coro(), loop = self.loop) + task._log_destroy_pending = False del task diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 819a745b4dd68a..f2c79cbecd530a 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -3063,6 +3063,8 @@ TaskObj_dealloc(PyObject *self) TaskObj *task = (TaskObj *)self; _PyObject_ResurrectStart(self); + // Unregister the task here so that even if any subclass of Task + // which doesn't end up calling TaskObj_finalize not crashes. asyncio_state *state = get_asyncio_state_by_def(self); unregister_task(state, task);