Skip to content

Commit

Permalink
gh-94673: More Per-Interpreter Fields for Builtin Static Types (gh-10…
Browse files Browse the repository at this point in the history
…3912)

his involves moving tp_dict, tp_bases, and tp_mro to PyInterpreterState, in the same way we did for tp_subclasses.  Those three fields are effectively const for builtin static types (unlike tp_subclasses).  In theory we only need to make their values immortal, along with their contents.  However, that isn't such a simple proposition.  (See gh-103823.)  In the meantime the simplest solution is to move the fields into the interpreter.

One alternative is to statically allocate the values, but that's its own can of worms.
  • Loading branch information
ericsnowcurrently committed May 3, 2023
1 parent 872cbc6 commit de64e75
Show file tree
Hide file tree
Showing 5 changed files with 185 additions and 86 deletions.
7 changes: 7 additions & 0 deletions Include/internal/pycore_typeobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@ struct type_cache {

typedef struct {
PyTypeObject *type;
int readying;
int ready;
// XXX tp_dict, tp_bases, and tp_mro can probably be statically
// allocated, instead of dynamically and stored on the interpreter.
PyObject *tp_dict;
PyObject *tp_bases;
PyObject *tp_mro;
PyObject *tp_subclasses;
/* We never clean up weakrefs for static builtin types since
they will effectively never get triggered. However, there
Expand Down
1 change: 1 addition & 0 deletions Modules/_abc.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "pycore_moduleobject.h" // _PyModule_GetState()
#include "pycore_object.h" // _PyType_GetSubclasses()
#include "pycore_runtime.h" // _Py_ID()
#include "pycore_typeobject.h" // _PyType_GetMRO()
#include "clinic/_abc.c.h"

/*[clinic input]
Expand Down
31 changes: 3 additions & 28 deletions Modules/gcmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -2174,41 +2174,16 @@ _PyGC_DumpShutdownStats(PyInterpreterState *interp)
}


static void
gc_fini_untrack(PyGC_Head *list)
{
PyGC_Head *gc;
for (gc = GC_NEXT(list); gc != list; gc = GC_NEXT(list)) {
PyObject *op = FROM_GC(gc);
_PyObject_GC_UNTRACK(op);
// gh-92036: If a deallocator function expect the object to be tracked
// by the GC (ex: func_dealloc()), it can crash if called on an object
// which is no longer tracked by the GC. Leak one strong reference on
// purpose so the object is never deleted and its deallocator is not
// called.
Py_INCREF(op);
}
}


void
_PyGC_Fini(PyInterpreterState *interp)
{
GCState *gcstate = &interp->gc;
Py_CLEAR(gcstate->garbage);
Py_CLEAR(gcstate->callbacks);

if (!_Py_IsMainInterpreter(interp)) {
// bpo-46070: Explicitly untrack all objects currently tracked by the
// GC. Otherwise, if an object is used later by another interpreter,
// calling PyObject_GC_UnTrack() on the object crashs if the previous
// or the next object of the PyGC_Head structure became a dangling
// pointer.
for (int i = 0; i < NUM_GENERATIONS; i++) {
PyGC_Head *gen = GEN_HEAD(gcstate, i);
gc_fini_untrack(gen);
}
}
/* We expect that none of this interpreters objects are shared
with other interpreters.
See https://github.com/python/cpython/issues/90228. */
}

/* for debugging */
Expand Down
9 changes: 2 additions & 7 deletions Objects/structseq.c
Original file line number Diff line number Diff line change
Expand Up @@ -511,7 +511,6 @@ _PyStructSequence_InitBuiltinWithFlags(PyInterpreterState *interp,
Py_ssize_t n_members = count_members(desc, &n_unnamed_members);
PyMemberDef *members = NULL;

int initialized = 1;
if ((type->tp_flags & Py_TPFLAGS_READY) == 0) {
assert(type->tp_name == NULL);
assert(type->tp_members == NULL);
Expand All @@ -524,7 +523,6 @@ _PyStructSequence_InitBuiltinWithFlags(PyInterpreterState *interp,
initialize_static_fields(type, desc, members, tp_flags);

_Py_SetImmortal(type);
initialized = 0;
}
#ifndef NDEBUG
else {
Expand All @@ -543,13 +541,10 @@ _PyStructSequence_InitBuiltinWithFlags(PyInterpreterState *interp,
desc->name);
goto error;
}
// This should be dropped if tp_dict is made per-interpreter.
if (initialized) {
return 0;
}

if (initialize_structseq_dict(
desc, _PyType_GetDict(type), n_members, n_unnamed_members) < 0) {
desc, _PyType_GetDict(type), n_members, n_unnamed_members) < 0)
{
goto error;
}

Expand Down
Loading

0 comments on commit de64e75

Please sign in to comment.