Skip to content

Commit cbb0aa7

Browse files
pythongh-102304: Consolidate Direct Usage of _Py_RefTotal (pythongh-102514)
This simplifies further changes to _Py_RefTotal (e.g. make it atomic or move it to PyInterpreterState). python#102304
1 parent 11a2c6c commit cbb0aa7

File tree

10 files changed

+127
-45
lines changed

10 files changed

+127
-45
lines changed

Include/cpython/object.h

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#endif
44

55
PyAPI_FUNC(void) _Py_NewReference(PyObject *op);
6+
PyAPI_FUNC(void) _Py_NewReferenceNoTotal(PyObject *op);
67

78
#ifdef Py_TRACE_REFS
89
/* Py_TRACE_REFS is such major surgery that we call external routines. */

Include/internal/pycore_object.h

+20-3
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,23 @@ PyAPI_FUNC(void) _Py_NO_RETURN _Py_FatalRefcountErrorFunc(
3737
#define _Py_FatalRefcountError(message) \
3838
_Py_FatalRefcountErrorFunc(__func__, (message))
3939

40+
41+
#ifdef Py_REF_DEBUG
42+
/* The symbol is only exposed in the API for the sake of extensions
43+
built against the pre-3.12 stable ABI. */
44+
PyAPI_DATA(Py_ssize_t) _Py_RefTotal;
45+
46+
extern void _Py_AddRefTotal(Py_ssize_t);
47+
extern void _Py_IncRefTotal(void);
48+
extern void _Py_DecRefTotal(void);
49+
# define _Py_DEC_REFTOTAL() _Py_RefTotal--
50+
#endif
51+
4052
// Increment reference count by n
4153
static inline void _Py_RefcntAdd(PyObject* op, Py_ssize_t n)
4254
{
4355
#ifdef Py_REF_DEBUG
44-
_Py_RefTotal += n;
56+
_Py_AddRefTotal(n);
4557
#endif
4658
op->ob_refcnt += n;
4759
}
@@ -52,7 +64,7 @@ _Py_DECREF_SPECIALIZED(PyObject *op, const destructor destruct)
5264
{
5365
_Py_DECREF_STAT_INC();
5466
#ifdef Py_REF_DEBUG
55-
_Py_RefTotal--;
67+
_Py_DEC_REFTOTAL();
5668
#endif
5769
if (--op->ob_refcnt != 0) {
5870
assert(op->ob_refcnt > 0);
@@ -70,7 +82,7 @@ _Py_DECREF_NO_DEALLOC(PyObject *op)
7082
{
7183
_Py_DECREF_STAT_INC();
7284
#ifdef Py_REF_DEBUG
73-
_Py_RefTotal--;
85+
_Py_DEC_REFTOTAL();
7486
#endif
7587
op->ob_refcnt--;
7688
#ifdef Py_DEBUG
@@ -80,6 +92,11 @@ _Py_DECREF_NO_DEALLOC(PyObject *op)
8092
#endif
8193
}
8294

95+
#ifdef Py_REF_DEBUG
96+
# undef _Py_DEC_REFTOTAL
97+
#endif
98+
99+
83100
PyAPI_FUNC(int) _PyType_CheckConsistency(PyTypeObject *type);
84101
PyAPI_FUNC(int) _PyDict_CheckConsistency(PyObject *mp, int check_content);
85102

Include/object.h

+21-4
Original file line numberDiff line numberDiff line change
@@ -490,7 +490,21 @@ you can count such references to the type object.)
490490
*/
491491

492492
#ifdef Py_REF_DEBUG
493-
PyAPI_DATA(Py_ssize_t) _Py_RefTotal;
493+
# if defined(Py_LIMITED_API) && Py_LIMITED_API+0 < 0x030A0000
494+
extern Py_ssize_t _Py_RefTotal;
495+
# define _Py_INC_REFTOTAL() _Py_RefTotal++
496+
# define _Py_DEC_REFTOTAL() _Py_RefTotal--
497+
# elif defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
498+
extern void _Py_IncRefTotal(void);
499+
extern void _Py_DecRefTotal(void);
500+
# define _Py_INC_REFTOTAL() _Py_IncRefTotal()
501+
# define _Py_DEC_REFTOTAL() _Py_DecRefTotal()
502+
# elif !defined(Py_LIMITED_API) || Py_LIMITED_API+0 > 0x030C0000
503+
extern void _Py_IncRefTotal_DO_NOT_USE_THIS(void);
504+
extern void _Py_DecRefTotal_DO_NOT_USE_THIS(void);
505+
# define _Py_INC_REFTOTAL() _Py_IncRefTotal_DO_NOT_USE_THIS()
506+
# define _Py_DEC_REFTOTAL() _Py_DecRefTotal_DO_NOT_USE_THIS()
507+
# endif
494508
PyAPI_FUNC(void) _Py_NegativeRefcount(const char *filename, int lineno,
495509
PyObject *op);
496510
#endif /* Py_REF_DEBUG */
@@ -519,8 +533,8 @@ static inline void Py_INCREF(PyObject *op)
519533
// Non-limited C API and limited C API for Python 3.9 and older access
520534
// directly PyObject.ob_refcnt.
521535
#ifdef Py_REF_DEBUG
522-
_Py_RefTotal++;
523-
#endif
536+
_Py_INC_REFTOTAL();
537+
#endif // Py_REF_DEBUG
524538
op->ob_refcnt++;
525539
#endif
526540
}
@@ -539,7 +553,7 @@ static inline void Py_DECREF(PyObject *op) {
539553
static inline void Py_DECREF(const char *filename, int lineno, PyObject *op)
540554
{
541555
_Py_DECREF_STAT_INC();
542-
_Py_RefTotal--;
556+
_Py_DEC_REFTOTAL();
543557
if (--op->ob_refcnt != 0) {
544558
if (op->ob_refcnt < 0) {
545559
_Py_NegativeRefcount(filename, lineno, op);
@@ -564,6 +578,9 @@ static inline void Py_DECREF(PyObject *op)
564578
#define Py_DECREF(op) Py_DECREF(_PyObject_CAST(op))
565579
#endif
566580

581+
#undef _Py_INC_REFTOTAL
582+
#undef _Py_DEC_REFTOTAL
583+
567584

568585
/* Safely decref `op` and set `op` to NULL, especially useful in tp_clear
569586
* and tp_dealloc implementations.

Modules/_testcapimodule.c

+1-6
Original file line numberDiff line numberDiff line change
@@ -1654,15 +1654,10 @@ slot_tp_del(PyObject *self)
16541654
*/
16551655
{
16561656
Py_ssize_t refcnt = Py_REFCNT(self);
1657-
_Py_NewReference(self);
1657+
_Py_NewReferenceNoTotal(self);
16581658
Py_SET_REFCNT(self, refcnt);
16591659
}
16601660
assert(!PyType_IS_GC(Py_TYPE(self)) || PyObject_GC_IsTracked(self));
1661-
/* If Py_REF_DEBUG macro is defined, _Py_NewReference() increased
1662-
_Py_RefTotal, so we need to undo that. */
1663-
#ifdef Py_REF_DEBUG
1664-
_Py_RefTotal--;
1665-
#endif
16661661
}
16671662

16681663
static PyObject *

Objects/bytesobject.c

+4-5
Original file line numberDiff line numberDiff line change
@@ -3060,21 +3060,20 @@ _PyBytes_Resize(PyObject **pv, Py_ssize_t newsize)
30603060
Py_DECREF(v);
30613061
return 0;
30623062
}
3063-
/* XXX UNREF/NEWREF interface should be more symmetrical */
3064-
#ifdef Py_REF_DEBUG
3065-
_Py_RefTotal--;
3066-
#endif
30673063
#ifdef Py_TRACE_REFS
30683064
_Py_ForgetReference(v);
30693065
#endif
30703066
*pv = (PyObject *)
30713067
PyObject_Realloc(v, PyBytesObject_SIZE + newsize);
30723068
if (*pv == NULL) {
3069+
#ifdef Py_REF_DEBUG
3070+
_Py_DecRefTotal();
3071+
#endif
30733072
PyObject_Free(v);
30743073
PyErr_NoMemory();
30753074
return -1;
30763075
}
3077-
_Py_NewReference(*pv);
3076+
_Py_NewReferenceNoTotal(*pv);
30783077
sv = (PyBytesObject *) *pv;
30793078
Py_SET_SIZE(sv, newsize);
30803079
sv->ob_sval[newsize] = '\0';

Objects/dictobject.c

+5-5
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ static inline void
303303
dictkeys_incref(PyDictKeysObject *dk)
304304
{
305305
#ifdef Py_REF_DEBUG
306-
_Py_RefTotal++;
306+
_Py_IncRefTotal();
307307
#endif
308308
dk->dk_refcnt++;
309309
}
@@ -313,7 +313,7 @@ dictkeys_decref(PyDictKeysObject *dk)
313313
{
314314
assert(dk->dk_refcnt > 0);
315315
#ifdef Py_REF_DEBUG
316-
_Py_RefTotal--;
316+
_Py_DecRefTotal();
317317
#endif
318318
if (--dk->dk_refcnt == 0) {
319319
free_keys_object(dk);
@@ -633,7 +633,7 @@ new_keys_object(uint8_t log2_size, bool unicode)
633633
}
634634
}
635635
#ifdef Py_REF_DEBUG
636-
_Py_RefTotal++;
636+
_Py_IncRefTotal();
637637
#endif
638638
dk->dk_refcnt = 1;
639639
dk->dk_log2_size = log2_size;
@@ -821,7 +821,7 @@ clone_combined_dict_keys(PyDictObject *orig)
821821
we have it now; calling dictkeys_incref would be an error as
822822
keys->dk_refcnt is already set to 1 (after memcpy). */
823823
#ifdef Py_REF_DEBUG
824-
_Py_RefTotal++;
824+
_Py_IncRefTotal();
825825
#endif
826826
return keys;
827827
}
@@ -1520,7 +1520,7 @@ dictresize(PyDictObject *mp, uint8_t log2_newsize, int unicode)
15201520
// We can not use free_keys_object here because key's reference
15211521
// are moved already.
15221522
#ifdef Py_REF_DEBUG
1523-
_Py_RefTotal--;
1523+
_Py_DecRefTotal();
15241524
#endif
15251525
if (oldkeys == Py_EMPTY_KEYS) {
15261526
oldkeys->dk_refcnt--;

Objects/object.c

+68-11
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,24 @@ _PyObject_CheckConsistency(PyObject *op, int check_content)
5656
#ifdef Py_REF_DEBUG
5757
Py_ssize_t _Py_RefTotal;
5858

59+
static inline void
60+
reftotal_increment(void)
61+
{
62+
_Py_RefTotal++;
63+
}
64+
65+
static inline void
66+
reftotal_decrement(void)
67+
{
68+
_Py_RefTotal--;
69+
}
70+
71+
void
72+
_Py_AddRefTotal(Py_ssize_t n)
73+
{
74+
_Py_RefTotal += n;
75+
}
76+
5977
Py_ssize_t
6078
_Py_GetRefTotal(void)
6179
{
@@ -121,6 +139,32 @@ _Py_NegativeRefcount(const char *filename, int lineno, PyObject *op)
121139
filename, lineno, __func__);
122140
}
123141

142+
/* This is exposed strictly for use in Py_INCREF(). */
143+
PyAPI_FUNC(void)
144+
_Py_IncRefTotal_DO_NOT_USE_THIS(void)
145+
{
146+
reftotal_increment();
147+
}
148+
149+
/* This is exposed strictly for use in Py_DECREF(). */
150+
PyAPI_FUNC(void)
151+
_Py_DecRefTotal_DO_NOT_USE_THIS(void)
152+
{
153+
reftotal_decrement();
154+
}
155+
156+
void
157+
_Py_IncRefTotal(void)
158+
{
159+
reftotal_increment();
160+
}
161+
162+
void
163+
_Py_DecRefTotal(void)
164+
{
165+
reftotal_decrement();
166+
}
167+
124168
#endif /* Py_REF_DEBUG */
125169

126170
void
@@ -138,12 +182,18 @@ Py_DecRef(PyObject *o)
138182
void
139183
_Py_IncRef(PyObject *o)
140184
{
185+
#ifdef Py_REF_DEBUG
186+
reftotal_increment();
187+
#endif
141188
Py_INCREF(o);
142189
}
143190

144191
void
145192
_Py_DecRef(PyObject *o)
146193
{
194+
#ifdef Py_REF_DEBUG
195+
reftotal_decrement();
196+
#endif
147197
Py_DECREF(o);
148198
}
149199

@@ -238,17 +288,12 @@ PyObject_CallFinalizerFromDealloc(PyObject *self)
238288
/* tp_finalize resurrected it! Make it look like the original Py_DECREF
239289
* never happened. */
240290
Py_ssize_t refcnt = Py_REFCNT(self);
241-
_Py_NewReference(self);
291+
_Py_NewReferenceNoTotal(self);
242292
Py_SET_REFCNT(self, refcnt);
243293

244294
_PyObject_ASSERT(self,
245295
(!_PyType_IS_GC(Py_TYPE(self))
246296
|| _PyObject_GC_IS_TRACKED(self)));
247-
/* If Py_REF_DEBUG macro is defined, _Py_NewReference() increased
248-
_Py_RefTotal, so we need to undo that. */
249-
#ifdef Py_REF_DEBUG
250-
_Py_RefTotal--;
251-
#endif
252297
return -1;
253298
}
254299

@@ -2010,21 +2055,33 @@ _PyTypes_FiniTypes(PyInterpreterState *interp)
20102055
}
20112056

