Skip to content

Commit 2b10a8d

Browse files
ericsnowcurrentlyFidget-Spinner
authored andcommitted
pythongh-102304: Move _Py_RefTotal to _PyRuntimeState (pythongh-102543)
The essentially eliminates the global variable, with the associated benefits. This is also a precursor to isolating this bit of state to PyInterpreterState. Folks that currently read _Py_RefTotal directly would have to start using _Py_GetGlobalRefTotal() instead. python#102304
1 parent 6e01113 commit 2b10a8d

File tree

13 files changed

+121
-36
lines changed

13 files changed

+121
-36
lines changed

Include/cpython/object.h

+4-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ PyAPI_FUNC(void) _Py_ForgetReference(PyObject *);
1111
#endif
1212

1313
#ifdef Py_REF_DEBUG
14-
PyAPI_FUNC(Py_ssize_t) _Py_GetRefTotal(void);
14+
/* These are useful as debugging aids when chasing down refleaks. */
15+
PyAPI_FUNC(Py_ssize_t) _Py_GetGlobalRefTotal(void);
16+
# define _Py_GetRefTotal() _Py_GetGlobalRefTotal()
17+
PyAPI_FUNC(Py_ssize_t) _Py_GetLegacyRefTotal(void);
1518
#endif
1619

1720

Include/internal/pycore_object.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ PyAPI_DATA(Py_ssize_t) _Py_RefTotal;
4646
extern void _Py_AddRefTotal(Py_ssize_t);
4747
extern void _Py_IncRefTotal(void);
4848
extern void _Py_DecRefTotal(void);
49-
# define _Py_DEC_REFTOTAL() _Py_RefTotal--
49+
50+
# define _Py_DEC_REFTOTAL() _PyRuntime.object_state.reftotal--
5051
#endif
5152

