Skip to content

Commit eba5bf2

Browse files
authored
bpo-42208: Call GC collect earlier in PyInterpreterState_Clear() (GH-23044)
The last GC collection is now done before clearing builtins and sys dictionaries. Add also assertions to ensure that gc.collect() is no longer called after _PyGC_Fini(). Pass also the tstate to PyInterpreterState_Clear() to pass the correct tstate to _PyGC_CollectNoFail() and _PyGC_Fini().
1 parent 4fe7209 commit eba5bf2

File tree

4 files changed

+35
-15
lines changed

4 files changed

+35
-15
lines changed

Include/internal/pycore_interp.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ extern PyStatus _PyInterpreterState_SetConfig(
267267
PyInterpreterState *interp,
268268
const PyConfig *config);
269269

270+
extern void _PyInterpreterState_Clear(PyThreadState *tstate);
270271

271272

272273
/* cross-interpreter data registry */

Modules/gcmodule.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1191,6 +1191,11 @@ gc_collect_main(PyThreadState *tstate, int generation,
11911191
_PyTime_t t1 = 0; /* initialize to prevent a compiler warning */
11921192
GCState *gcstate = &tstate->interp->gc;
11931193

1194+
// gc_collect_main() must not be called before _PyGC_Init
1195+
// or after _PyGC_Fini()
1196+
assert(gcstate->garbage != NULL);
1197+
assert(!_PyErr_Occurred(tstate));
1198+
11941199
#ifdef EXPERIMENTAL_ISOLATED_SUBINTERPRETERS
11951200
if (tstate->interp->config._isolated_interpreter) {
11961201
// bpo-40533: The garbage collector must not be run on parallel on
@@ -2073,16 +2078,13 @@ PyGC_Collect(void)
20732078
Py_ssize_t
20742079
_PyGC_CollectNoFail(PyThreadState *tstate)
20752080
{
2076-
assert(!_PyErr_Occurred(tstate));
2077-
2078-
GCState *gcstate = &tstate->interp->gc;
2079-
20802081
/* Ideally, this function is only called on interpreter shutdown,
20812082
and therefore not recursively. Unfortunately, when there are daemon
20822083
threads, a daemon thread can start a cyclic garbage collection
20832084
during interpreter shutdown (and then never finish it).
20842085
See http://bugs.python.org/issue8713#msg195178 for an example.
20852086
*/
2087+
GCState *gcstate = &tstate->interp->gc;
20862088
if (gcstate->collecting) {
20872089
return 0;
20882090
}

Python/pylifecycle.c

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1576,10 +1576,7 @@ finalize_interp_clear(PyThreadState *tstate)
15761576
int is_main_interp = _Py_IsMainInterpreter(tstate);
15771577

15781578
/* Clear interpreter state and all thread states */
1579-
PyInterpreterState_Clear(tstate->interp);
1580-
1581-
/* Last explicit GC collection */
1582-
_PyGC_CollectNoFail(tstate);
1579+
_PyInterpreterState_Clear(tstate);
15831580

15841581
/* Clear all loghooks */
15851582
/* Both _PySys_Audit function and users still need PyObject, such as tuple.
@@ -1588,8 +1585,6 @@ finalize_interp_clear(PyThreadState *tstate)
15881585
_PySys_ClearAuditHooks(tstate);
15891586
}
15901587

1591-
_PyGC_Fini(tstate);
1592-
15931588
if (is_main_interp) {
15941589
_Py_HashRandomization_Fini();
15951590
_PyArg_Fini();

Python/pystate.c

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -268,14 +268,11 @@ PyInterpreterState_New(void)
268268
}
269269

270270

271-
void
272-
PyInterpreterState_Clear(PyInterpreterState *interp)
271+
static void
272+
interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate)
273273
{
274274
_PyRuntimeState *runtime = interp->runtime;
275275

276-
/* Use the current Python thread state to call audit hooks,
277-
not the current Python thread state of 'interp'. */
278-
PyThreadState *tstate = _PyThreadState_GET();
279276
if (_PySys_Audit(tstate, "cpython.PyInterpreterState_Clear", NULL) < 0) {
280277
_PyErr_Clear(tstate);
281278
}
@@ -306,6 +303,12 @@ PyInterpreterState_Clear(PyInterpreterState *interp)
306303
if (_PyRuntimeState_GetFinalizing(runtime) == NULL) {
307304
_PyWarnings_Fini(interp);
308305
}
306+
307+
/* Last garbage collection on this interpreter */
308+
_PyGC_CollectNoFail(tstate);
309+
310+
_PyGC_Fini(tstate);
311+
309312
/* We don't clear sysdict and builtins until the end of this function.
310313
Because clearing other attributes can execute arbitrary Python code
311314
which requires sysdict and builtins. */
@@ -320,6 +323,25 @@ PyInterpreterState_Clear(PyInterpreterState *interp)
320323
}
321324

322325

326+
void
327+
PyInterpreterState_Clear(PyInterpreterState *interp)
328+
{
329+
// Use the current Python thread state to call audit hooks and to collect
330+
// garbage. It can be different than the current Python thread state
331+
// of 'interp'.
332+
PyThreadState *current_tstate = _PyThreadState_GET();
333+
334+
interpreter_clear(interp, current_tstate);
335+
}
336+
337+
338+
void
339+
_PyInterpreterState_Clear(PyThreadState *tstate)
340+
{
341+
interpreter_clear(tstate->interp, tstate);
342+
}
343+
344+
323345
static void
324346
zapthreads(PyInterpreterState *interp, int check_current)
325347
{

0 commit comments

Comments
 (0)