Skip to content

Commit 6f8b84f

Browse files
Unrevert "[3.12] gh-107080: Fix Py_TRACE_REFS Crashes Under Isolated Subinterpreters (gh-107567) (#107599)".
This reverts commit 6e4eec7 (gh-107648).
1 parent e5582bd commit 6f8b84f

File tree

6 files changed

+62
-29
lines changed

6 files changed

+62
-29
lines changed

Include/internal/pycore_object.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -271,8 +271,8 @@ extern void _PyDebug_PrintTotalRefs(void);
271271

272272
#ifdef Py_TRACE_REFS
273273
extern void _Py_AddToAllObjects(PyObject *op, int force);
274-
extern void _Py_PrintReferences(FILE *);
275-
extern void _Py_PrintReferenceAddresses(FILE *);
274+
extern void _Py_PrintReferences(PyInterpreterState *, FILE *);
275+
extern void _Py_PrintReferenceAddresses(PyInterpreterState *, FILE *);
276276
#endif
277277

278278

Include/internal/pycore_object_state.h

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,22 @@ extern "C" {
1111
struct _py_object_runtime_state {
1212
#ifdef Py_REF_DEBUG
1313
Py_ssize_t interpreter_leaks;
14-
#else
15-
int _not_used;
1614
#endif
15+
int _not_used;
1716
};
1817

1918
struct _py_object_state {
2019
#ifdef Py_REF_DEBUG
2120
Py_ssize_t reftotal;
22-
#else
23-
int _not_used;
2421
#endif
22+
#ifdef Py_TRACE_REFS
23+
/* Head of circular doubly-linked list of all objects. These are linked
24+
* together via the _ob_prev and _ob_next members of a PyObject, which
25+
* exist only in a Py_TRACE_REFS build.
26+
*/
27+
PyObject refchain;
28+
#endif
29+
int _not_used;
2530
};
2631

2732

Include/internal/pycore_runtime_init.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ extern PyTypeObject _PyExc_MemoryError;
101101
{ .threshold = 10, }, \
102102
}, \
103103
}, \
104+
.object_state = _py_object_state_INIT(INTERP), \
104105
.dtoa = _dtoa_state_INIT(&(INTERP)), \
105106
.dict_state = _dict_state_INIT, \
106107
.func_state = { \
@@ -130,6 +131,16 @@ extern PyTypeObject _PyExc_MemoryError;
130131
.context_ver = 1, \
131132
}
132133

134+
#ifdef Py_TRACE_REFS
135+
# define _py_object_state_INIT(INTERP) \
136+
{ \
137+
.refchain = {&INTERP.object_state.refchain, &INTERP.object_state.refchain}, \
138+
}
139+
#else
140+
# define _py_object_state_INIT(INTERP) \
141+
{ 0 }
142+
#endif
143+
133144

134145
// global objects
135146

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Trace refs builds (``--with-trace-refs``) were crashing when used with
2+
isolated subinterpreters. The problematic global state has been isolated to
3+
each interpreter. Other fixing the crashes, this change does not affect
4+
users.

Objects/object.c

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -158,11 +158,8 @@ _PyDebug_PrintTotalRefs(void) {
158158
Do not call them otherwise, they do not initialize the object! */
159159

160160
#ifdef Py_TRACE_REFS
161-
/* Head of circular doubly-linked list of all objects. These are linked
162-
* together via the _ob_prev and _ob_next members of a PyObject, which
163-
* exist only in a Py_TRACE_REFS build.
164-
*/
165-
static PyObject refchain = {&refchain, &refchain};
161+
162+
#define REFCHAIN(interp) &interp->object_state.refchain
166163

167164
/* Insert op at the front of the list of all objects. If force is true,
168165
* op is added even if _ob_prev and _ob_next are non-NULL already. If
@@ -187,10 +184,11 @@ _Py_AddToAllObjects(PyObject *op, int force)
187184
}
188185
#endif
189186
if (force || op->_ob_prev == NULL) {
190-
op->_ob_next = refchain._ob_next;
191-
op->_ob_prev = &refchain;
192-
refchain._ob_next->_ob_prev = op;
193-
refchain._ob_next = op;
187+
PyObject *refchain = REFCHAIN(_PyInterpreterState_GET());
188+
op->_ob_next = refchain->_ob_next;
189+
op->_ob_prev = refchain;
190+
refchain->_ob_next->_ob_prev = op;
191+
refchain->_ob_next = op;
194192
}
195193
}
196194
#endif /* Py_TRACE_REFS */
@@ -2206,20 +2204,21 @@ _Py_ForgetReference(PyObject *op)
22062204
_PyObject_ASSERT_FAILED_MSG(op, "negative refcnt");
22072205
}
22082206