5253
// Increment reference count by n
@@ -225,6 +226,7 @@ static inline void _PyObject_GC_UNTRACK(
225226
#endif
226227

227228
#ifdef Py_REF_DEBUG
229+
extern void _Py_FinalizeRefTotal(_PyRuntimeState *);
228230
extern void _PyDebug_PrintTotalRefs(void);
229231
#endif
230232

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#ifndef Py_INTERNAL_OBJECT_STATE_H
2+
#define Py_INTERNAL_OBJECT_STATE_H
3+
#ifdef __cplusplus
4+
extern "C" {
5+
#endif
6+
7+
#ifndef Py_BUILD_CORE
8+
# error "this header requires Py_BUILD_CORE define"
9+
#endif
10+
11+
struct _py_object_runtime_state {
12+
#ifdef Py_REF_DEBUG
13+
Py_ssize_t reftotal;
14+
#else
15+
int _not_used;
16+
#endif
17+
};
18+
19+
20+
#ifdef __cplusplus
21+
}
22+
#endif
23+
#endif /* !Py_INTERNAL_OBJECT_STATE_H */

Include/internal/pycore_runtime.h

+2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ extern "C" {
1515
#include "pycore_global_objects.h" // struct _Py_global_objects
1616
#include "pycore_import.h" // struct _import_runtime_state
1717
#include "pycore_interp.h" // PyInterpreterState
18+
#include "pycore_object_state.h" // struct _py_object_runtime_state
1819
#include "pycore_parser.h" // struct _parser_runtime_state
1920
#include "pycore_pymem.h" // struct _pymem_allocators
2021
#include "pycore_pyhash.h" // struct pyhash_runtime_state
@@ -150,6 +151,7 @@ typedef struct pyruntimestate {
150151
void *open_code_userdata;
151152
_Py_AuditHookEntry *audit_hook_head;
152153

154+
struct _py_object_runtime_state object_state;
153155
struct _Py_float_runtime_state float_state;
154156
struct _Py_unicode_runtime_state unicode_state;
155157

Include/object.h

+2-7
Original file line numberDiff line numberDiff line change
@@ -494,14 +494,9 @@ you can count such references to the type object.)
494494
extern Py_ssize_t _Py_RefTotal;
495495
# define _Py_INC_REFTOTAL() _Py_RefTotal++
496496
# 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()
502497
# 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);
498+
PyAPI_FUNC(void) _Py_IncRefTotal_DO_NOT_USE_THIS(void);
499+
PyAPI_FUNC(void) _Py_DecRefTotal_DO_NOT_USE_THIS(void);
505500
# define _Py_INC_REFTOTAL() _Py_IncRefTotal_DO_NOT_USE_THIS()
506501
# define _Py_DEC_REFTOTAL() _Py_DecRefTotal_DO_NOT_USE_THIS()
507502
# endif

Makefile.pre.in

+1
Original file line numberDiff line numberDiff line change
@@ -1699,6 +1699,7 @@ PYTHON_HEADERS= \
16991699
$(srcdir)/Include/internal/pycore_moduleobject.h \
17001700
$(srcdir)/Include/internal/pycore_namespace.h \
17011701
$(srcdir)/Include/internal/pycore_object.h \
1702+
$(srcdir)/Include/internal/pycore_object_state.h \
17021703
$(srcdir)/Include/internal/pycore_obmalloc.h \
17031704
$(srcdir)/Include/internal/pycore_obmalloc_init.h \
17041705
$(srcdir)/Include/internal/pycore_pathconfig.h \

Objects/object.c

+76-25
Original file line numberDiff line numberDiff line change
@@ -54,37 +54,71 @@ _PyObject_CheckConsistency(PyObject *op, int check_content)
5454

5555

5656
#ifdef Py_REF_DEBUG
57+
/* We keep the legacy symbol around for backward compatibility. */
5758
Py_ssize_t _Py_RefTotal;
5859

60+
static inline Py_ssize_t
61+
get_legacy_reftotal(void)
62+
{
63+
return _Py_RefTotal;
64+
}
65+
#endif
66+
67+
#ifdef Py_REF_DEBUG
68+
69+
# define REFTOTAL(runtime) \
70+
(runtime)->object_state.reftotal
71+
72+
static inline void
73+
reftotal_increment(_PyRuntimeState *runtime)
74+
{
75+
REFTOTAL(runtime)++;
76+
}
77+
5978
static inline void
60-
reftotal_increment(void)
79+
reftotal_decrement(_PyRuntimeState *runtime)
6180
{
62-
_Py_RefTotal++;
81+
REFTOTAL(runtime)--;
6382
}
6483

6584
static inline void
66-
reftotal_decrement(void)
85+
reftotal_add(_PyRuntimeState *runtime, Py_ssize_t n)
6786
{
68-
_Py_RefTotal--;
87+
REFTOTAL(runtime) += n;
6988
}
7089

90+
static inline Py_ssize_t get_global_reftotal(_PyRuntimeState *);
91+
92+
/* We preserve the number of refs leaked during runtime finalization,
93+
so they can be reported if the runtime is initialized again. */
94+
// XXX We don't lose any information by dropping this,
95+
// so we should consider doing so.
96+
static Py_ssize_t last_final_reftotal = 0;
97+
7198
void
72-
_Py_AddRefTotal(Py_ssize_t n)
99+
_Py_FinalizeRefTotal(_PyRuntimeState *runtime)
73100
{
74-
_Py_RefTotal += n;
101+
last_final_reftotal = get_global_reftotal(runtime);
102+
REFTOTAL(runtime) = 0;
75103
}
76104

77-
Py_ssize_t
78-
_Py_GetRefTotal(void)
105+
static inline Py_ssize_t
106+
get_global_reftotal(_PyRuntimeState *runtime)
79107
{
80-
return _Py_RefTotal;
108+
/* For an update from _Py_RefTotal first. */
109+
Py_ssize_t legacy = get_legacy_reftotal();
110+
return REFTOTAL(runtime) + legacy + last_final_reftotal;
81111
}
82112

113+
#undef REFTOTAL
114+
83115
void
84116
_PyDebug_PrintTotalRefs(void) {
117+
_PyRuntimeState *runtime = &_PyRuntime;
85118
fprintf(stderr,
86119
"[%zd refs, %zd blocks]\n",
87-
_Py_GetRefTotal(), _Py_GetAllocatedBlocks());
120+
get_global_reftotal(runtime), _Py_GetAllocatedBlocks());
121+
/* It may be helpful to also print the "legacy" reftotal separately. */
88122
}
89123
#endif /* Py_REF_DEBUG */
90124

@@ -139,30 +173,50 @@ _Py_NegativeRefcount(const char *filename, int lineno, PyObject *op)
139173
filename, lineno, __func__);
140174
}
141175

142-
/* This is exposed strictly for use in Py_INCREF(). */
143-
PyAPI_FUNC(void)
176+
/* This is used strictly by Py_INCREF(). */
177+
void
144178
_Py_IncRefTotal_DO_NOT_USE_THIS(void)
145179
{
146-
reftotal_increment();
180+
reftotal_increment(&_PyRuntime);
147181
}
148182

149-
/* This is exposed strictly for use in Py_DECREF(). */
150-
PyAPI_FUNC(void)
183+
/* This is used strictly by Py_DECREF(). */
184+
void
151185
_Py_DecRefTotal_DO_NOT_USE_THIS(void)
152186
{
153-
reftotal_decrement();
187+
reftotal_decrement(&_PyRuntime);
154188
}
155189

156190
void
157191
_Py_IncRefTotal(void)
158192
{
159-
reftotal_increment();
193+
reftotal_increment(&_PyRuntime);
160194
}
161195

162196
void
163197
_Py_DecRefTotal(void)
164198
{
165-
reftotal_decrement();
199+
reftotal_decrement(&_PyRuntime);
200+
}
201+
202+
void
203+
_Py_AddRefTotal(Py_ssize_t n)
204+
{
205+
reftotal_add(&_PyRuntime, n);
206+
}
207+
208+
/* This includes the legacy total
209+
and any carried over from the last runtime init/fini cycle. */
210+
Py_ssize_t
211+
_Py_GetGlobalRefTotal(void)
212+
{
213+
return get_global_reftotal(&_PyRuntime);
214+
}
215+
216+
Py_ssize_t
217+
_Py_GetLegacyRefTotal(void)
218+
{
219+
return get_legacy_reftotal();
166220
}
167221

