Skip to content

Commit fd957c1

Browse files
authored
bpo-41796: Call _PyAST_Fini() earlier to fix a leak (GH-23131)
Call _PyAST_Fini() on all interpreters, not only on the main interpreter. Also, call it ealier to fix a reference leak. Python types contain a reference to themselves in in their PyTypeObject.tp_mro member. _PyAST_Fini() must called before the last GC collection to destroy AST types. _PyInterpreterState_Clear() now calls _PyAST_Fini(). It now also calls _PyWarnings_Fini() on subinterpeters, not only on the main interpreter. Add an assertion in AST init_types() to ensure that the _ast module is no longer used after _PyAST_Fini() has been called.
1 parent 212d32f commit fd957c1

File tree

5 files changed

+79
-44
lines changed

5 files changed

+79
-44
lines changed

Include/internal/pycore_pylifecycle.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ extern void _PyFaulthandler_Fini(void);
8484
extern void _PyHash_Fini(void);
8585
extern void _PyTraceMalloc_Fini(void);
8686
extern void _PyWarnings_Fini(PyInterpreterState *interp);
87-
extern void _PyAST_Fini(PyThreadState *tstate);
87+
extern void _PyAST_Fini(PyInterpreterState *interp);
8888

8989
extern PyStatus _PyGILState_Init(PyThreadState *tstate);
9090
extern void _PyGILState_Fini(PyThreadState *tstate);

Parser/asdl_c.py

+45-24
Original file line numberDiff line numberDiff line change
@@ -1015,18 +1015,35 @@ def visitModule(self, mod):
10151015
10161016
""", 0, reflow=False)
10171017

1018-
self.emit("static int init_types(struct ast_state *state)",0)
1019-
self.emit("{", 0)
1020-
self.emit("if (state->initialized) return 1;", 1)
1021-
self.emit("if (init_identifiers(state) < 0) return 0;", 1)
1022-
self.emit("state->AST_type = PyType_FromSpec(&AST_type_spec);", 1)
1023-
self.emit("if (!state->AST_type) return 0;", 1)
1024-
self.emit("if (add_ast_fields(state) < 0) return 0;", 1)
1018+
self.file.write(textwrap.dedent('''
1019+
static int
1020+
init_types(struct ast_state *state)
1021+
{
1022+
// init_types() must not be called after _PyAST_Fini()
1023+
// has been called
1024+
assert(state->initialized >= 0);
1025+
1026+
if (state->initialized) {
1027+
return 1;
1028+
}
1029+
if (init_identifiers(state) < 0) {
1030+
return 0;
1031+
}
1032+
state->AST_type = PyType_FromSpec(&AST_type_spec);
1033+
if (!state->AST_type) {
1034+
return 0;
1035+
}
1036+
if (add_ast_fields(state) < 0) {
1037+
return 0;
1038+
}
1039+
'''))
10251040
for dfn in mod.dfns:
10261041
self.visit(dfn)
1027-
self.emit("state->initialized = 1;", 1)
1028-
self.emit("return 1;", 1);
1029-
self.emit("}", 0)
1042+
self.file.write(textwrap.dedent('''
1043+
state->initialized = 1;
1044+
return 1;
1045+
}
1046+
'''))
10301047

10311048
def visitProduct(self, prod, name):
10321049
if prod.fields:
@@ -1353,23 +1370,27 @@ def generate_ast_state(module_state, f):
13531370

13541371

13551372
def generate_ast_fini(module_state, f):
1356-
f.write("""
1357-
void _PyAST_Fini(PyThreadState *tstate)
1358-
{
1359-
#ifdef Py_BUILD_CORE
1360-
struct ast_state *state = &tstate->interp->ast;
1361-
#else
1362-
struct ast_state *state = &global_ast_state;
1363-
#endif
1364-
1365-
""")
1373+
f.write(textwrap.dedent("""
1374+
void _PyAST_Fini(PyInterpreterState *interp)
1375+
{
1376+
#ifdef Py_BUILD_CORE
1377+
struct ast_state *state = &interp->ast;
1378+
#else
1379+
struct ast_state *state = &global_ast_state;
1380+
#endif
1381+
1382+
"""))
13661383
for s in module_state:
13671384
f.write(" Py_CLEAR(state->" + s + ');\n')
1368-
f.write("""
1369-
state->initialized = 0;
1370-
}
1385+
f.write(textwrap.dedent("""
1386+
#if defined(Py_BUILD_CORE) && !defined(NDEBUG)
1387+
state->initialized = -1;
1388+
#else
1389+
state->initialized = 0;
1390+
#endif
1391+
}
13711392
1372-
""")
1393+
"""))
13731394

13741395

13751396
def generate_module_def(mod, f, internal_h):

Python/Python-ast.c

+26-7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/pylifecycle.c

-8
Original file line numberDiff line numberDiff line change
@@ -1545,12 +1545,6 @@ flush_std_files(void)
15451545
static void
15461546
finalize_interp_types(PyThreadState *tstate)
15471547
{
1548-
// The _ast module state is shared by all interpreters.
1549-
// The state must only be cleared by the main interpreter.
1550-
if (_Py_IsMainInterpreter(tstate)) {
1551-
_PyAST_Fini(tstate);
1552-
}
1553-
15541548
_PyExc_Fini(tstate);
15551549
_PyFrame_Fini(tstate);
15561550
_PyAsyncGen_Fini(tstate);
@@ -1591,8 +1585,6 @@ finalize_interp_clear(PyThreadState *tstate)
15911585
_Py_ClearFileSystemEncoding();
15921586
}
15931587

1594-
_PyWarnings_Fini(tstate->interp);
1595-
15961588
finalize_interp_types(tstate);
15971589
}
15981590

Python/pystate.c

+7-4
Original file line numberDiff line numberDiff line change
@@ -300,13 +300,16 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate)
300300
Py_CLEAR(interp->after_forkers_parent);
301301
Py_CLEAR(interp->after_forkers_child);
302302
#endif
303-
if (_PyRuntimeState_GetFinalizing(runtime) == NULL) {
304-
_PyWarnings_Fini(interp);
305-
}
303+
304+
_PyAST_Fini(interp);
305+
_PyWarnings_Fini(interp);
306+
307+
// All Python types must be destroyed before the last GC collection. Python
308+
// types create a reference cycle to themselves in their in their
309+
// PyTypeObject.tp_mro member (the tuple contains the type).
306310

307311
/* Last garbage collection on this interpreter */
308312
_PyGC_CollectNoFail(tstate);
309-
310313
_PyGC_Fini(tstate);
311314

312315
/* We don't clear sysdict and builtins until the end of this function.

0 commit comments

Comments
 (0)