From 4951703306d9852f8bc50248dc0966a9ef86feca Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 10 Dec 2020 11:02:26 -0700 Subject: [PATCH 01/16] Add support for immortal objects (large neg int means "immortal"). --- Include/cpython/object.h | 26 ++++++++++++++++++++++++++ Include/object.h | 17 ++++++++++++++--- Objects/object.c | 24 +++++++++++++++++++++++- Objects/typeobject.c | 4 +++- 4 files changed, 66 insertions(+), 5 deletions(-) diff --git a/Include/cpython/object.h b/Include/cpython/object.h index 58e4d2b11b93f9..b8ab2c032f201c 100644 --- a/Include/cpython/object.h +++ b/Include/cpython/object.h @@ -375,6 +375,32 @@ 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 + * negative value. To play it safe, we use a value right in the + * middle of the negative range. + */ + +#ifdef Py_BUILD_CORE +#define _Py_IMMORTAL_OBJECTS 1 +#endif + +#ifdef _Py_IMMORTAL_OBJECTS +#define _PyObject_IMMORTAL_BIT (PY_SSIZE_T_MAX / -4) + +// We leave plenty of room to preserve _PyObject_IMMORTAL_BIT. +#define _PyObject_IMMORTAL_INIT_REFCNT \ + (_PyObject_IMMORTAL_BIT + (_PyObject_IMMORTAL_BIT / 2)) + +#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; diff --git a/Include/object.h b/Include/object.h index 14545839341f4a..1be357b9b98547 100644 --- a/Include/object.h +++ b/Include/object.h @@ -121,8 +121,13 @@ typedef struct { #define _PyVarObject_CAST(op) ((PyVarObject*)(op)) #define _PyVarObject_CAST_CONST(op) ((const PyVarObject*)(op)) - static inline Py_ssize_t _Py_REFCNT(const PyObject *ob) { + extern int _PyObject_IsImmortal(PyObject *); + if (_PyObject_IsImmortal((PyObject *)ob)) { + // The value is somewhat arbitrary. It just needs + // to be positive and sufficiently far from 0. + return 1LL << (8 * sizeof(Py_ssize_t) - 4); + } return ob->ob_refcnt; } #define Py_REFCNT(ob) _Py_REFCNT(_PyObject_CAST_CONST(ob)) @@ -142,6 +147,10 @@ static inline int _Py_IS_TYPE(const PyObject *ob, const PyTypeObject *type) { static inline void _Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt) { + extern int _PyObject_IsImmortal(PyObject *); + if (_PyObject_IsImmortal((PyObject *)ob)) { + return; + } ob->ob_refcnt = refcnt; } #define Py_SET_REFCNT(ob, refcnt) _Py_SET_REFCNT(_PyObject_CAST(ob), refcnt) @@ -448,7 +457,10 @@ static inline void _Py_DECREF( if (--op->ob_refcnt != 0) { #ifdef Py_REF_DEBUG if (op->ob_refcnt < 0) { - _Py_NegativeRefcount(filename, lineno, op); + extern int _PyObject_IsImmortal(PyObject *); + if (!_PyObject_IsImmortal(op)) { + _Py_NegativeRefcount(filename, lineno, op); + } } #endif } @@ -462,7 +474,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. * diff --git a/Objects/object.c b/Objects/object.c index 0a8621b3503b31..1f77b232009791 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -136,6 +136,23 @@ Py_DecRef(PyObject *o) Py_XDECREF(o); } +int +_PyObject_IsImmortal(PyObject *ob) +{ + if ((ob->ob_refcnt & _PyObject_IMMORTAL_BIT) == 0) { + return 0; + } + // Reset it to avoid approaching the boundaries. + _PyObject_SetImmortal(ob); + return 1; +} + +void +_PyObject_SetImmortal(PyObject *ob) +{ + ob->ob_refcnt = _PyObject_IMMORTAL_INIT_REFCNT; +} + PyObject * PyObject_Init(PyObject *op, PyTypeObject *tp) { @@ -1728,11 +1745,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"); @@ -1817,7 +1837,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 diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 9e7121448f87df..c4b4af7705b391 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -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)); From 4b1eea38e5fcc34c98390308bc72e95a0ee59acd Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 10 Dec 2020 11:18:59 -0700 Subject: [PATCH 02/16] Switch to a positive number. --- Include/cpython/object.h | 9 ++++++--- Include/object.h | 6 ------ 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/Include/cpython/object.h b/Include/cpython/object.h index b8ab2c032f201c..6722bc0f0197af 100644 --- a/Include/cpython/object.h +++ b/Include/cpython/object.h @@ -377,8 +377,8 @@ PyAPI_FUNC(PyObject *) _PyObject_FunctionStr(PyObject *); /* An "immortal" object is one for which Py_DECREF() will never try * to deallocate it. To achieve this we set the refcount to some - * negative value. To play it safe, we use a value right in the - * middle of the negative range. + * positive value that we would never expect to be reachable through + * use of Py_INCREF() in a program. */ #ifdef Py_BUILD_CORE @@ -386,7 +386,10 @@ PyAPI_FUNC(PyObject *) _PyObject_FunctionStr(PyObject *); #endif #ifdef _Py_IMMORTAL_OBJECTS -#define _PyObject_IMMORTAL_BIT (PY_SSIZE_T_MAX / -4) +/* 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)) // We leave plenty of room to preserve _PyObject_IMMORTAL_BIT. #define _PyObject_IMMORTAL_INIT_REFCNT \ diff --git a/Include/object.h b/Include/object.h index 1be357b9b98547..886adf0617b67a 100644 --- a/Include/object.h +++ b/Include/object.h @@ -122,12 +122,6 @@ typedef struct { #define _PyVarObject_CAST_CONST(op) ((const PyVarObject*)(op)) static inline Py_ssize_t _Py_REFCNT(const PyObject *ob) { - extern int _PyObject_IsImmortal(PyObject *); - if (_PyObject_IsImmortal((PyObject *)ob)) { - // The value is somewhat arbitrary. It just needs - // to be positive and sufficiently far from 0. - return 1LL << (8 * sizeof(Py_ssize_t) - 4); - } return ob->ob_refcnt; } #define Py_REFCNT(ob) _Py_REFCNT(_PyObject_CAST_CONST(ob)) From a34f2856bbe68a19b564161a95b8cbbc49e38105 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 10 Dec 2020 11:24:17 -0700 Subject: [PATCH 03/16] Add Py_IMMORTAL_CONST_REFCOUNTS. --- Include/cpython/object.h | 7 ++++++- Include/object.h | 33 +++++++++++++++++++++++++++++++-- Objects/object.c | 2 ++ 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/Include/cpython/object.h b/Include/cpython/object.h index 6722bc0f0197af..af415da2e39448 100644 --- a/Include/cpython/object.h +++ b/Include/cpython/object.h @@ -381,7 +381,7 @@ PyAPI_FUNC(PyObject *) _PyObject_FunctionStr(PyObject *); * use of Py_INCREF() in a program. */ -#ifdef Py_BUILD_CORE +#if defined(Py_IMMORTAL_CONST_REFCOUNTS) || defined(Py_BUILD_CORE) #define _Py_IMMORTAL_OBJECTS 1 #endif @@ -391,9 +391,14 @@ PyAPI_FUNC(PyObject *) _PyObject_FunctionStr(PyObject *); * room for a shift of two.) */ #define _PyObject_IMMORTAL_BIT (1LL << (8 * sizeof(Py_ssize_t) - 4)) +#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 }, diff --git a/Include/object.h b/Include/object.h index 886adf0617b67a..949fa03d6b7e77 100644 --- a/Include/object.h +++ b/Include/object.h @@ -121,6 +121,22 @@ 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; } @@ -141,8 +157,7 @@ static inline int _Py_IS_TYPE(const PyObject *ob, const PyTypeObject *type) { static inline void _Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt) { - extern int _PyObject_IsImmortal(PyObject *); - if (_PyObject_IsImmortal((PyObject *)ob)) { + if (_py_is_immortal((PyObject *)ob)) { return; } ob->ob_refcnt = refcnt; @@ -434,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++; } @@ -447,14 +467,23 @@ 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 if (op->ob_refcnt < 0) { +#ifdef Py_IMMORTAL_CONST_REFCOUNTS + _Py_NegativeRefcount(filename, lineno, op); +#else extern int _PyObject_IsImmortal(PyObject *); if (!_PyObject_IsImmortal(op)) { _Py_NegativeRefcount(filename, lineno, op); } +#endif } #endif } diff --git a/Objects/object.c b/Objects/object.c index 1f77b232009791..b1cd733f241416 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -142,8 +142,10 @@ _PyObject_IsImmortal(PyObject *ob) if ((ob->ob_refcnt & _PyObject_IMMORTAL_BIT) == 0) { return 0; } +#ifndef Py_IMMORTAL_CONST_REFCOUNTS // Reset it to avoid approaching the boundaries. _PyObject_SetImmortal(ob); +#endif return 1; } From 3966551811e7f30d9fdc5cc015ea764e11a1f7b4 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 11 Mar 2021 13:03:58 -0700 Subject: [PATCH 04/16] Add a NEWS entry. --- .../2021-03-11-13-03-15.bpo-40255._1LfiG.rst | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2021-03-11-13-03-15.bpo-40255._1LfiG.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2021-03-11-13-03-15.bpo-40255._1LfiG.rst b/Misc/NEWS.d/next/Core and Builtins/2021-03-11-13-03-15.bpo-40255._1LfiG.rst new file mode 100644 index 00000000000000..4986d612704475 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2021-03-11-13-03-15.bpo-40255._1LfiG.rst @@ -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. From 1a3b4dabf448f0cce2b5513ff35e97c037e032c1 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 11 Mar 2021 14:39:07 -0700 Subject: [PATCH 05/16] Add docs. --- Doc/c-api/refcounting.rst | 66 +++++++++++++++++++++++++++++++++++++++ Doc/c-api/structures.rst | 34 ++++++++++++++++++++ 2 files changed, 100 insertions(+) diff --git a/Doc/c-api/refcounting.rst b/Doc/c-api/refcounting.rst index 391907c8c2976a..3da657032f033d 100644 --- a/Doc/c-api/refcounting.rst +++ b/Doc/c-api/refcounting.rst @@ -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. + diff --git a/Doc/c-api/structures.rst b/Doc/c-api/structures.rst index 37072d30d57153..e0c3891cff3581 100644 --- a/Doc/c-api/structures.rst +++ b/Doc/c-api/structures.rst @@ -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 @@ -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 `. 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 `. 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 ---------------------------------- From 728f24bb47d167b024a80400b14b4d53512595f7 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 11 Mar 2021 15:07:34 -0700 Subject: [PATCH 06/16] Drop special handling of negative refcounts. --- Include/object.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Include/object.h b/Include/object.h index 949fa03d6b7e77..713312a1f4b3f6 100644 --- a/Include/object.h +++ b/Include/object.h @@ -476,14 +476,7 @@ static inline void _Py_DECREF( if (--op->ob_refcnt != 0) { #ifdef Py_REF_DEBUG if (op->ob_refcnt < 0) { -#ifdef Py_IMMORTAL_CONST_REFCOUNTS _Py_NegativeRefcount(filename, lineno, op); -#else - extern int _PyObject_IsImmortal(PyObject *); - if (!_PyObject_IsImmortal(op)) { - _Py_NegativeRefcount(filename, lineno, op); - } -#endif } #endif } From 2536e8b45f4eae343b91f29a13f626ffc1dd555a Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 11 Mar 2021 15:10:10 -0700 Subject: [PATCH 07/16] Do not reset the refcount in _PyObject_IsImmortal(). --- Objects/object.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Objects/object.c b/Objects/object.c index b1cd733f241416..c2cb48892a734c 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -142,10 +142,6 @@ _PyObject_IsImmortal(PyObject *ob) if ((ob->ob_refcnt & _PyObject_IMMORTAL_BIT) == 0) { return 0; } -#ifndef Py_IMMORTAL_CONST_REFCOUNTS - // Reset it to avoid approaching the boundaries. - _PyObject_SetImmortal(ob); -#endif return 1; } From 518e9e84f6eee25be38369ea48825620fa68b953 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 11 Mar 2021 16:54:17 -0700 Subject: [PATCH 08/16] Do not allow immortal objects in the limited API. --- Include/object.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Include/object.h b/Include/object.h index 713312a1f4b3f6..07511b215e163a 100644 --- a/Include/object.h +++ b/Include/object.h @@ -703,6 +703,12 @@ times. #endif +#if defined(Py_LIMITED_API) && \ + (defined(_Py_IMMORTAL_OBJECTS) || defined(Py_IMMORTAL_CONST_REFCOUNTS)) +#error "the immortal objects API is not available in the limited API" +#endif + + static inline int PyType_HasFeature(PyTypeObject *type, unsigned long feature) { From 277e1ea1f3996f15dfda3f44c9a495ca3c224616 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 11 Mar 2021 15:55:30 -0700 Subject: [PATCH 09/16] Do not re-declare _PyObject_IsImmortal. --- Include/object.h | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/Include/object.h b/Include/object.h index 07511b215e163a..3f8c331ea98667 100644 --- a/Include/object.h +++ b/Include/object.h @@ -121,22 +121,6 @@ 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; } @@ -156,6 +140,8 @@ static inline int _Py_IS_TYPE(const PyObject *ob, const PyTypeObject *type) { #define Py_IS_TYPE(ob, type) _Py_IS_TYPE(_PyObject_CAST_CONST(ob), type) +static inline int _py_is_immortal(PyObject *); // forward + static inline void _Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt) { if (_py_is_immortal((PyObject *)ob)) { return; @@ -708,6 +694,20 @@ times. #error "the immortal objects API is not available in the limited API" #endif +// 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 + return (op->ob_refcnt & _PyObject_IMMORTAL_BIT) != 0; +#else +#ifndef _Py_IMMORTAL_OBJECTS + extern int _PyObject_IsImmortal(PyObject *); +#endif + return _PyObject_IsImmortal(op); +#endif +} + static inline int PyType_HasFeature(PyTypeObject *type, unsigned long feature) From edef3429eb9c45a8ef139398cc57f3414e880f8b Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Fri, 12 Mar 2021 09:45:22 -0700 Subject: [PATCH 10/16] Go back to using Py_SET_REFCNT() in _Py_NewReference(). --- Objects/object.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Objects/object.c b/Objects/object.c index c2cb48892a734c..02358cc9f014d4 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -1825,19 +1825,26 @@ _PyTypes_Init(void) #undef INIT_TYPE } +/* _Py_NewReference() is called in the following situations: + * - for newly allocatoed objects, via _PyObject_Init() + * - when resizing immutable objects (bytes, unicode, tuple) + * - when "allocating" from a freelist + * - when resurrecting an object + */ void _Py_NewReference(PyObject *op) { + // None of the cases above should ever apply to immortal objects. + assert(!_PyObject_IsImmortal(op)); + if (_Py_tracemalloc_config.tracing) { _PyTraceMalloc_NewReference(op); } #ifdef Py_REF_DEBUG _Py_RefTotal++; #endif - /* 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; + Py_SET_REFCNT(op, 1); #ifdef Py_TRACE_REFS _Py_AddToAllObjects(op, 1); #endif From 4d22e00271999d64dbb5f278f1a4d7c210686e11 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Fri, 12 Mar 2021 11:28:08 -0700 Subject: [PATCH 11/16] Fix an accidental line removal. --- Include/object.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Include/object.h b/Include/object.h index 3f8c331ea98667..0f4794198337e8 100644 --- a/Include/object.h +++ b/Include/object.h @@ -121,6 +121,7 @@ typedef struct { #define _PyVarObject_CAST(op) ((PyVarObject*)(op)) #define _PyVarObject_CAST_CONST(op) ((const PyVarObject*)(op)) + static inline Py_ssize_t _Py_REFCNT(const PyObject *ob) { return ob->ob_refcnt; } @@ -476,6 +477,7 @@ 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. * From 838f88fc135ff7a7bcb7fe1f12377991fb594717 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Fri, 12 Mar 2021 11:42:42 -0700 Subject: [PATCH 12/16] Add more comments and drop the internal-only macros from the docs. --- Doc/c-api/refcounting.rst | 22 ---------------------- Include/cpython/object.h | 38 +++++++++++++++++++++++++++++--------- 2 files changed, 29 insertions(+), 31 deletions(-) diff --git a/Doc/c-api/refcounting.rst b/Doc/c-api/refcounting.rst index 3da657032f033d..22ecdcc99fb495 100644 --- a/Doc/c-api/refcounting.rst +++ b/Doc/c-api/refcounting.rst @@ -147,28 +147,6 @@ This API should not be considered stable yet. .. 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`. diff --git a/Include/cpython/object.h b/Include/cpython/object.h index af415da2e39448..3c2672528c8693 100644 --- a/Include/cpython/object.h +++ b/Include/cpython/object.h @@ -375,22 +375,41 @@ 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. - */ + +/* Immortal Objects + * + * An "immortal" object is one for which Py_DECREF() will never try + * to deallocate it. */ #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 + +/* The implementation-independent API is only the following functions: */ +PyAPI_FUNC(int) _PyObject_IsImmortal(PyObject *); +PyAPI_FUNC(void) _PyObject_SetImmortal(PyObject *); + +/* In the actual implementation we set the refcount to some positive + * value that we would never expect to be reachable through use of + * Py_INCREF() in a program. + * + * The only parts that should be used directly are the two + * _Py*Object_HEAD_IMMORTAL_INIT() macros. + */ + +/* _PyObject_IMMORTAL_BIT is the bit in the refcount value (Py_ssize_t) + * that we use to mark an object as immortal. It shouldn't ever be + * part of the public API. + * + * 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)) +/* _PyObject_IMMORTAL_INIT_REFCNT is the initial value we use for + * immortal objects. It shouldn't ever be part of the public API. */ #ifdef Py_IMMORTAL_CONST_REFCOUNTS #define _PyObject_IMMORTAL_INIT_REFCNT \ _PyObject_IMMORTAL_BIT @@ -400,14 +419,15 @@ PyAPI_FUNC(PyObject *) _PyObject_FunctionStr(PyObject *); (_PyObject_IMMORTAL_BIT + (_PyObject_IMMORTAL_BIT / 2)) #endif +/* These macros are drop-in replacements for the corresponding + * Py*Object_HEAD_INIT() macros. They will probably become + * part of the public API. */ #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 +#endif /* defined(_Py_IMMORTAL_OBJECTS) */ PyAPI_DATA(PyTypeObject) _PyNone_Type; From 00948a007b7316242e489bfdc7d2bd60dfa1505c Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Mon, 15 Mar 2021 10:33:17 -0600 Subject: [PATCH 13/16] Drop a holdover from immortal objects with negative refcounts. --- Objects/typeobject.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index c4b4af7705b391..9e7121448f87df 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -146,9 +146,7 @@ _PyType_CheckConsistency(PyTypeObject *type) return 1; } - if (!_PyObject_IsImmortal((PyObject *)type)) { - CHECK(Py_REFCNT(type) >= 1); - } + CHECK(Py_REFCNT(type) >= 1); CHECK(PyType_Check(type)); CHECK(!(type->tp_flags & Py_TPFLAGS_READYING)); From 00504d974d492b66619380419f749c1f37e31509 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Mon, 15 Mar 2021 10:44:24 -0600 Subject: [PATCH 14/16] Move _Py_SET_REFCNT() out of the header file. --- Include/object.h | 11 +++-------- Objects/object.c | 9 +++++++++ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/Include/object.h b/Include/object.h index 0f4794198337e8..c6563ffd00ae5b 100644 --- a/Include/object.h +++ b/Include/object.h @@ -141,14 +141,7 @@ static inline int _Py_IS_TYPE(const PyObject *ob, const PyTypeObject *type) { #define Py_IS_TYPE(ob, type) _Py_IS_TYPE(_PyObject_CAST_CONST(ob), type) -static inline int _py_is_immortal(PyObject *); // forward - -static inline void _Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt) { - if (_py_is_immortal((PyObject *)ob)) { - return; - } - ob->ob_refcnt = refcnt; -} +PyAPI_FUNC(void) _Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt); #define Py_SET_REFCNT(ob, refcnt) _Py_SET_REFCNT(_PyObject_CAST(ob), refcnt) @@ -432,6 +425,8 @@ PyAPI_FUNC(void) _Py_NegativeRefcount(const char *filename, int lineno, PyAPI_FUNC(void) _Py_Dealloc(PyObject *); +static inline int _py_is_immortal(PyObject *); // forward + static inline void _Py_INCREF(PyObject *op) { #ifdef Py_REF_DEBUG diff --git a/Objects/object.c b/Objects/object.c index 02358cc9f014d4..2ff92779d5ac8a 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -136,6 +136,15 @@ Py_DecRef(PyObject *o) Py_XDECREF(o); } +void +_Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt) { + if (_PyObject_IsImmortal(ob)) { + // XXX It may be worth emitting a warning here. + return; + } + ob->ob_refcnt = refcnt; +} + int _PyObject_IsImmortal(PyObject *ob) { From e6fc84f35290a8254e0e2eaecb502954cf0ab9a0 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Mon, 15 Mar 2021 10:53:06 -0600 Subject: [PATCH 15/16] Define _py_is_immortal() only for Py_IMMORTAL_CONST_REFCOUNTS. --- Include/object.h | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/Include/object.h b/Include/object.h index c6563ffd00ae5b..064b96d980fa51 100644 --- a/Include/object.h +++ b/Include/object.h @@ -425,7 +425,9 @@ PyAPI_FUNC(void) _Py_NegativeRefcount(const char *filename, int lineno, PyAPI_FUNC(void) _Py_Dealloc(PyObject *); +#ifdef Py_IMMORTAL_CONST_REFCOUNTS static inline int _py_is_immortal(PyObject *); // forward +#endif static inline void _Py_INCREF(PyObject *op) { @@ -691,19 +693,15 @@ times. #error "the immortal objects API is not available in the limited API" #endif -// This is a static version of _PyObject_IsImmortal(), for the sake -// of other static functions, like _Py_SET_REFCNT() and _Py_INCREF(). +#ifdef Py_IMMORTAL_CONST_REFCOUNTS +// We need this function since _PyObject_IMMORTAL_BIT is defined +// in cpython/object.h and not available for use above. Otherwise +// we wouldn't need this function. static inline int _py_is_immortal(PyObject *op) { -#ifdef Py_IMMORTAL_CONST_REFCOUNTS return (op->ob_refcnt & _PyObject_IMMORTAL_BIT) != 0; -#else -#ifndef _Py_IMMORTAL_OBJECTS - extern int _PyObject_IsImmortal(PyObject *); -#endif - return _PyObject_IsImmortal(op); -#endif } +#endif static inline int From f103804a1fb16d42afd91e420e8e66707ae7adc1 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Fri, 12 Mar 2021 11:21:05 -0700 Subject: [PATCH 16/16] Drop Py_IMMORTAL_CONST_REFCOUNTS and make immortal objects internal. --- Doc/c-api/refcounting.rst | 44 --------------- Doc/c-api/structures.rst | 34 ------------ Include/cpython/object.h | 54 ------------------- Include/internal/pycore_object.h | 49 +++++++++++++++++ Include/object.h | 26 --------- .../2021-03-11-13-03-15.bpo-40255._1LfiG.rst | 11 ++-- 6 files changed, 54 insertions(+), 164 deletions(-) diff --git a/Doc/c-api/refcounting.rst b/Doc/c-api/refcounting.rst index 22ecdcc99fb495..391907c8c2976a 100644 --- a/Doc/c-api/refcounting.rst +++ b/Doc/c-api/refcounting.rst @@ -119,47 +119,3 @@ 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 - -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. - diff --git a/Doc/c-api/structures.rst b/Doc/c-api/structures.rst index e0c3891cff3581..37072d30d57153 100644 --- a/Doc/c-api/structures.rst +++ b/Doc/c-api/structures.rst @@ -98,7 +98,6 @@ 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 @@ -136,39 +135,6 @@ 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 `. 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 `. 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 ---------------------------------- diff --git a/Include/cpython/object.h b/Include/cpython/object.h index 3c2672528c8693..58e4d2b11b93f9 100644 --- a/Include/cpython/object.h +++ b/Include/cpython/object.h @@ -376,60 +376,6 @@ PyAPI_FUNC(PyObject *) _PyObject_FunctionStr(PyObject *); } while (0) -/* Immortal Objects - * - * An "immortal" object is one for which Py_DECREF() will never try - * to deallocate it. */ - -#if defined(Py_IMMORTAL_CONST_REFCOUNTS) || defined(Py_BUILD_CORE) -#define _Py_IMMORTAL_OBJECTS 1 -#endif - -#ifdef _Py_IMMORTAL_OBJECTS - -/* The implementation-independent API is only the following functions: */ -PyAPI_FUNC(int) _PyObject_IsImmortal(PyObject *); -PyAPI_FUNC(void) _PyObject_SetImmortal(PyObject *); - -/* In the actual implementation we set the refcount to some positive - * value that we would never expect to be reachable through use of - * Py_INCREF() in a program. - * - * The only parts that should be used directly are the two - * _Py*Object_HEAD_IMMORTAL_INIT() macros. - */ - -/* _PyObject_IMMORTAL_BIT is the bit in the refcount value (Py_ssize_t) - * that we use to mark an object as immortal. It shouldn't ever be - * part of the public API. - * - * 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)) - -/* _PyObject_IMMORTAL_INIT_REFCNT is the initial value we use for - * immortal objects. It shouldn't ever be part of the public API. */ -#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 - -/* These macros are drop-in replacements for the corresponding - * Py*Object_HEAD_INIT() macros. They will probably become - * part of the public API. */ -#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 }, - -#endif /* defined(_Py_IMMORTAL_OBJECTS) */ - - PyAPI_DATA(PyTypeObject) _PyNone_Type; PyAPI_DATA(PyTypeObject) _PyNotImplemented_Type; diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h index 79c1c44ae72d60..001be56fab8207 100644 --- a/Include/internal/pycore_object.h +++ b/Include/internal/pycore_object.h @@ -30,6 +30,55 @@ _PyType_HasFeature(PyTypeObject *type, unsigned long feature) { extern void _PyType_InitCache(PyInterpreterState *interp); +/* Immortal Objects + * + * An "immortal" object is one for which Py_DECREF() will never try + * to deallocate it. + * + * At the moment this API is strictly internal. However, if it proves + * helpful for extension authors we may move it to the public API. */ + +#define _Py_IMMORTAL_OBJECTS 1 + +/* The implementation-independent API is only the following functions: */ +PyAPI_FUNC(int) _PyObject_IsImmortal(PyObject *); +PyAPI_FUNC(void) _PyObject_SetImmortal(PyObject *); + +/* In the actual implementation we set the refcount to some positive + * value that we would never expect to be reachable through use of + * Py_INCREF() in a program. + * + * The only parts that should be used directly are the two + * _Py*Object_HEAD_IMMORTAL_INIT() macros. + */ + +/* _PyObject_IMMORTAL_BIT is the bit in the refcount value (Py_ssize_t) + * that we use to mark an object as immortal. It shouldn't ever be + * part of the public API. + * + * 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)) + +/* _PyObject_IMMORTAL_INIT_REFCNT is the initial value we use for + * immortal objects. It shouldn't ever be part of the public API. + * + * We leave plenty of room to preserve _PyObject_IMMORTAL_BIT. */ +#define _PyObject_IMMORTAL_INIT_REFCNT \ + (_PyObject_IMMORTAL_BIT + (_PyObject_IMMORTAL_BIT / 2)) + +/* These macros are drop-in replacements for the corresponding + * Py*Object_HEAD_INIT() macros. They will probably become + * part of the public API. */ +#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 }, + +/* end Immortal Objects */ + + /* Inline functions trading binary compatibility for speed: _PyObject_Init() is the fast version of PyObject_Init(), and _PyObject_InitVar() is the fast version of PyObject_InitVar(). diff --git a/Include/object.h b/Include/object.h index 064b96d980fa51..c2f626bf4f93b0 100644 --- a/Include/object.h +++ b/Include/object.h @@ -433,11 +433,6 @@ 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++; } @@ -451,11 +446,6 @@ 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 @@ -688,22 +678,6 @@ times. #endif -#if defined(Py_LIMITED_API) && \ - (defined(_Py_IMMORTAL_OBJECTS) || defined(Py_IMMORTAL_CONST_REFCOUNTS)) -#error "the immortal objects API is not available in the limited API" -#endif - -#ifdef Py_IMMORTAL_CONST_REFCOUNTS -// We need this function since _PyObject_IMMORTAL_BIT is defined -// in cpython/object.h and not available for use above. Otherwise -// we wouldn't need this function. -static inline int _py_is_immortal(PyObject *op) -{ - return (op->ob_refcnt & _PyObject_IMMORTAL_BIT) != 0; -} -#endif - - static inline int PyType_HasFeature(PyTypeObject *type, unsigned long feature) { diff --git a/Misc/NEWS.d/next/Core and Builtins/2021-03-11-13-03-15.bpo-40255._1LfiG.rst b/Misc/NEWS.d/next/Core and Builtins/2021-03-11-13-03-15.bpo-40255._1LfiG.rst index 4986d612704475..d7236aa94278ff 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2021-03-11-13-03-15.bpo-40255._1LfiG.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2021-03-11-13-03-15.bpo-40255._1LfiG.rst @@ -1,6 +1,5 @@ -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. +There is now internal support for "immortal" objects (with a "private" +C-API). Those are objects that will never be deleted, like the +singletons and static types. This will benefit the C-API and +subinterpreters. For now the API is currently intended only +for internal use.