From cbb0aa71d040022db61390380b8aebc7c04f3275 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 8 Mar 2023 12:03:50 -0700 Subject: gh-102304: Consolidate Direct Usage of _Py_RefTotal (gh-102514) This simplifies further changes to _Py_RefTotal (e.g. make it atomic or move it to PyInterpreterState). https://github.com/python/cpython/issues/102304 --- Include/cpython/object.h | 1 + Include/internal/pycore_object.h | 23 ++++++++++-- Include/object.h | 25 +++++++++++-- Modules/_testcapimodule.c | 7 +--- Objects/bytesobject.c | 9 ++--- Objects/dictobject.c | 10 ++--- Objects/object.c | 79 ++++++++++++++++++++++++++++++++++------ Objects/structseq.c | 2 +- Objects/tupleobject.c | 9 ++--- Objects/unicodeobject.c | 7 +--- 10 files changed, 127 insertions(+), 45 deletions(-) diff --git a/Include/cpython/object.h b/Include/cpython/object.h index 3f26f24..7b687d3 100644 --- a/Include/cpython/object.h +++ b/Include/cpython/object.h @@ -3,6 +3,7 @@ #endif PyAPI_FUNC(void) _Py_NewReference(PyObject *op); +PyAPI_FUNC(void) _Py_NewReferenceNoTotal(PyObject *op); #ifdef Py_TRACE_REFS /* Py_TRACE_REFS is such major surgery that we call external routines. */ diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h index 8796dfe..e15685f 100644 --- a/Include/internal/pycore_object.h +++ b/Include/internal/pycore_object.h @@ -37,11 +37,23 @@ PyAPI_FUNC(void) _Py_NO_RETURN _Py_FatalRefcountErrorFunc( #define _Py_FatalRefcountError(message) \ _Py_FatalRefcountErrorFunc(__func__, (message)) + +#ifdef Py_REF_DEBUG +/* The symbol is only exposed in the API for the sake of extensions + built against the pre-3.12 stable ABI. */ +PyAPI_DATA(Py_ssize_t) _Py_RefTotal; + +extern void _Py_AddRefTotal(Py_ssize_t); +extern void _Py_IncRefTotal(void); +extern void _Py_DecRefTotal(void); +# define _Py_DEC_REFTOTAL() _Py_RefTotal-- +#endif + // Increment reference count by n static inline void _Py_RefcntAdd(PyObject* op, Py_ssize_t n) { #ifdef Py_REF_DEBUG - _Py_RefTotal += n; + _Py_AddRefTotal(n); #endif op->ob_refcnt += n; } @@ -52,7 +64,7 @@ _Py_DECREF_SPECIALIZED(PyObject *op, const destructor destruct) { _Py_DECREF_STAT_INC(); #ifdef Py_REF_DEBUG - _Py_RefTotal--; + _Py_DEC_REFTOTAL(); #endif if (--op->ob_refcnt != 0) { assert(op->ob_refcnt > 0); @@ -70,7 +82,7 @@ _Py_DECREF_NO_DEALLOC(PyObject *op) { _Py_DECREF_STAT_INC(); #ifdef Py_REF_DEBUG - _Py_RefTotal--; + _Py_DEC_REFTOTAL(); #endif op->ob_refcnt--; #ifdef Py_DEBUG @@ -80,6 +92,11 @@ _Py_DECREF_NO_DEALLOC(PyObject *op) #endif } +#ifdef Py_REF_DEBUG +# undef _Py_DEC_REFTOTAL +#endif + + PyAPI_FUNC(int) _PyType_CheckConsistency(PyTypeObject *type); PyAPI_FUNC(int) _PyDict_CheckConsistency(PyObject *mp, int check_content); diff --git a/Include/object.h b/Include/object.h index 3774f12..844b9c4 100644 --- a/Include/object.h +++ b/Include/object.h @@ -490,7 +490,21 @@ you can count such references to the type object.) */ #ifdef Py_REF_DEBUG -PyAPI_DATA(Py_ssize_t) _Py_RefTotal; +# if defined(Py_LIMITED_API) && Py_LIMITED_API+0 < 0x030A0000 +extern Py_ssize_t _Py_RefTotal; +# define _Py_INC_REFTOTAL() _Py_RefTotal++ +# define _Py_DEC_REFTOTAL() _Py_RefTotal-- +# elif defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) +extern void _Py_IncRefTotal(void); +extern void _Py_DecRefTotal(void); +# define _Py_INC_REFTOTAL() _Py_IncRefTotal() +# define _Py_DEC_REFTOTAL() _Py_DecRefTotal() +# elif !defined(Py_LIMITED_API) || Py_LIMITED_API+0 > 0x030C0000 +extern void _Py_IncRefTotal_DO_NOT_USE_THIS(void); +extern void _Py_DecRefTotal_DO_NOT_USE_THIS(void); +# define _Py_INC_REFTOTAL() _Py_IncRefTotal_DO_NOT_USE_THIS() +# define _Py_DEC_REFTOTAL() _Py_DecRefTotal_DO_NOT_USE_THIS() +# endif PyAPI_FUNC(void) _Py_NegativeRefcount(const char *filename, int lineno, PyObject *op); #endif /* Py_REF_DEBUG */ @@ -519,8 +533,8 @@ static inline void Py_INCREF(PyObject *op) // Non-limited C API and limited C API for Python 3.9 and older access // directly PyObject.ob_refcnt. #ifdef Py_REF_DEBUG - _Py_RefTotal++; -#endif + _Py_INC_REFTOTAL(); +#endif // Py_REF_DEBUG op->ob_refcnt++; #endif } @@ -539,7 +553,7 @@ static inline void Py_DECREF(PyObject *op) { static inline void Py_DECREF(const char *filename, int lineno, PyObject *op) { _Py_DECREF_STAT_INC(); - _Py_RefTotal--; + _Py_DEC_REFTOTAL(); if (--op->ob_refcnt != 0) { if (op->ob_refcnt < 0) { _Py_NegativeRefcount(filename, lineno, op); @@ -564,6 +578,9 @@ static inline void Py_DECREF(PyObject *op) #define Py_DECREF(op) Py_DECREF(_PyObject_CAST(op)) #endif +#undef _Py_INC_REFTOTAL +#undef _Py_DEC_REFTOTAL + /* Safely decref `op` and set `op` to NULL, especially useful in tp_clear * and tp_dealloc implementations. diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 10e507d..ea67017 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -1654,15 +1654,10 @@ slot_tp_del(PyObject *self) */ { Py_ssize_t refcnt = Py_REFCNT(self); - _Py_NewReference(self); + _Py_NewReferenceNoTotal(self); Py_SET_REFCNT(self, refcnt); } assert(!PyType_IS_GC(Py_TYPE(self)) || PyObject_GC_IsTracked(self)); - /* If Py_REF_DEBUG macro is defined, _Py_NewReference() increased - _Py_RefTotal, so we need to undo that. */ -#ifdef Py_REF_DEBUG - _Py_RefTotal--; -#endif } static PyObject * diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c index 657443f..687a654 100644 --- a/Objects/bytesobject.c +++ b/Objects/bytesobject.c @@ -3060,21 +3060,20 @@ _PyBytes_Resize(PyObject **pv, Py_ssize_t newsize) Py_DECREF(v); return 0; } - /* XXX UNREF/NEWREF interface should be more symmetrical */ -#ifdef Py_REF_DEBUG - _Py_RefTotal--; -#endif #ifdef Py_TRACE_REFS _Py_ForgetReference(v); #endif *pv = (PyObject *) PyObject_Realloc(v, PyBytesObject_SIZE + newsize); if (*pv == NULL) { +#ifdef Py_REF_DEBUG + _Py_DecRefTotal(); +#endif PyObject_Free(v); PyErr_NoMemory(); return -1; } - _Py_NewReference(*pv); + _Py_NewReferenceNoTotal(*pv); sv = (PyBytesObject *) *pv; Py_SET_SIZE(sv, newsize); sv->ob_sval[newsize] = '\0'; diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 75c9217..a60f275 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -303,7 +303,7 @@ static inline void dictkeys_incref(PyDictKeysObject *dk) { #ifdef Py_REF_DEBUG - _Py_RefTotal++; + _Py_IncRefTotal(); #endif dk->dk_refcnt++; } @@ -313,7 +313,7 @@ dictkeys_decref(PyDictKeysObject *dk) { assert(dk->dk_refcnt > 0); #ifdef Py_REF_DEBUG - _Py_RefTotal--; + _Py_DecRefTotal(); #endif if (--dk->dk_refcnt == 0) { free_keys_object(dk); @@ -633,7 +633,7 @@ new_keys_object(uint8_t log2_size, bool unicode) } } #ifdef Py_REF_DEBUG - _Py_RefTotal++; + _Py_IncRefTotal(); #endif dk->dk_refcnt = 1; dk->dk_log2_size = log2_size; @@ -821,7 +821,7 @@ clone_combined_dict_keys(PyDictObject *orig) we have it now; calling dictkeys_incref would be an error as keys->dk_refcnt is already set to 1 (after memcpy). */ #ifdef Py_REF_DEBUG - _Py_RefTotal++; + _Py_IncRefTotal(); #endif return keys; } @@ -1520,7 +1520,7 @@ dictresize(PyDictObject *mp, uint8_t log2_newsize, int unicode) // We can not use free_keys_object here because key's reference // are moved already. #ifdef Py_REF_DEBUG - _Py_RefTotal--; + _Py_DecRefTotal(); #endif if (oldkeys == Py_EMPTY_KEYS) { oldkeys->dk_refcnt--; diff --git a/Objects/object.c b/Objects/object.c index 5db2b6a..38da4d4 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -56,6 +56,24 @@ _PyObject_CheckConsistency(PyObject *op, int check_content) #ifdef Py_REF_DEBUG Py_ssize_t _Py_RefTotal; +static inline void +reftotal_increment(void) +{ + _Py_RefTotal++; +} + +static inline void +reftotal_decrement(void) +{ + _Py_RefTotal--; +} + +void +_Py_AddRefTotal(Py_ssize_t n) +{ + _Py_RefTotal += n; +} + Py_ssize_t _Py_GetRefTotal(void) { @@ -121,6 +139,32 @@ _Py_NegativeRefcount(const char *filename, int lineno, PyObject *op) filename, lineno, __func__); } +/* This is exposed strictly for use in Py_INCREF(). */ +PyAPI_FUNC(void) +_Py_IncRefTotal_DO_NOT_USE_THIS(void) +{ + reftotal_increment(); +} + +/* This is exposed strictly for use in Py_DECREF(). */ +PyAPI_FUNC(void) +_Py_DecRefTotal_DO_NOT_USE_THIS(void) +{ + reftotal_decrement(); +} + +void +_Py_IncRefTotal(void) +{ + reftotal_increment(); +} + +void +_Py_DecRefTotal(void) +{ + reftotal_decrement(); +} + #endif /* Py_REF_DEBUG */ void @@ -138,12 +182,18 @@ Py_DecRef(PyObject *o) void _Py_IncRef(PyObject *o) { +#ifdef Py_REF_DEBUG + reftotal_increment(); +#endif Py_INCREF(o); } void _Py_DecRef(PyObject *o) { +#ifdef Py_REF_DEBUG + reftotal_decrement(); +#endif Py_DECREF(o); } @@ -238,17 +288,12 @@ PyObject_CallFinalizerFromDealloc(PyObject *self) /* tp_finalize resurrected it! Make it look like the original Py_DECREF * never happened. */ Py_ssize_t refcnt = Py_REFCNT(self); - _Py_NewReference(self); + _Py_NewReferenceNoTotal(self); Py_SET_REFCNT(self, refcnt); _PyObject_ASSERT(self, (!_PyType_IS_GC(Py_TYPE(self)) || _PyObject_GC_IS_TRACKED(self))); - /* If Py_REF_DEBUG macro is defined, _Py_NewReference() increased - _Py_RefTotal, so we need to undo that. */ -#ifdef Py_REF_DEBUG - _Py_RefTotal--; -#endif return -1; } @@ -2010,21 +2055,33 @@ _PyTypes_FiniTypes(PyInterpreterState *interp) } -void -_Py_NewReference(PyObject *op) +static inline void +new_reference(PyObject *op) { if (_PyRuntime.tracemalloc.config.tracing) { _PyTraceMalloc_NewReference(op); } -#ifdef Py_REF_DEBUG - _Py_RefTotal++; -#endif Py_SET_REFCNT(op, 1); #ifdef Py_TRACE_REFS _Py_AddToAllObjects(op, 1); #endif } +void +_Py_NewReference(PyObject *op) +{ +#ifdef Py_REF_DEBUG + reftotal_increment(); +#endif + new_reference(op); +} + +void +_Py_NewReferenceNoTotal(PyObject *op) +{ + new_reference(op); +} + #ifdef Py_TRACE_REFS void diff --git a/Objects/structseq.c b/Objects/structseq.c index 100ccfe..c20962e 100644 --- a/Objects/structseq.c +++ b/Objects/structseq.c @@ -592,7 +592,7 @@ _PyStructSequence_FiniType(PyTypeObject *type) // Don't use Py_DECREF(): static type must not be deallocated Py_SET_REFCNT(type, 0); #ifdef Py_REF_DEBUG - _Py_RefTotal--; + _Py_DecRefTotal(); #endif // Make sure that _PyStructSequence_InitType() will initialize diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c index 6ee93ab..59c0251 100644 --- a/Objects/tupleobject.c +++ b/Objects/tupleobject.c @@ -930,10 +930,6 @@ _PyTuple_Resize(PyObject **pv, Py_ssize_t newsize) return *pv == NULL ? -1 : 0; } - /* XXX UNREF/NEWREF interface should be more symmetrical */ -#ifdef Py_REF_DEBUG - _Py_RefTotal--; -#endif if (_PyObject_GC_IS_TRACKED(v)) { _PyObject_GC_UNTRACK(v); } @@ -947,10 +943,13 @@ _PyTuple_Resize(PyObject **pv, Py_ssize_t newsize) sv = PyObject_GC_Resize(PyTupleObject, v, newsize); if (sv == NULL) { *pv = NULL; +#ifdef Py_REF_DEBUG + _Py_DecRefTotal(); +#endif PyObject_GC_Del(v); return -1; } - _Py_NewReference((PyObject *) sv); + _Py_NewReferenceNoTotal((PyObject *) sv); /* Zero out items added by growing */ if (newsize > oldsize) memset(&sv->ob_item[oldsize], 0, diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 1ba3042..2d50f9c 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -947,21 +947,18 @@ resize_compact(PyObject *unicode, Py_ssize_t length) _PyUnicode_UTF8(unicode) = NULL; _PyUnicode_UTF8_LENGTH(unicode) = 0; } -#ifdef Py_REF_DEBUG - _Py_RefTotal--; -#endif #ifdef Py_TRACE_REFS _Py_ForgetReference(unicode); #endif new_unicode = (PyObject *)PyObject_Realloc(unicode, new_size); if (new_unicode == NULL) { - _Py_NewReference(unicode); + _Py_NewReferenceNoTotal(unicode); PyErr_NoMemory(); return NULL; } unicode = new_unicode; - _Py_NewReference(unicode); + _Py_NewReferenceNoTotal(unicode); _PyUnicode_LENGTH(unicode) = length; #ifdef Py_DEBUG -- cgit v0.12