Skip to content

Commit dcfe6bc

Browse files
committed
Merge branch 'gh-117439-reftotal' into nogil-integration
2 parents 6608ab3 + 6bdbad0 commit dcfe6bc

File tree

9 files changed

+59
-44
lines changed

9 files changed

+59
-44
lines changed

Include/internal/pycore_object.h

+6-6
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,9 @@ PyAPI_FUNC(void) _Py_NO_RETURN _Py_FatalRefcountErrorFunc(
8787
built against the pre-3.12 stable ABI. */
8888
PyAPI_DATA(Py_ssize_t) _Py_RefTotal;
8989

90-
extern void _Py_AddRefTotal(PyInterpreterState *, Py_ssize_t);
91-
extern void _Py_IncRefTotal(PyInterpreterState *);
92-
extern void _Py_DecRefTotal(PyInterpreterState *);
90+
extern void _Py_AddRefTotal(PyThreadState *, Py_ssize_t);
91+
extern void _Py_IncRefTotal(PyThreadState *);
92+
extern void _Py_DecRefTotal(PyThreadState *);
9393

9494
# define _Py_DEC_REFTOTAL(interp) \
9595
interp->object_state.reftotal--
@@ -102,7 +102,7 @@ static inline void _Py_RefcntAdd(PyObject* op, Py_ssize_t n)
102102
return;
103103
}
104104
#ifdef Py_REF_DEBUG
105-
_Py_AddRefTotal(_PyInterpreterState_GET(), n);
105+
_Py_AddRefTotal(_PyThreadState_GET(), n);
106106
#endif
107107
#if !defined(Py_GIL_DISABLED)
108108
op->ob_refcnt += n;
@@ -394,7 +394,7 @@ _Py_TryIncrefFast(PyObject *op) {
394394
_Py_INCREF_STAT_INC();
395395
_Py_atomic_store_uint32_relaxed(&op->ob_ref_local, local);
396396
#ifdef Py_REF_DEBUG
397-
_Py_IncRefTotal(_PyInterpreterState_GET());
397+
_Py_IncRefTotal(_PyThreadState_GET());
398398
#endif
399399
return 1;
400400
}
@@ -417,7 +417,7 @@ _Py_TryIncRefShared(PyObject *op)
417417
&shared,
418418
shared + (1 << _Py_REF_SHARED_SHIFT))) {
419419
#ifdef Py_REF_DEBUG
420-
_Py_IncRefTotal(_PyInterpreterState_GET());
420+
_Py_IncRefTotal(_PyThreadState_GET());
421421
#endif
422422
_Py_INCREF_STAT_INC();
423423
return 1;

Include/internal/pycore_tstate.h

+4
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ typedef struct _PyThreadStateImpl {
3838
struct _brc_thread_state brc;
3939
#endif
4040

41+
#if defined(Py_REF_DEBUG) && defined(Py_GIL_DISABLED)
42+
Py_ssize_t reftotal; // this thread's total refcount operations
43+
#endif
44+
4145
} _PyThreadStateImpl;
4246

4347

Objects/bytesobject.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -3084,7 +3084,7 @@ _PyBytes_Resize(PyObject **pv, Py_ssize_t newsize)
30843084
PyObject_Realloc(v, PyBytesObject_SIZE + newsize);
30853085
if (*pv == NULL) {
30863086
#ifdef Py_REF_DEBUG
3087-
_Py_DecRefTotal(_PyInterpreterState_GET());
3087+
_Py_DecRefTotal(_PyThreadState_GET());
30883088
#endif
30893089
PyObject_Free(v);
30903090
PyErr_NoMemory();

Objects/dictobject.c

+5-5
Original file line numberDiff line numberDiff line change
@@ -445,7 +445,7 @@ dictkeys_incref(PyDictKeysObject *dk)
445445
return;
446446
}
447447
#ifdef Py_REF_DEBUG
448-
_Py_IncRefTotal(_PyInterpreterState_GET());
448+
_Py_IncRefTotal(_PyThreadState_GET());
449449
#endif
450450
INCREF_KEYS(dk);
451451
}
@@ -458,7 +458,7 @@ dictkeys_decref(PyInterpreterState *interp, PyDictKeysObject *dk, bool use_qsbr)
458458
}
459459
assert(dk->dk_refcnt > 0);
460460
#ifdef Py_REF_DEBUG
461-
_Py_DecRefTotal(_PyInterpreterState_GET());
461+
_Py_DecRefTotal(_PyThreadState_GET());
462462
#endif
463463
if (DECREF_KEYS(dk) == 1) {
464464
if (DK_IS_UNICODE(dk)) {
@@ -790,7 +790,7 @@ new_keys_object(PyInterpreterState *interp, uint8_t log2_size, bool unicode)
790790
}
791791
}
792792
#ifdef Py_REF_DEBUG
793-
_Py_IncRefTotal(_PyInterpreterState_GET());
793+
_Py_IncRefTotal(_PyThreadState_GET());
794794
#endif
795795
dk->dk_refcnt = 1;
796796
dk->dk_log2_size = log2_size;
@@ -978,7 +978,7 @@ clone_combined_dict_keys(PyDictObject *orig)
978978
we have it now; calling dictkeys_incref would be an error as
979979
keys->dk_refcnt is already set to 1 (after memcpy). */
980980
#ifdef Py_REF_DEBUG
981-
_Py_IncRefTotal(_PyInterpreterState_GET());
981+
_Py_IncRefTotal(_PyThreadState_GET());
982982
#endif
983983
return keys;
984984
}
@@ -2011,7 +2011,7 @@ dictresize(PyInterpreterState *interp, PyDictObject *mp,
20112011

20122012
if (oldkeys != Py_EMPTY_KEYS) {
20132013
#ifdef Py_REF_DEBUG
2014-
_Py_DecRefTotal(_PyInterpreterState_GET());
2014+
_Py_DecRefTotal(_PyThreadState_GET());
20152015
#endif
20162016
assert(oldkeys->dk_kind != DICT_KEYS_SPLIT);
20172017
assert(oldkeys->dk_refcnt == 1);

Objects/object.c

+31-28
Original file line numberDiff line numberDiff line change
@@ -74,21 +74,14 @@ get_legacy_reftotal(void)
7474
interp->object_state.reftotal
7575

7676
static inline void
77-
reftotal_increment(PyInterpreterState *interp)
77+
reftotal_add(PyThreadState *tstate, Py_ssize_t n)
7878
{
79-
REFTOTAL(interp)++;
80-
}
81-
82-
static inline void
83-
reftotal_decrement(PyInterpreterState *interp)
84-
{
85-
REFTOTAL(interp)--;
86-
}
87-
88-
static inline void
89-
reftotal_add(PyInterpreterState *interp, Py_ssize_t n)
90-
{
91-
REFTOTAL(interp) += n;
79+
#ifdef Py_GIL_DISABLED
80+
_PyThreadStateImpl *tstate_impl = (_PyThreadStateImpl *)tstate;
81+
tstate_impl->reftotal += n;
82+
#else
83+
REFTOTAL(tstate->interp) += n;
84+
#endif
9285
}
9386

9487
static inline Py_ssize_t get_global_reftotal(_PyRuntimeState *);
@@ -118,7 +111,14 @@ get_reftotal(PyInterpreterState *interp)
118111
{
119112
/* For a single interpreter, we ignore the legacy _Py_RefTotal,
120113
since we can't determine which interpreter updated it. */
121-
return REFTOTAL(interp);
114+
Py_ssize_t total = REFTOTAL(interp);
115+
#ifdef Py_GIL_DISABLED
116+
for (PyThreadState *p = interp->threads.head; p != NULL; p = p->next) {
117+
/* This may race with other threads modifications to their reftotal */
118+
total += ((_PyThreadStateImpl *)p)->reftotal;
119+
}
120+
#endif
121+
return total;
122122
}
123123

124124
static inline Py_ssize_t
@@ -130,7 +130,7 @@ get_global_reftotal(_PyRuntimeState *runtime)
130130
HEAD_LOCK(&_PyRuntime);
131131
PyInterpreterState *interp = PyInterpreterState_Head();
132132
for (; interp != NULL; interp = PyInterpreterState_Next(interp)) {
133-
total += REFTOTAL(interp);
133+
total += get_reftotal(interp);
134134
}
135135
HEAD_UNLOCK(&_PyRuntime);
136136

@@ -223,32 +223,32 @@ _Py_NegativeRefcount(const char *filename, int lineno, PyObject *op)
223223
void
224224
_Py_INCREF_IncRefTotal(void)
225225
{
226-
reftotal_increment(_PyInterpreterState_GET());
226+
reftotal_add(_PyThreadState_GET(), 1);
227227
}
228228

229229
/* This is used strictly by Py_DECREF(). */
230230
void
231231
_Py_DECREF_DecRefTotal(void)
232232
{
233-
reftotal_decrement(_PyInterpreterState_GET());
233+
reftotal_add(_PyThreadState_GET(), -1);
234234
}
235235

236236
void
237-
_Py_IncRefTotal(PyInterpreterState *interp)
237+
_Py_IncRefTotal(PyThreadState *tstate)
238238
{
239-
reftotal_increment(interp);
239+
reftotal_add(tstate, 1);
240240
}
241241

242242
void
243-
_Py_DecRefTotal(PyInterpreterState *interp)
243+
_Py_DecRefTotal(PyThreadState *tstate)
244244
{
245-
reftotal_decrement(interp);
245+
reftotal_add(tstate, -1);
246246
}
247247

248248
void
249-
_Py_AddRefTotal(PyInterpreterState *interp, Py_ssize_t n)
249+
_Py_AddRefTotal(PyThreadState *tstate, Py_ssize_t n)
250250
{
251-
reftotal_add(interp, n);
251+
reftotal_add(tstate, n);
252252
}
253253

254254
/* This includes the legacy total
@@ -268,7 +268,10 @@ _Py_GetLegacyRefTotal(void)
268268
Py_ssize_t
269269
_PyInterpreterState_GetRefTotal(PyInterpreterState *interp)
270270
{
271-
return get_reftotal(interp);
271+
HEAD_LOCK(&_PyRuntime);
272+
Py_ssize_t total = get_reftotal(interp);
273+
HEAD_UNLOCK(&_PyRuntime);
274+
return total;
272275
}
273276

274277
#endif /* Py_REF_DEBUG */
@@ -346,7 +349,7 @@ _Py_DecRefSharedDebug(PyObject *o, const char *filename, int lineno)
346349

347350
if (should_queue) {
348351
#ifdef Py_REF_DEBUG
349-
_Py_IncRefTotal(_PyInterpreterState_GET());
352+
_Py_IncRefTotal(_PyThreadState_GET());
350353
#endif
351354
_Py_brc_queue_object(o);
352355
}
@@ -406,7 +409,7 @@ _Py_ExplicitMergeRefcount(PyObject *op, Py_ssize_t extra)
406409
&shared, new_shared));
407410

