Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions Doc/c-api/refcounting.rst
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,69 @@ The following functions or macros are only for use within the interpreter core:
:c:func:`_Py_Dealloc`, :c:func:`_Py_ForgetReference`, :c:func:`_Py_NewReference`,
as well as the global variable :c:data:`_Py_RefTotal`.

.. _immortal-objects:

Immortal Objects
================

"Immortal" objects are those that are expected to never be deallocated
by the runtime (due to their reference count reaching 0). In the public
C-API examples of such objects includes the singletons and many builtin
types. For such objects the reference count is essentially irrelevant.
Immortal objects are especially useful if otherwise immutable.

Note that for now the API for immortal objects is not available
for general use, by default. Users of the public C-API (but not
the limited API) may opt in by defining ``_Py_IMMORTAL_OBJECTS``.
This API should not be considered stable yet.

.. c:function:: int _PyObject_IsImmortal(PyObject *o)

Return non-zero if the object is immortal.

.. versionadded:: 3.10

.. c:function:: void _PyObject_SetImmortal(PyObject *o)

Mark an object as immortal.

.. versionadded:: 3.10

.. c:macro:: _PyObject_IMMORTAL_BIT

This is the bit in the reference count value that indicates whether
or not the object is immortal.

This is for internal use only. Instead use
:c:func:`_PyObject_IsImmortal` and
:c:func:`_PyObject_IsImmortal`.

.. versionadded:: 3.10

.. c:macro:: _PyObject_IMMORTAL_INIT_REFCNT

This is the reference count value to which immortal objects
are initialized.

This is for internal use only. Instead use
:c:func:`_PyObject_IsImmortal` and
:c:func:`_PyObject_IsImmortal`.

.. versionadded:: 3.10

Also see :c:macro:`_PyObject_HEAD_IMMORTAL_INIT` and
:c:macro:`_PyVarObject_HEAD_IMMORTAL_INIT`.

.. _immutable-refcounts:

Immutable Refcounts
-------------------

If ``Py_IMMORTAL_CONST_REFCOUNTS`` is defined then the following
happens:

* the immortal objects API is enabled
* the runtime never changes reference counts for immortal objects

This mode can help with copy-on-write semantics when forking.

34 changes: 34 additions & 0 deletions Doc/c-api/structures.rst
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ the definition of all other Python objects.
.. c:function:: void Py_SET_REFCNT(PyObject *o, Py_ssize_t refcnt)

Set the object *o* reference counter to *refcnt*.
:ref:`immortal-objects` are not affected.

.. versionadded:: 3.9

Expand Down Expand Up @@ -135,6 +136,39 @@ the definition of all other Python objects.
1, type, size,


.. c:macro:: _PyObject_HEAD_IMMORTAL_INIT(type)

This is a macro which expands to initialization values for a new
:c:type:`PyObject` type. It makes the object
:ref:`immortal <immortal-objects>`. This macro expands to::

_PyObject_EXTRA_INIT
_PyObject_IMMORTAL_INIT_REFCNT, type,

For now you must opt in to use this by defining
``_Py_IMMORTAL_OBJECTS``.

.. versionadded:: 3.10


.. c:macro:: PyVarObject_HEAD_IMMORTAL_INIT(type, size)

This is a macro which expands to initialization values for a new
:c:type:`PyVarObject` type, including the :attr:`ob_size` field.
It makes the object :ref:`immortal <immortal-objects>`. This
macro expands to::

_PyObject_EXTRA_INIT
_PyObject_IMMORTAL_INIT_REFCNT, type, size,

This is especially useful for static types.

For now you must opt in to use this by defining
``_Py_IMMORTAL_OBJECTS``.

.. versionadded:: 3.10


Implementing functions and methods
----------------------------------

Expand Down
34 changes: 34 additions & 0 deletions Include/cpython/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,40 @@ PyAPI_FUNC(PyObject *) _PyObject_FunctionStr(PyObject *);
Py_XDECREF(_py_tmp); \
} while (0)

/* An "immortal" object is one for which Py_DECREF() will never try
* to deallocate it. To achieve this we set the refcount to some
* positive value that we would never expect to be reachable through
* use of Py_INCREF() in a program.
*/

#if defined(Py_IMMORTAL_CONST_REFCOUNTS) || defined(Py_BUILD_CORE)
#define _Py_IMMORTAL_OBJECTS 1
#endif

#ifdef _Py_IMMORTAL_OBJECTS
/* The GC bit-shifts refcounts left by two, and after that shift we still
* need this to be >> 0, so leave three high zero bits (the sign bit and
* room for a shift of two.) */
#define _PyObject_IMMORTAL_BIT (1LL << (8 * sizeof(Py_ssize_t) - 4))
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use a higher bit on 32-bit builds?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably not, due to the bits used by GC.


#ifdef Py_IMMORTAL_CONST_REFCOUNTS
#define _PyObject_IMMORTAL_INIT_REFCNT \
_PyObject_IMMORTAL_BIT
#else
// We leave plenty of room to preserve _PyObject_IMMORTAL_BIT.
#define _PyObject_IMMORTAL_INIT_REFCNT \
(_PyObject_IMMORTAL_BIT + (_PyObject_IMMORTAL_BIT / 2))
#endif

#define _PyObject_HEAD_IMMORTAL_INIT(type) \
{ _PyObject_EXTRA_INIT _PyObject_IMMORTAL_INIT_REFCNT, type },
#define _PyVarObject_HEAD_IMMORTAL_INIT(type, size) \
{ PyObject_HEAD_IMMORTAL_INIT(type) size },

