diff --git a/Doc/c-api/object.rst b/Doc/c-api/object.rst index 992a4383f97241..12a98fd40f7523 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 *op) + + 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..55ef5be573e8e5 100644 --- a/Modules/_testcapi/object.c +++ b/Modules/_testcapi/object.c @@ -201,6 +201,22 @@ 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 = {0}; + Py_SET_REFCNT(&object, 1); + Py_SET_TYPE(&object, &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 +544,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) {