From b874f0b43a717205c57e26020254aa46ec9d406b Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Fri, 6 Feb 2026 19:11:20 +0530 Subject: [PATCH 1/3] implement PyUnstable_SetImmortal --- Doc/c-api/object.rst | 16 ++++++++++++++++ Include/cpython/object.h | 2 ++ Modules/_testcapi/object.c | 18 ++++++++++++++++++ Objects/object.c | 8 ++++++++ 4 files changed, 44 insertions(+) diff --git a/Doc/c-api/object.rst b/Doc/c-api/object.rst index 992a4383f97241..476776e32f9b34 100644 --- a/Doc/c-api/object.rst +++ b/Doc/c-api/object.rst @@ -801,3 +801,19 @@ Object Protocol cannot fail. .. versionadded:: 3.14 + +.. c:function:: int PyUnstable_SetImmortal(PyObject *obj) + + Marks the object op immortal. The argument should be uniquely referenced by + the calling thread. + + This is a one-way process: objects can only be made immortal, they cannot be + made mortal once again. Immortal objects do not participate in reference counting + and will never be garbage collected. + + This function is intended to be used soon after op is created, by the code that + creates it, such as in the object's tp_new slot. + Returns 1 if the object was made immortal and returns 0 if it was not. + This function cannot fail. + + .. versionadded:: next \ No newline at end of file diff --git a/Include/cpython/object.h b/Include/cpython/object.h index 28c909531dba64..61cdb93d1d5354 100644 --- a/Include/cpython/object.h +++ b/Include/cpython/object.h @@ -493,3 +493,5 @@ PyAPI_FUNC(int) PyUnstable_TryIncRef(PyObject *); PyAPI_FUNC(void) PyUnstable_EnableTryIncRef(PyObject *); PyAPI_FUNC(int) PyUnstable_Object_IsUniquelyReferenced(PyObject *); + +PyAPI_FUNC(int) PyUnstable_SetImmortal(PyObject *op); diff --git a/Modules/_testcapi/object.c b/Modules/_testcapi/object.c index 153b28e5fe2e95..8b67e79b1f090b 100644 --- a/Modules/_testcapi/object.c +++ b/Modules/_testcapi/object.c @@ -201,6 +201,23 @@ test_py_try_inc_ref(PyObject *self, PyObject *unused) Py_RETURN_NONE; } +static PyObject * +test_py_set_immortal(PyObject *self, PyObject *unused) +{ + // the object is allocated on C stack as otherwise, + // it would trip the refleak checker when the object + // is made immortal and leak memory + PyObject object = { + .ob_refcnt = 1, + .ob_type = &PyBaseObject_Type, + }; + assert(!PyUnstable_IsImmortal(&object)); + PyUnstable_SetImmortal(&object); + assert(PyUnstable_IsImmortal(&object)); + Py_DECREF(&object); // should not dealloc + assert(PyUnstable_IsImmortal(&object)); + Py_RETURN_NONE; +} static PyObject * _test_incref(PyObject *ob) @@ -528,6 +545,7 @@ static PyMethodDef test_methods[] = { {"pyobject_is_unique_temporary", pyobject_is_unique_temporary, METH_O}, {"pyobject_is_unique_temporary_new_object", pyobject_is_unique_temporary_new_object, METH_NOARGS}, {"test_py_try_inc_ref", test_py_try_inc_ref, METH_NOARGS}, + {"test_py_set_immortal", test_py_set_immortal, METH_NOARGS}, {"test_xincref_doesnt_leak",test_xincref_doesnt_leak, METH_NOARGS}, {"test_incref_doesnt_leak", test_incref_doesnt_leak, METH_NOARGS}, {"test_xdecref_doesnt_leak",test_xdecref_doesnt_leak, METH_NOARGS}, diff --git a/Objects/object.c b/Objects/object.c index 38717def24239f..53bf405b96fcd2 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -2790,6 +2790,14 @@ PyUnstable_EnableTryIncRef(PyObject *op) #endif } +int +PyUnstable_SetImmortal(PyObject *op) +{ + assert(op != NULL); + _Py_SetImmortal(op); + return 1; +} + void _Py_ResurrectReference(PyObject *op) { From cb6cc99349a4d4993623cee265fef98f879d2942 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Fri, 6 Feb 2026 19:21:36 +0530 Subject: [PATCH 2/3] fix compilation under ft --- Modules/_testcapi/object.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Modules/_testcapi/object.c b/Modules/_testcapi/object.c index 8b67e79b1f090b..55ef5be573e8e5 100644 --- a/Modules/_testcapi/object.c +++ b/Modules/_testcapi/object.c @@ -207,10 +207,9 @@ test_py_set_immortal(PyObject *self, PyObject *unused) // the object is allocated on C stack as otherwise, // it would trip the refleak checker when the object // is made immortal and leak memory - PyObject object = { - .ob_refcnt = 1, - .ob_type = &PyBaseObject_Type, - }; + PyObject object = {0}; + Py_SET_REFCNT(&object, 1); + Py_SET_TYPE(&object, &PyBaseObject_Type); assert(!PyUnstable_IsImmortal(&object)); PyUnstable_SetImmortal(&object); assert(PyUnstable_IsImmortal(&object)); From c897a08d2eca94186518e7f58fee096b0ae946fb Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Sat, 7 Feb 2026 08:47:00 +0530 Subject: [PATCH 3/3] minor fixes --- Doc/c-api/object.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Doc/c-api/object.rst b/Doc/c-api/object.rst index 476776e32f9b34..12a98fd40f7523 100644 --- a/Doc/c-api/object.rst +++ b/Doc/c-api/object.rst @@ -802,16 +802,16 @@ Object Protocol .. versionadded:: 3.14 -.. c:function:: int PyUnstable_SetImmortal(PyObject *obj) +.. c:function:: int PyUnstable_SetImmortal(PyObject *op) - Marks the object op immortal. The argument should be uniquely referenced by + Marks the object *op* immortal. The argument should be uniquely referenced by the calling thread. This is a one-way process: objects can only be made immortal, they cannot be made mortal once again. Immortal objects do not participate in reference counting and will never be garbage collected. - This function is intended to be used soon after op is created, by the code that + This function is intended to be used soon after *op* is created, by the code that creates it, such as in the object's tp_new slot. Returns 1 if the object was made immortal and returns 0 if it was not. This function cannot fail.