408411
#ifdef Py_REF_DEBUG
409-
_Py_AddRefTotal(_PyInterpreterState_GET(), extra);
412+
_Py_AddRefTotal(_PyThreadState_GET(), extra);
410413
#endif
411414

412415
_Py_atomic_store_uint32_relaxed(&op->ob_ref_local, 0);
@@ -2374,7 +2377,7 @@ void
23742377
_Py_NewReference(PyObject *op)
23752378
{
23762379
#ifdef Py_REF_DEBUG
2377-
reftotal_increment(_PyInterpreterState_GET());
2380+
_Py_IncRefTotal(_PyThreadState_GET());
23782381
#endif
23792382
new_reference(op);
23802383
}

Objects/tupleobject.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -946,7 +946,7 @@ _PyTuple_Resize(PyObject **pv, Py_ssize_t newsize)
946946
if (sv == NULL) {
947947
*pv = NULL;
948948
#ifdef Py_REF_DEBUG
949-
_Py_DecRefTotal(_PyInterpreterState_GET());
949+
_Py_DecRefTotal(_PyThreadState_GET());
950950
#endif
951951
PyObject_GC_Del(v);
952952
return -1;

Objects/unicodeobject.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -14977,7 +14977,7 @@ _PyUnicode_InternInPlace(PyInterpreterState *interp, PyObject **p)
1497714977
decrements to these objects will not be registered so they
1497814978
need to be accounted for in here. */
1497914979
for (Py_ssize_t i = 0; i < Py_REFCNT(s) - 2; i++) {
14980-
_Py_DecRefTotal(_PyInterpreterState_GET());
14980+
_Py_DecRefTotal(_PyThreadState_GET());
1498114981
}
1498214982
#endif
1498314983
_Py_SetImmortal(s);

Python/gc_free_threading.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ merge_refcount(PyObject *op, Py_ssize_t extra)
168168
refcount += extra;
169169

170170
#ifdef Py_REF_DEBUG
171-
_Py_AddRefTotal(_PyInterpreterState_GET(), extra);
171+
_Py_AddRefTotal(_PyThreadState_GET(), extra);
172172
#endif
173173

174174
// No atomics necessary; all other threads in this interpreter are paused.
@@ -307,7 +307,7 @@ merge_queued_objects(_PyThreadStateImpl *tstate, struct collection_state *state)
307307
// decref and deallocate the object once we start the world again.
308308
op->ob_ref_shared += (1 << _Py_REF_SHARED_SHIFT);
309309
#ifdef Py_REF_DEBUG
310-
_Py_IncRefTotal(_PyInterpreterState_GET());
310+
_Py_IncRefTotal(_PyThreadState_GET());
311311
#endif
312312
worklist_push(&state->objs_to_decref, op);
313313
}

Python/pystate.c

+8
Original file line numberDiff line numberDiff line change
@@ -1698,6 +1698,14 @@ tstate_delete_common(PyThreadState *tstate)
16981698
decrement_stoptheworld_countdown(&runtime->stoptheworld);
16991699
}
17001700
}
1701+
1702+
#if defined(Py_REF_DEBUG) && defined(Py_GIL_DISABLED)
1703+
// Add our portion of the total refcount to the interpreter's total.
1704+
_PyThreadStateImpl *tstate_impl = (_PyThreadStateImpl *)tstate;
1705+
tstate->interp->object_state.reftotal += tstate_impl->reftotal;
1706+
tstate_impl->reftotal = 0;
1707+
#endif
1708+
17011709
HEAD_UNLOCK(runtime);
17021710

17031711
#ifdef Py_GIL_DISABLED

0 commit comments

Comments
 (0)