@@ -21,35 +21,41 @@ cleanup during runtime finalization.
2121
2222#if SIZEOF_VOID_P > 4
2323/*
24- In 64+ bit systems, an object will be marked as immortal by setting all of the
25- lower 32 bits of the reference count field, which is equal to: 0xFFFFFFFF
24+ In 64+ bit systems, any object whose 32 bit reference count is >= 2**31
25+ will be treated as immortal.
2626
2727Using the lower 32 bits makes the value backwards compatible by allowing
2828C-Extensions without the updated checks in Py_INCREF and Py_DECREF to safely
29- increase and decrease the objects reference count. The object would lose its
30- immortality, but the execution would still be correct.
29+ increase and decrease the objects reference count.
30+
31+ In order to offer sufficient resilience to C extensions using the stable ABI
32+ compiled against 3.11 or earlier, we set the initial value near the
33+ middle of the range (2**31, 2**32). That way the the refcount can be
34+ off by ~1 billion without affecting immortality.
3135
3236Reference count increases will use saturated arithmetic, taking advantage of
3337having all the lower 32 bits set, which will avoid the reference count to go
3438beyond the refcount limit. Immortality checks for reference count decreases will
3539be done by checking the bit sign flag in the lower 32 bits.
40+
3641*/
37- #define _Py_IMMORTAL_REFCNT _Py_CAST( Py_ssize_t, UINT_MAX )
42+ #define _Py_IMMORTAL_INITIAL_REFCNT (( Py_ssize_t)(3UL << 30) )
3843
3944#else
4045/*
41- In 32 bit systems, an object will be marked as immortal by setting all of the
42- lower 30 bits of the reference count field, which is equal to: 0x3FFFFFFF
46+ In 32 bit systems, an object will be treated as immortal if its reference
47+ count equals or exceeds _Py_IMMORTAL_MINIMUM_REFCNT (2**30).
4348
4449Using the lower 30 bits makes the value backwards compatible by allowing
4550C-Extensions without the updated checks in Py_INCREF and Py_DECREF to safely
4651increase and decrease the objects reference count. The object would lose its
4752immortality, but the execution would still be correct.
4853
4954Reference count increases and decreases will first go through an immortality
50- check by comparing the reference count field to the immortality reference count .
55+ check by comparing the reference count field to the minimum immortality refcount .
5156*/
52- #define _Py_IMMORTAL_REFCNT _Py_CAST(Py_ssize_t, UINT_MAX >> 2)
57+ #define _Py_IMMORTAL_INITIAL_REFCNT ((Py_ssize_t)(3L << 29))
58+ #define _Py_IMMORTAL_MINIMUM_REFCNT ((Py_ssize_t)(1L << 30))
5359#endif
5460
5561// Py_GIL_DISABLED builds indicate immortal objects using `ob_ref_local`, which is
@@ -90,7 +96,7 @@ PyAPI_FUNC(Py_ssize_t) Py_REFCNT(PyObject *ob);
9096 #else
9197 uint32_t local = _Py_atomic_load_uint32_relaxed (& ob -> ob_ref_local );
9298 if (local == _Py_IMMORTAL_REFCNT_LOCAL ) {
93- return _Py_IMMORTAL_REFCNT ;
99+ return _Py_IMMORTAL_INITIAL_REFCNT ;
94100 }
95101 Py_ssize_t shared = _Py_atomic_load_ssize_relaxed (& ob -> ob_ref_shared );
96102 return _Py_STATIC_CAST (Py_ssize_t , local ) +
@@ -109,9 +115,9 @@ static inline Py_ALWAYS_INLINE int _Py_IsImmortal(PyObject *op)
109115 return (_Py_atomic_load_uint32_relaxed (& op -> ob_ref_local ) ==
110116 _Py_IMMORTAL_REFCNT_LOCAL );
111117#elif SIZEOF_VOID_P > 4
112- return ( _Py_CAST (PY_INT32_T , op -> ob_refcnt ) < 0 ) ;
118+ return _Py_CAST (PY_INT32_T , op -> ob_refcnt ) < 0 ;
113119#else
114- return ( op -> ob_refcnt == _Py_IMMORTAL_REFCNT ) ;
120+ return op -> ob_refcnt >= _Py_IMMORTAL_MINIMUM_REFCNT ;
115121#endif
116122}
117123#define _Py_IsImmortal (op ) _Py_IsImmortal(_PyObject_CAST(op))
@@ -236,7 +242,7 @@ static inline Py_ALWAYS_INLINE void Py_INCREF(PyObject *op)
236242 uint32_t new_local = local + 1 ;
237243 if (new_local == 0 ) {
238244 _Py_INCREF_IMMORTAL_STAT_INC ();
239- // local is equal to _Py_IMMORTAL_REFCNT : do nothing
245+ // local is equal to _Py_IMMORTAL_REFCNT_LOCAL : do nothing
240246 return ;
241247 }
242248 if (_Py_IsOwnedByCurrentThread (op )) {
@@ -246,18 +252,14 @@ static inline Py_ALWAYS_INLINE void Py_INCREF(PyObject *op)
246252 _Py_atomic_add_ssize (& op -> ob_ref_shared , (1 << _Py_REF_SHARED_SHIFT ));
247253 }
248254#elif SIZEOF_VOID_P > 4
249- // Portable saturated add, branching on the carry flag and set low bits
250255 PY_UINT32_T cur_refcnt = op -> ob_refcnt_split [PY_BIG_ENDIAN ];
251- PY_UINT32_T new_refcnt = cur_refcnt + 1 ;
252- if ( new_refcnt == 0 ) {
256+ if ((( int32_t ) cur_refcnt ) < 0 ) {
257+ // the object is immortal
253258 _Py_INCREF_IMMORTAL_STAT_INC ();
254- // cur_refcnt is equal to _Py_IMMORTAL_REFCNT: the object is immortal,
255- // do nothing
256259 return ;
257260 }
258- op -> ob_refcnt_split [PY_BIG_ENDIAN ] = new_refcnt ;
261+ op -> ob_refcnt_split [PY_BIG_ENDIAN ] = cur_refcnt + 1 ;
259262#else
260- // Explicitly check immortality against the immortal value
261263 if (_Py_IsImmortal (op )) {
262264 _Py_INCREF_IMMORTAL_STAT_INC ();
263265 return ;
0 commit comments