20122057

2013-
void
2014-
_Py_NewReference(PyObject *op)
2058+
static inline void
2059+
new_reference(PyObject *op)
20152060
{
20162061
if (_PyRuntime.tracemalloc.config.tracing) {
20172062
_PyTraceMalloc_NewReference(op);
20182063
}
2019-
#ifdef Py_REF_DEBUG
2020-
_Py_RefTotal++;
2021-
#endif
20222064
Py_SET_REFCNT(op, 1);
20232065
#ifdef Py_TRACE_REFS
20242066
_Py_AddToAllObjects(op, 1);
20252067
#endif
20262068
}
20272069

2070+
void
2071+
_Py_NewReference(PyObject *op)
2072+
{
2073+
#ifdef Py_REF_DEBUG
2074+
reftotal_increment();
2075+
#endif
2076+
new_reference(op);
2077+
}
2078+
2079+
void
2080+
_Py_NewReferenceNoTotal(PyObject *op)
2081+
{
2082+
new_reference(op);
2083+
}
2084+
20282085

20292086
#ifdef Py_TRACE_REFS
20302087
void

Objects/structseq.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -592,7 +592,7 @@ _PyStructSequence_FiniType(PyTypeObject *type)
592592
// Don't use Py_DECREF(): static type must not be deallocated
593593
Py_SET_REFCNT(type, 0);
594594
#ifdef Py_REF_DEBUG
595-
_Py_RefTotal--;
595+
_Py_DecRefTotal();
596596
#endif
597597