PyAPI_FUNC(int) _PyObject_IsImmortal(PyObject *);
PyAPI_FUNC(void) _PyObject_SetImmortal(PyObject *);
#endif


PyAPI_DATA(PyTypeObject) _PyNone_Type;
PyAPI_DATA(PyTypeObject) _PyNotImplemented_Type;
Expand Down
29 changes: 28 additions & 1 deletion Include/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,21 @@ typedef struct {
#define _PyVarObject_CAST(op) ((PyVarObject*)(op))
#define _PyVarObject_CAST_CONST(op) ((const PyVarObject*)(op))

// This is a static version of _PyObject_IsImmortal(), for the sake
// of other static functions, like _Py_SET_REFCNT() and _Py_INCREF().
static inline int _py_is_immortal(PyObject *op)
{
#ifdef Py_IMMORTAL_CONST_REFCOUNTS
#ifndef _PyObject_IMMORTAL_BIT
// This is duplicated as-is from the internal API.
#define _PyObject_IMMORTAL_BIT (1LL << (8 * sizeof(Py_ssize_t) - 4))
#endif
return (op->ob_refcnt & _PyObject_IMMORTAL_BIT) != 0;
#else
extern int _PyObject_IsImmortal(PyObject *);
return _PyObject_IsImmortal(op);
#endif
}

static inline Py_ssize_t _Py_REFCNT(const PyObject *ob) {
return ob->ob_refcnt;
Expand All @@ -142,6 +157,9 @@ static inline int _Py_IS_TYPE(const PyObject *ob, const PyTypeObject *type) {


static inline void _Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt) {
if (_py_is_immortal((PyObject *)ob)) {
return;
}
ob->ob_refcnt = refcnt;
}
#define Py_SET_REFCNT(ob, refcnt) _Py_SET_REFCNT(_PyObject_CAST(ob), refcnt)
Expand Down Expand Up @@ -431,6 +449,11 @@ static inline void _Py_INCREF(PyObject *op)
{
#ifdef Py_REF_DEBUG
_Py_RefTotal++;
#endif
#ifdef Py_IMMORTAL_CONST_REFCOUNTS
if (_py_is_immortal(op)) {
return;
}
#endif
op->ob_refcnt++;
}
Expand All @@ -444,6 +467,11 @@ static inline void _Py_DECREF(
{
#ifdef Py_REF_DEBUG
_Py_RefTotal--;
#endif
#ifdef Py_IMMORTAL_CONST_REFCOUNTS
if (_py_is_immortal(op)) {
return;
}
#endif
if (--op->ob_refcnt != 0) {
#ifdef Py_REF_DEBUG
Expand All @@ -462,7 +490,6 @@ static inline void _Py_DECREF(
# define Py_DECREF(op) _Py_DECREF(_PyObject_CAST(op))
#endif


/* Safely decref `op` and set `op` to NULL, especially useful in tp_clear
* and tp_dealloc implementations.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
There is now internal support for "immortal" objects. Those are objects
that will never be deleted, like the singletons and static types. This has
benefits for the C-API and subinterpreters, as well as allowing for better
copy-on-write behavior for forking (if Py_IMMORTAL_CONST_REFCOUNTS is used).
The feature is currently intended for internal use, though you can try it
out by building with _Py_IMMORTAL_OBJECTS defined.
22 changes: 21 additions & 1 deletion Objects/object.c
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,21 @@ Py_DecRef(PyObject *o)
Py_XDECREF(o);
}

int
_PyObject_IsImmortal(PyObject *ob)
{
if ((ob->ob_refcnt & _PyObject_IMMORTAL_BIT) == 0) {
return 0;
}
return 1;
}

void
_PyObject_SetImmortal(PyObject *ob)
{
ob->ob_refcnt = _PyObject_IMMORTAL_INIT_REFCNT;
}

PyObject *
PyObject_Init(PyObject *op, PyTypeObject *tp)
{
Expand Down Expand Up @@ -1728,11 +1743,14 @@ _PyTypes_Init(void)
return status;
}

// XXX We can stop calling _PyObject_SetImmortal() once we change
// all the static types to use PyVarObject_HEAD_IMMORTAL_INIT.
#define INIT_TYPE(TYPE, NAME) \
do { \
if (PyType_Ready(TYPE) < 0) { \
return _PyStatus_ERR("Can't initialize " NAME " type"); \
} \
_PyObject_SetImmortal((PyObject *)TYPE); \
} while (0)

INIT_TYPE(&PyBaseObject_Type, "object");
Expand Down Expand Up @@ -1817,7 +1835,9 @@ _Py_NewReference(PyObject *op)
#ifdef Py_REF_DEBUG
_Py_RefTotal++;
#endif
Py_SET_REFCNT(op, 1);
/* Do not use Py_SET_REFCNT to skip the Immortal Instance check. This
* API guarantees that an instance will always be set to a refcnt of 1 */
op->ob_refcnt = 1;
#ifdef Py_TRACE_REFS
_Py_AddToAllObjects(op, 1);
#endif
Expand Down
4 changes: 3 additions & 1 deletion Objects/typeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,9 @@ _PyType_CheckConsistency(PyTypeObject *type)
return 1;
}

CHECK(Py_REFCNT(type) >= 1);
if (!_PyObject_IsImmortal((PyObject *)type)) {
CHECK(Py_REFCNT(type) >= 1);
}
CHECK(PyType_Check(type));

CHECK(!(type->tp_flags & Py_TPFLAGS_READYING));
Expand Down