GH-127705: Fix _Py_RefcntAdd to handle objects becoming immortal (GH-131140)

This commit is contained in:
Mark Shannon 2025-03-12 16:54:10 +00:00 committed by GitHub
parent 061da44bac
commit f30376c650
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 20 additions and 11 deletions

View file

@ -135,15 +135,20 @@ static inline void _Py_RefcntAdd(PyObject* op, Py_ssize_t n)
_Py_INCREF_IMMORTAL_STAT_INC(); _Py_INCREF_IMMORTAL_STAT_INC();
return; return;
} }
#ifdef Py_REF_DEBUG #ifndef Py_GIL_DISABLED
_Py_AddRefTotal(_PyThreadState_GET(), n); Py_ssize_t refcnt = _Py_REFCNT(op);
#endif Py_ssize_t new_refcnt = refcnt + n;
#if !defined(Py_GIL_DISABLED) if (new_refcnt >= (Py_ssize_t)_Py_IMMORTAL_MINIMUM_REFCNT) {
#if SIZEOF_VOID_P > 4 new_refcnt = _Py_IMMORTAL_INITIAL_REFCNT;
op->ob_refcnt += (PY_UINT32_T)n; }
#else # if SIZEOF_VOID_P > 4
op->ob_refcnt += n; op->ob_refcnt = (PY_UINT32_T)new_refcnt;
#endif # else
op->ob_refcnt = new_refcnt;
# endif
# ifdef Py_REF_DEBUG
_Py_AddRefTotal(_PyThreadState_GET(), new_refcnt - refcnt);
# endif
#else #else
if (_Py_IsOwnedByCurrentThread(op)) { if (_Py_IsOwnedByCurrentThread(op)) {
uint32_t local = op->ob_ref_local; uint32_t local = op->ob_ref_local;
@ -160,6 +165,9 @@ static inline void _Py_RefcntAdd(PyObject* op, Py_ssize_t n)
else { else {
_Py_atomic_add_ssize(&op->ob_ref_shared, (n << _Py_REF_SHARED_SHIFT)); _Py_atomic_add_ssize(&op->ob_ref_shared, (n << _Py_REF_SHARED_SHIFT));
} }
# ifdef Py_REF_DEBUG
_Py_AddRefTotal(_PyThreadState_GET(), n);
# endif
#endif #endif
// Although the ref count was increased by `n` (which may be greater than 1) // Although the ref count was increased by `n` (which may be greater than 1)
// it is only a single increment (i.e. addition) operation, so only 1 refcnt // it is only a single increment (i.e. addition) operation, so only 1 refcnt

View file

@ -42,7 +42,8 @@ beyond the refcount limit. Immortality checks for reference count decreases will
be done by checking the bit sign flag in the lower 32 bits. be done by checking the bit sign flag in the lower 32 bits.
*/ */
#define _Py_IMMORTAL_INITIAL_REFCNT (3UL << 30) #define _Py_IMMORTAL_INITIAL_REFCNT (3ULL << 30)
#define _Py_IMMORTAL_MINIMUM_REFCNT (1ULL << 31)
#define _Py_STATIC_FLAG_BITS ((Py_ssize_t)(_Py_STATICALLY_ALLOCATED_FLAG | _Py_IMMORTAL_FLAGS)) #define _Py_STATIC_FLAG_BITS ((Py_ssize_t)(_Py_STATICALLY_ALLOCATED_FLAG | _Py_IMMORTAL_FLAGS))
#define _Py_STATIC_IMMORTAL_INITIAL_REFCNT (((Py_ssize_t)_Py_IMMORTAL_INITIAL_REFCNT) | (_Py_STATIC_FLAG_BITS << 48)) #define _Py_STATIC_IMMORTAL_INITIAL_REFCNT (((Py_ssize_t)_Py_IMMORTAL_INITIAL_REFCNT) | (_Py_STATIC_FLAG_BITS << 48))

View file

@ -15982,7 +15982,7 @@ _PyUnicode_ClearInterned(PyInterpreterState *interp)
case SSTATE_INTERNED_MORTAL: case SSTATE_INTERNED_MORTAL:
// Restore 2 references held by the interned dict; these will // Restore 2 references held by the interned dict; these will
// be decref'd by clear_interned_dict's PyDict_Clear. // be decref'd by clear_interned_dict's PyDict_Clear.
Py_SET_REFCNT(s, Py_REFCNT(s) + 2); _Py_RefcntAdd(s, 2);
#ifdef Py_REF_DEBUG #ifdef Py_REF_DEBUG
/* let's be pedantic with the ref total */ /* let's be pedantic with the ref total */
_Py_IncRefTotal(_PyThreadState_GET()); _Py_IncRefTotal(_PyThreadState_GET());