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 7a052817766a07..e53cbf0036e265 100644 --- a/Lib/test/test_asyncio/test_tasks.py +++ b/Lib/test/test_asyncio/test_tasks.py @@ -2710,6 +2710,19 @@ def __str__(self): self.assertEqual(sys.getrefcount(obj), initial_refcount) + def test_subclass_fail_to_propagate_del(self): + # gh-129289: Fix subclass of asyncio.Task not propagating __del__() causes segfault. + class BadTask(self.Task): + def __del__(self): + pass + + async def func(): + return + + task = BadTask(func(), loop=self.loop) + + result = self.loop.run_until_complete(task) + def add_subclass_tests(cls): BaseTask = cls.Task diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-02-07-12-58-23.gh-issue-129289.tHkEC-.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-02-07-12-58-23.gh-issue-129289.tHkEC-.rst new file mode 100644 index 00000000000000..c790acfae91917 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-02-07-12-58-23.gh-issue-129289.tHkEC-.rst @@ -0,0 +1 @@ +Fix subclass of :class:`asyncio.Task` not propagating finalizer causes segfault. diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 656c03a98d73b2..68d8294dbeaada 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -3075,6 +3075,16 @@ TaskObj_dealloc(PyObject *self) // resurrected. return; } + if (task->task_node.next != NULL) { + if (PyErr_WarnEx(PyExc_Warning, "subclasses of asyncio.Task must propagate __del__()", 1) < 0) { + PyErr_Clear(); + } + _PyObject_ResurrectStart(self); + TaskObj_finalize(task); + if (_PyObject_ResurrectEnd(self)) { + return; + } + } PyTypeObject *tp = Py_TYPE(task); PyObject_GC_UnTrack(self);