Skip to content

Commit b915df3

Browse files
committed
Fix _Py_RefcntAdd to handle objects becoming immortal
1 parent 9d759b6 commit b915df3

File tree

3 files changed

+10
-26
lines changed

3 files changed

+10
-26
lines changed

Include/internal/pycore_object.h

Lines changed: 7 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -135,31 +135,14 @@ static inline void _Py_RefcntAdd(PyObject* op, Py_ssize_t n)
135135
_Py_INCREF_IMMORTAL_STAT_INC();
136136
return;
137137
}
138-
#ifdef Py_REF_DEBUG
139-
_Py_AddRefTotal(_PyThreadState_GET(), n);
140-
#endif
141-
#if !defined(Py_GIL_DISABLED)
142-
#if SIZEOF_VOID_P > 4
143-
op->ob_refcnt += (PY_UINT32_T)n;
144-
#else
145-
op->ob_refcnt += n;
146-
#endif
147-
#else
148-
if (_Py_IsOwnedByCurrentThread(op)) {
149-
uint32_t local = op->ob_ref_local;
150-
Py_ssize_t refcnt = (Py_ssize_t)local + n;
151-
# if PY_SSIZE_T_MAX > UINT32_MAX
152-
if (refcnt > (Py_ssize_t)UINT32_MAX) {
153-
// Make the object immortal if the 32-bit local reference count
154-
// would overflow.
155-
refcnt = _Py_IMMORTAL_REFCNT_LOCAL;
156-
}
157-
# endif
158-
_Py_atomic_store_uint32_relaxed(&op->ob_ref_local, (uint32_t)refcnt);
159-
}
160-
else {
161-
_Py_atomic_add_ssize(&op->ob_ref_shared, (n << _Py_REF_SHARED_SHIFT));
138+
Py_ssize_t refcnt = _Py_REFCNT(op);
139+
Py_ssize_t new_refcnt = refcnt + n;
140+
if (new_refcnt >= (Py_ssize_t)_Py_IMMORTAL_MINIMUM_REFCNT) {
141+
new_refcnt = _Py_IMMORTAL_INITIAL_REFCNT;
162142
}
143+
Py_SET_REFCNT(op, new_refcnt);
144+
#ifdef Py_REF_DEBUG
145+
_Py_AddRefTotal(_PyThreadState_GET(), new_refcnt - refcnt);
163146
#endif
164147
// Although the ref count was increased by `n` (which may be greater than 1)
165148
// it is only a single increment (i.e. addition) operation, so only 1 refcnt

Include/refcount.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ beyond the refcount limit. Immortality checks for reference count decreases will
4242
be done by checking the bit sign flag in the lower 32 bits.
4343
4444
*/
45-
#define _Py_IMMORTAL_INITIAL_REFCNT (3UL << 30)
45+
#define _Py_IMMORTAL_INITIAL_REFCNT (3ULL << 30)
46+
#define _Py_IMMORTAL_MINIMUM_REFCNT (1ULL << 31)
4647
#define _Py_STATIC_FLAG_BITS ((Py_ssize_t)(_Py_STATICALLY_ALLOCATED_FLAG | _Py_IMMORTAL_FLAGS))
4748
#define _Py_STATIC_IMMORTAL_INITIAL_REFCNT (((Py_ssize_t)_Py_IMMORTAL_INITIAL_REFCNT) | (_Py_STATIC_FLAG_BITS << 48))
4849

Objects/unicodeobject.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15982,7 +15982,7 @@ _PyUnicode_ClearInterned(PyInterpreterState *interp)
1598215982
case SSTATE_INTERNED_MORTAL:
1598315983
// Restore 2 references held by the interned dict; these will
1598415984
// be decref'd by clear_interned_dict's PyDict_Clear.
15985-
Py_SET_REFCNT(s, Py_REFCNT(s) + 2);
15985+
_Py_RefcntAdd(s, 2);
1598615986
#ifdef Py_REF_DEBUG
1598715987
/* let's be pedantic with the ref total */
1598815988
_Py_IncRefTotal(_PyThreadState_GET());

0 commit comments

Comments
 (0)