Skip to content

Commit 347d359

Browse files
gh-143300: implement PyUnstable_SetImmortal for marking objects as immortal (#144543)
1 parent 81484c5 commit 347d359

File tree

5 files changed

+70
-0
lines changed

5 files changed

+70
-0
lines changed

Doc/c-api/object.rst

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -801,3 +801,20 @@ Object Protocol
801801
cannot fail.
802802
803803
.. versionadded:: 3.14
804+
805+
.. c:function:: int PyUnstable_SetImmortal(PyObject *op)
806+
807+
Marks the object *op* :term:`immortal`. The argument should be uniquely referenced by
808+
the calling thread. This is intended to be used for reducing reference counting contention
809+
in the :term:`free-threaded build` for objects which are shared across threads.
810+
811+
This is a one-way process: objects can only be made immortal; they cannot be
812+
made mortal once again. Immortal objects do not participate in reference counting
813+
and will never be garbage collected. If the object is GC-tracked, it is untracked.
814+
815+
This function is intended to be used soon after *op* is created, by the code that
816+
creates it, such as in the object's :c:member:`~PyTypeObject.tp_new` slot.
817+
Returns 1 if the object was made immortal and returns 0 if it was not.
818+
This function cannot fail.
819+
820+
.. versionadded:: next

Include/cpython/object.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,3 +493,5 @@ PyAPI_FUNC(int) PyUnstable_TryIncRef(PyObject *);
493493
PyAPI_FUNC(void) PyUnstable_EnableTryIncRef(PyObject *);
494494

495495
PyAPI_FUNC(int) PyUnstable_Object_IsUniquelyReferenced(PyObject *);
496+
497+
PyAPI_FUNC(int) PyUnstable_SetImmortal(PyObject *op);
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add :c:func:`PyUnstable_SetImmortal` C-API function to mark objects as :term:`immortal`.

Modules/_testcapi/object.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,44 @@ test_py_try_inc_ref(PyObject *self, PyObject *unused)
201201
Py_RETURN_NONE;
202202
}
203203

204+
static PyObject *
205+
test_py_set_immortal(PyObject *self, PyObject *unused)
206+
{
207+
// the object is allocated on C stack as otherwise,
208+
// it would trip the refleak checker when the object
209+
// is made immortal and leak memory, for the same
210+
// reason we cannot call PyObject_Init() on it.
211+
PyObject object = {0};
212+
#ifdef Py_GIL_DISABLED
213+
object.ob_tid = _Py_ThreadId();
214+
object.ob_gc_bits = 0;
215+
object.ob_ref_local = 1;
216+
object.ob_ref_shared = 0;
217+
#else
218+
object.ob_refcnt = 1;
219+
#endif
220+
object.ob_type = &PyBaseObject_Type;
221+
222+
assert(!PyUnstable_IsImmortal(&object));
223+
int rc = PyUnstable_SetImmortal(&object);
224+
assert(rc == 1);
225+
assert(PyUnstable_IsImmortal(&object));
226+
Py_DECREF(&object); // should not dealloc
227+
assert(PyUnstable_IsImmortal(&object));
228+
229+
// Check already immortal object
230+
rc = PyUnstable_SetImmortal(&object);
231+
assert(rc == 0);
232+
233+
// Check unicode objects
234+
PyObject *unicode = PyUnicode_FromString("test");
235+
assert(!PyUnstable_IsImmortal(unicode));
236+
rc = PyUnstable_SetImmortal(unicode);
237+
assert(rc == 0);
238+
assert(!PyUnstable_IsImmortal(unicode));
239+
Py_DECREF(unicode);
240+
Py_RETURN_NONE;
241+
}
204242

205243
static PyObject *
206244
_test_incref(PyObject *ob)
@@ -528,6 +566,7 @@ static PyMethodDef test_methods[] = {
528566
{"pyobject_is_unique_temporary", pyobject_is_unique_temporary, METH_O},
529567
{"pyobject_is_unique_temporary_new_object", pyobject_is_unique_temporary_new_object, METH_NOARGS},
530568
{"test_py_try_inc_ref", test_py_try_inc_ref, METH_NOARGS},
569+
{"test_py_set_immortal", test_py_set_immortal, METH_NOARGS},
531570
{"test_xincref_doesnt_leak",test_xincref_doesnt_leak, METH_NOARGS},
532571
{"test_incref_doesnt_leak", test_incref_doesnt_leak, METH_NOARGS},
533572
{"test_xdecref_doesnt_leak",test_xdecref_doesnt_leak, METH_NOARGS},

Objects/object.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2839,6 +2839,17 @@ PyUnstable_EnableTryIncRef(PyObject *op)
28392839
#endif
28402840
}
28412841

2842+
int
2843+
PyUnstable_SetImmortal(PyObject *op)
2844+
{
2845+
assert(op != NULL);
2846+
if (!_PyObject_IsUniquelyReferenced(op) || PyUnicode_Check(op)) {
2847+
return 0;
2848+
}
2849+
_Py_SetImmortal(op);
2850+
return 1;
2851+
}
2852+
28422853
void
28432854
_Py_ResurrectReference(PyObject *op)
28442855
{

0 commit comments

Comments
 (0)