From 73ae2023a76f199ff854f8da14bd9ff8e93ee7fd Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Wed, 10 Jan 2024 13:18:38 -0500 Subject: [PATCH] gh-113753: Clear finalized bit when putting PyAsyncGenASend back into free list (#113754) --- Include/internal/pycore_gc.h | 4 ++++ Lib/test/test_asyncgen.py | 8 ++++++++ .../2024-01-05-21-28-48.gh-issue-113753.2HNiuq.rst | 2 ++ Objects/genobject.c | 2 ++ 4 files changed, 16 insertions(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-01-05-21-28-48.gh-issue-113753.2HNiuq.rst diff --git a/Include/internal/pycore_gc.h b/Include/internal/pycore_gc.h index 2a0730eebb8..753763a5a50 100644 --- a/Include/internal/pycore_gc.h +++ b/Include/internal/pycore_gc.h @@ -122,6 +122,10 @@ static inline void _PyGC_SET_FINALIZED(PyObject *op) { PyGC_Head *gc = _Py_AS_GC(op); _PyGCHead_SET_FINALIZED(gc); } +static inline void _PyGC_CLEAR_FINALIZED(PyObject *op) { + PyGC_Head *gc = _Py_AS_GC(op); + gc->_gc_prev &= ~_PyGC_PREV_MASK_FINALIZED; +} /* GC runtime state */ diff --git a/Lib/test/test_asyncgen.py b/Lib/test/test_asyncgen.py index a49630112af..7fa0a85100a 100644 --- a/Lib/test/test_asyncgen.py +++ b/Lib/test/test_asyncgen.py @@ -1701,6 +1701,14 @@ class TestUnawaitedWarnings(unittest.TestCase): async def gen(): yield 1 + # gh-113753: asend objects allocated from a free-list should warn. + # Ensure there is a finalized 'asend' object ready to be reused. + try: + g = gen() + g.asend(None).send(None) + except StopIteration: + pass + msg = f"coroutine method 'asend' of '{gen.__qualname__}' was never awaited" with self.assertWarnsRegex(RuntimeWarning, msg): g = gen() diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-01-05-21-28-48.gh-issue-113753.2HNiuq.rst b/Misc/NEWS.d/next/Core and Builtins/2024-01-05-21-28-48.gh-issue-113753.2HNiuq.rst new file mode 100644 index 00000000000..32cf2cb2a4a --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-01-05-21-28-48.gh-issue-113753.2HNiuq.rst @@ -0,0 +1,2 @@ +Fix an issue where the finalizer of ``PyAsyncGenASend`` objects might not be +called if they were allocated from a free list. diff --git a/Objects/genobject.c b/Objects/genobject.c index 96147138837..f03919c75d7 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -6,6 +6,7 @@ #include "pycore_call.h" // _PyObject_CallNoArgs() #include "pycore_ceval.h" // _PyEval_EvalFrame() #include "pycore_frame.h" // _PyInterpreterFrame +#include "pycore_gc.h" // _PyGC_CLEAR_FINALIZED() #include "pycore_genobject.h" // struct _Py_async_gen_state #include "pycore_modsupport.h" // _PyArg_CheckPositional() #include "pycore_object.h" // _PyObject_GC_UNTRACK() @@ -1739,6 +1740,7 @@ async_gen_asend_dealloc(PyAsyncGenASend *o) #endif if (state->asend_numfree < _PyAsyncGen_MAXFREELIST) { assert(PyAsyncGenASend_CheckExact(o)); + _PyGC_CLEAR_FINALIZED((PyObject *)o); state->asend_freelist[state->asend_numfree++] = o; } else