168222
#endif /* Py_REF_DEBUG */
@@ -182,21 +236,18 @@ Py_DecRef(PyObject *o)
182236
void
183237
_Py_IncRef(PyObject *o)
184238
{
185-
#ifdef Py_REF_DEBUG
186-
reftotal_increment();
187-
#endif
188239
Py_INCREF(o);
189240
}
190241

191242
void
192243
_Py_DecRef(PyObject *o)
193244
{
194-
#ifdef Py_REF_DEBUG
195-
reftotal_decrement();
196-
#endif
197245
Py_DECREF(o);
198246
}
199247

248+
249+
/**************************************/
250+
200251
PyObject *
201252
PyObject_Init(PyObject *op, PyTypeObject *tp)
202253
{
@@ -2077,7 +2128,7 @@ void
20772128
_Py_NewReference(PyObject *op)
20782129
{
20792130
#ifdef Py_REF_DEBUG
2080-
reftotal_increment();
2131+
reftotal_increment(&_PyRuntime);
20812132
#endif
20822133
new_reference(op);
20832134
}

PCbuild/pythoncore.vcxproj

+1
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@
239239
<ClInclude Include="..\Include\internal\pycore_moduleobject.h" />
240240
<ClInclude Include="..\Include\internal\pycore_namespace.h" />
241241
<ClInclude Include="..\Include\internal\pycore_object.h" />
242+
<ClInclude Include="..\Include\internal\pycore_object_state.h" />
242243
<ClInclude Include="..\Include\internal\pycore_obmalloc.h" />
243244
<ClInclude Include="..\Include\internal\pycore_obmalloc_init.h" />
244245
<ClInclude Include="..\Include\internal\pycore_pathconfig.h" />

PCbuild/pythoncore.vcxproj.filters

+3
Original file line numberDiff line numberDiff line change
@@ -621,6 +621,9 @@
621621
<ClInclude Include="..\Include\internal\pycore_object.h">
622622
<Filter>Include\internal</Filter>
623623
</ClInclude>
624+
<ClInclude Include="..\Include\internal\pycore_object_state.h">
625+
<Filter>Include\internal</Filter>
626+
</ClInclude>
624627
<ClInclude Include="..\Include\internal\pycore_obmalloc.h">
625628
<Filter>Include\internal</Filter>
626629
</ClInclude>

Python/pylifecycle.c

+1
Original file line numberDiff line numberDiff line change
@@ -1930,6 +1930,7 @@ Py_FinalizeEx(void)
19301930
if (show_ref_count) {
19311931
_PyDebug_PrintTotalRefs();
19321932
}
1933+
_Py_FinalizeRefTotal(runtime);
19331934
#endif
19341935

19351936
#ifdef Py_TRACE_REFS

Python/pystate.c

+3
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,9 @@ _PyRuntimeState_Init(_PyRuntimeState *runtime)
482482
void
483483
_PyRuntimeState_Fini(_PyRuntimeState *runtime)
484484
{
485+
/* The reftotal is cleared by _Py_FinalizeRefTotal(). */
486+
assert(runtime->object_state.reftotal == 0);
487+
485488
if (gilstate_tss_initialized(runtime)) {
486489
gilstate_tss_fini(runtime);
487490
}

Python/sysmodule.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -1854,7 +1854,7 @@ static Py_ssize_t
18541854
sys_gettotalrefcount_impl(PyObject *module)
18551855
/*[clinic end generated code: output=4103886cf17c25bc input=53b744faa5d2e4f6]*/
18561856
{
1857-
return _Py_GetRefTotal();
1857+
return _Py_GetGlobalRefTotal();
18581858
}
18591859

18601860
#endif /* Py_REF_DEBUG */

Tools/c-analyzer/cpython/ignored.tsv

+1-1
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,6 @@ Modules/syslogmodule.c - S_log_open -
141141
##-----------------------
142142
## kept for stable ABI compatibility
143143

144-
# XXX should be per-interpreter, without impacting stable ABI extensions
145144
Objects/object.c - _Py_RefTotal -
146145

147146
##-----------------------
@@ -301,6 +300,7 @@ Objects/genobject.c - NON_INIT_CORO_MSG -
301300
Objects/longobject.c - _PyLong_DigitValue -
302301
Objects/object.c - _Py_SwappedOp -
303302
Objects/object.c - _Py_abstract_hack -
303+
Objects/object.c - last_final_reftotal -
304304
Objects/object.c - static_types -
305305
Objects/obmalloc.c - _PyMem -
306306
Objects/obmalloc.c - _PyMem_Debug -

0 commit comments

Comments
 (0)