2209-
if (op == &refchain ||
2207+
PyObject *refchain = REFCHAIN(_PyInterpreterState_GET());
2208+
if (op == refchain ||
22102209
op->_ob_prev->_ob_next != op || op->_ob_next->_ob_prev != op)
22112210
{
22122211
_PyObject_ASSERT_FAILED_MSG(op, "invalid object chain");
22132212
}
22142213

22152214
#ifdef SLOW_UNREF_CHECK
22162215
PyObject *p;
2217-
for (p = refchain._ob_next; p != &refchain; p = p->_ob_next) {
2216+
for (p = refchain->_ob_next; p != refchain; p = p->_ob_next) {
22182217
if (p == op) {
22192218
break;
22202219
}
22212220
}
2222-
if (p == &refchain) {
2221+
if (p == refchain) {
22232222
/* Not found */
22242223
_PyObject_ASSERT_FAILED_MSG(op,
22252224
"object not found in the objects list");
@@ -2235,11 +2234,15 @@ _Py_ForgetReference(PyObject *op)
22352234
* interpreter must be in a healthy state.
22362235
*/
22372236
void
2238-
_Py_PrintReferences(FILE *fp)
2237+
_Py_PrintReferences(PyInterpreterState *interp, FILE *fp)
22392238
{
22402239
PyObject *op;
2240+
if (interp == NULL) {
2241+
interp = _PyInterpreterState_Main();
2242+
}
22412243
fprintf(fp, "Remaining objects:\n");
2242-
for (op = refchain._ob_next; op != &refchain; op = op->_ob_next) {
2244+
PyObject *refchain = REFCHAIN(interp);
2245+
for (op = refchain->_ob_next; op != refchain; op = op->_ob_next) {
22432246
fprintf(fp, "%p [%zd] ", (void *)op, Py_REFCNT(op));
22442247
if (PyObject_Print(op, fp, 0) != 0) {
22452248
PyErr_Clear();
@@ -2251,34 +2254,42 @@ _Py_PrintReferences(FILE *fp)
22512254
/* Print the addresses of all live objects. Unlike _Py_PrintReferences, this
22522255
* doesn't make any calls to the Python C API, so is always safe to call.
22532256
*/
2257+
// XXX This function is not safe to use if the interpreter has been
2258+
// freed or is in an unhealthy state (e.g. late in finalization).
2259+
// The call in Py_FinalizeEx() is okay since the main interpreter
2260+
// is statically allocated.
22542261
void
2255-
_Py_PrintReferenceAddresses(FILE *fp)
2262+
_Py_PrintReferenceAddresses(PyInterpreterState *interp, FILE *fp)
22562263
{
22572264
PyObject *op;
2265+
PyObject *refchain = REFCHAIN(interp);
22582266
fprintf(fp, "Remaining object addresses:\n");
2259-
for (op = refchain._ob_next; op != &refchain; op = op->_ob_next)
2267+
for (op = refchain->_ob_next; op != refchain; op = op->_ob_next)
22602268
fprintf(fp, "%p [%zd] %s\n", (void *)op,
22612269
Py_REFCNT(op), Py_TYPE(op)->tp_name);
22622270
}
22632271

2272+
/* The implementation of sys.getobjects(). */
22642273
PyObject *
22652274
_Py_GetObjects(PyObject *self, PyObject *args)
22662275
{
22672276
int i, n;
22682277
PyObject *t = NULL;
22692278
PyObject *res, *op;
2279+
PyInterpreterState *interp = _PyInterpreterState_GET();
22702280

22712281
if (!PyArg_ParseTuple(args, "i|O", &n, &t))
22722282
return NULL;
2273-
op = refchain._ob_next;
2283+
PyObject *refchain = REFCHAIN(interp);
2284+
op = refchain->_ob_next;
22742285
res = PyList_New(0);
22752286
if (res == NULL)
22762287
return NULL;
2277-
for (i = 0; (n == 0 || i < n) && op != &refchain; i++) {
2288+
for (i = 0; (n == 0 || i < n) && op != refchain; i++) {
22782289
while (op == self || op == args || op == res || op == t ||
22792290
(t != NULL && !Py_IS_TYPE(op, (PyTypeObject *) t))) {
22802291
op = op->_ob_next;
2281-
if (op == &refchain)
2292+
if (op == refchain)
22822293
return res;
22832294
}
22842295
if (PyList_Append(res, op) < 0) {
@@ -2290,6 +2301,8 @@ _Py_GetObjects(PyObject *self, PyObject *args)
22902301
return res;
22912302
}
22922303

2304+
#undef REFCHAIN
2305+
22932306
#endif
22942307

22952308

Python/pylifecycle.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1920,11 +1920,11 @@ Py_FinalizeEx(void)
19201920
}
19211921

19221922
if (dump_refs) {
1923-
_Py_PrintReferences(stderr);
1923+
_Py_PrintReferences(tstate->interp, stderr);
19241924
}
19251925

19261926
if (dump_refs_fp != NULL) {
1927-
_Py_PrintReferences(dump_refs_fp);
1927+
_Py_PrintReferences(tstate->interp, dump_refs_fp);
19281928
}
19291929
#endif /* Py_TRACE_REFS */
19301930

@@ -1960,11 +1960,11 @@ Py_FinalizeEx(void)
19601960
*/
19611961

19621962
if (dump_refs) {
1963-
_Py_PrintReferenceAddresses(stderr);
1963+
_Py_PrintReferenceAddresses(tstate->interp, stderr);
19641964
}
19651965

19661966
if (dump_refs_fp != NULL) {
1967-
_Py_PrintReferenceAddresses(dump_refs_fp);
1967+
_Py_PrintReferenceAddresses(tstate->interp, dump_refs_fp);
19681968
fclose(dump_refs_fp);
19691969
}
19701970
#endif /* Py_TRACE_REFS */

0 commit comments

Comments
 (0)