598598
// Make sure that _PyStructSequence_InitType() will initialize

Objects/tupleobject.c

+4-5
Original file line numberDiff line numberDiff line change
@@ -930,10 +930,6 @@ _PyTuple_Resize(PyObject **pv, Py_ssize_t newsize)
930930
return *pv == NULL ? -1 : 0;
931931
}
932932

933-
/* XXX UNREF/NEWREF interface should be more symmetrical */
934-
#ifdef Py_REF_DEBUG
935-
_Py_RefTotal--;
936-
#endif
937933
if (_PyObject_GC_IS_TRACKED(v)) {
938934
_PyObject_GC_UNTRACK(v);
939935
}
@@ -947,10 +943,13 @@ _PyTuple_Resize(PyObject **pv, Py_ssize_t newsize)
947943
sv = PyObject_GC_Resize(PyTupleObject, v, newsize);
948944
if (sv == NULL) {
949945
*pv = NULL;
946+
#ifdef Py_REF_DEBUG
947+
_Py_DecRefTotal();
948+
#endif
950949
PyObject_GC_Del(v);
951950
return -1;
952951
}
953-
_Py_NewReference((PyObject *) sv);
952+
_Py_NewReferenceNoTotal((PyObject *) sv);
954953
/* Zero out items added by growing */
955954
if (newsize > oldsize)
956955
memset(&sv->ob_item[oldsize], 0,

Objects/unicodeobject.c

+2-5
Original file line numberDiff line numberDiff line change
@@ -947,21 +947,18 @@ resize_compact(PyObject *unicode, Py_ssize_t length)
947947
_PyUnicode_UTF8(unicode) = NULL;
948948
_PyUnicode_UTF8_LENGTH(unicode) = 0;
949949
}
950-
#ifdef Py_REF_DEBUG
951-
_Py_RefTotal--;
952-
#endif
953950
#ifdef Py_TRACE_REFS
954951
_Py_ForgetReference(unicode);
955952
#endif
956953

957954
new_unicode = (PyObject *)PyObject_Realloc(unicode, new_size);
958955
if (new_unicode == NULL) {
959-
_Py_NewReference(unicode);
956+
_Py_NewReferenceNoTotal(unicode);
960957
PyErr_NoMemory();
961958
return NULL;
962959
}
963960
unicode = new_unicode;
964-
_Py_NewReference(unicode);
961+
_Py_NewReferenceNoTotal(unicode);
965962

966963
_PyUnicode_LENGTH(unicode) = length;
967964
#ifdef Py_DEBUG

0 commit comments

Comments
 (0)