https://github.com/python/cpython/commit/f30376c650c40e0819edd9981e45f8ba206fdd83
commit: f30376c650c40e0819edd9981e45f8ba206fdd83
branch: main
author: Mark Shannon <m...@hotpy.org>
committer: markshannon <m...@hotpy.org>
date: 2025-03-12T16:54:10Z
summary:

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

files:
M Include/internal/pycore_object.h
M Include/refcount.h
M Objects/unicodeobject.c

diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h
index 1c4c3e30fd2f6d..0b686b416ec193 100644
--- a/Include/internal/pycore_object.h
+++ b/Include/internal/pycore_object.h
@@ -135,15 +135,20 @@ static inline void _Py_RefcntAdd(PyObject* op, Py_ssize_t 
n)
         _Py_INCREF_IMMORTAL_STAT_INC();
         return;
     }
-#ifdef Py_REF_DEBUG
-    _Py_AddRefTotal(_PyThreadState_GET(), n);
-#endif
-#if !defined(Py_GIL_DISABLED)
-#if SIZEOF_VOID_P > 4
-    op->ob_refcnt += (PY_UINT32_T)n;
-#else
-    op->ob_refcnt += n;
-#endif
+#ifndef Py_GIL_DISABLED
+    Py_ssize_t refcnt = _Py_REFCNT(op);
+    Py_ssize_t new_refcnt = refcnt + n;
+    if (new_refcnt >= (Py_ssize_t)_Py_IMMORTAL_MINIMUM_REFCNT) {
+        new_refcnt = _Py_IMMORTAL_INITIAL_REFCNT;
+    }
+#  if SIZEOF_VOID_P > 4
+    op->ob_refcnt = (PY_UINT32_T)new_refcnt;
+#  else
+    op->ob_refcnt = new_refcnt;
+#  endif
+#  ifdef Py_REF_DEBUG
+    _Py_AddRefTotal(_PyThreadState_GET(), new_refcnt - refcnt);
+#  endif
 #else
     if (_Py_IsOwnedByCurrentThread(op)) {
         uint32_t local = op->ob_ref_local;
@@ -160,6 +165,9 @@ static inline void _Py_RefcntAdd(PyObject* op, Py_ssize_t n)
     else {
         _Py_atomic_add_ssize(&op->ob_ref_shared, (n << _Py_REF_SHARED_SHIFT));
     }
+#  ifdef Py_REF_DEBUG
+    _Py_AddRefTotal(_PyThreadState_GET(), n);
+#  endif
 #endif
     // 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
diff --git a/Include/refcount.h b/Include/refcount.h
index e66e4aaace3140..ba14bc6965ce3e 100644
--- a/Include/refcount.h
+++ b/Include/refcount.h
@@ -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.
 
 */
-#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_IMMORTAL_INITIAL_REFCNT 
(((Py_ssize_t)_Py_IMMORTAL_INITIAL_REFCNT) | (_Py_STATIC_FLAG_BITS << 48))
 
diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c
index 21ccb01f86bc61..085944cd6bd989 100644
--- a/Objects/unicodeobject.c
+++ b/Objects/unicodeobject.c
@@ -15982,7 +15982,7 @@ _PyUnicode_ClearInterned(PyInterpreterState *interp)
         case SSTATE_INTERNED_MORTAL:
             // Restore 2 references held by the interned dict; these will
             // 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
             /* let's be pedantic with the ref total */
             _Py_IncRefTotal(_PyThreadState_GET());

_______________________________________________
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: arch...@mail-archive.com

Reply via email to