Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gh-94673: Add Per-Interpreter Storage for Static Builtin Types #94995

Closed
Closed
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
5de3f04
Add PyInterpreterState.types.
ericsnowcurrently Jul 8, 2022
24aea28
Add _PyStaticType_InitBuiltin().
ericsnowcurrently Jul 9, 2022
b1cc7d2
Call PyType_Ready() in _PyStaticType_InitBuiltin().
ericsnowcurrently Jul 15, 2022
c63bccb
Add _PyStructSequence_InitBuiltin().
ericsnowcurrently Jul 15, 2022
0aacc7a
Hard-code _Py_NUM_STATIC_BUILTIN_TYPES.
ericsnowcurrently Jul 18, 2022
a006e21
Store per-interpreter state for static builtin types.
ericsnowcurrently Jul 19, 2022
e3ed4fd
Export _PyStaticType_GetState().
ericsnowcurrently Jul 19, 2022
0c695b5
Move _PyStaticType_GetState() down next to _PyStaticType_InitBuiltin().
ericsnowcurrently Jul 19, 2022
2df59c1
make it compile
kumaraditya303 Jul 19, 2022
5d76cfa
Fix a typo.
ericsnowcurrently Jul 19, 2022
d05be58
fix test_sys
kumaraditya303 Jul 19, 2022
1a3d6f1
Merge branch 'main' into init-static-builtin
ericsnowcurrently Jul 20, 2022
160738b
Ignore non-builtin static types in _PyStaticType_Dealloc().
ericsnowcurrently Jul 20, 2022
7655114
Remember if a type is a static builtin.
ericsnowcurrently Jul 20, 2022
6820fca
Factor out get_static_builtin_index() and set_static_builtin_index().
ericsnowcurrently Jul 20, 2022
98f9eaf
Revert to skipping on subclasses for now.
ericsnowcurrently Jul 20, 2022
a3fd8e3
Throw away the leaking subclasses.
ericsnowcurrently Jul 20, 2022
cb3895c
Reset state->type later on.
ericsnowcurrently Jul 21, 2022
8d7edd7
Add some asserts.
ericsnowcurrently Jul 21, 2022
f057fcf
Decrement num_builtins_initialized.
ericsnowcurrently Jul 21, 2022
bb8264d
Factor out subclass_from_ref().
ericsnowcurrently Jul 22, 2022
d680e8e
Handle MemoryError in remove_subclass().
ericsnowcurrently Jul 22, 2022
41850e8
Call _PyTypes_FiniTypes() before _PyTypes_Fini().
ericsnowcurrently Jul 22, 2022
f1bbbd2
Verify the per-interpreter type state has been cleared.
ericsnowcurrently Jul 22, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Include/cpython/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ struct _typeobject {

destructor tp_finalize;
vectorcallfunc tp_vectorcall;
size_t tp_static_builtin_index; /* 0 means "not initialized" */
};

/* This struct is used by the specializer
Expand Down
2 changes: 1 addition & 1 deletion Include/internal/pycore_interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ struct _is {
struct _Py_exc_state exc_state;

struct ast_state ast;
struct type_cache type_cache;
struct types_state types;
struct callable_cache callable_cache;

/* The following fields are here to avoid allocation during init.
Expand Down
9 changes: 8 additions & 1 deletion Include/internal/pycore_structseq.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,18 @@ PyAPI_FUNC(PyTypeObject *) _PyStructSequence_NewType(
PyStructSequence_Desc *desc,
unsigned long tp_flags);

PyAPI_FUNC(int) _PyStructSequence_InitType(
PyAPI_FUNC(int) _PyStructSequence_InitBuiltinWithFlags(
PyTypeObject *type,
PyStructSequence_Desc *desc,
unsigned long tp_flags);

static inline int
_PyStructSequence_InitBuiltin(PyTypeObject *type,
PyStructSequence_Desc *desc)
{
return _PyStructSequence_InitBuiltinWithFlags(type, desc, 0);
}

extern void _PyStructSequence_FiniType(PyTypeObject *type);

#ifdef __cplusplus
Expand Down
17 changes: 17 additions & 0 deletions Include/internal/pycore_typeobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,25 @@ struct type_cache {
#endif
};

/* For now we hard-code this to a value for which we are confident
all the static builtin types will fit (for all builds). */
#define _Py_MAX_STATIC_BUILTIN_TYPES 200

typedef struct {
PyTypeObject *type;
} static_builtin_type_state;

struct types_state {
struct type_cache type_cache;
size_t num_builtins_initialized;
static_builtin_type_state builtins[_Py_MAX_STATIC_BUILTIN_TYPES];
};


extern PyStatus _PyTypes_InitSlotDefs(void);

extern int _PyStaticType_InitBuiltin(PyTypeObject *type);
extern static_builtin_type_state * _PyStaticType_GetState(PyTypeObject *);
extern void _PyStaticType_Dealloc(PyTypeObject *type);


Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_sys.py
Original file line number Diff line number Diff line change
Expand Up @@ -1507,7 +1507,7 @@ def delx(self): del self.__x
check((1,2,3), vsize('') + 3*self.P)
# type
# static type: PyTypeObject
fmt = 'P2nPI13Pl4Pn9Pn12PIP'
fmt = 'P2nPI13Pl4Pn9Pn12PIPI'
s = vsize('2P' + fmt)
check(int, s)
# class
Expand Down
3 changes: 1 addition & 2 deletions Objects/exceptions.c
Original file line number Diff line number Diff line change
Expand Up @@ -3556,8 +3556,7 @@ _PyExc_InitTypes(PyInterpreterState *interp)

for (size_t i=0; i < Py_ARRAY_LENGTH(static_exceptions); i++) {
PyTypeObject *exc = static_exceptions[i].exc;

if (PyType_Ready(exc) < 0) {
if (_PyStaticType_InitBuiltin(exc) < 0) {
return -1;
}
}
Expand Down
3 changes: 2 additions & 1 deletion Objects/floatobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -1992,7 +1992,8 @@ _PyFloat_InitTypes(PyInterpreterState *interp)

/* Init float info */
if (FloatInfoType.tp_name == NULL) {
if (PyStructSequence_InitType2(&FloatInfoType, &floatinfo_desc) < 0) {
if (_PyStructSequence_InitBuiltin(&FloatInfoType,
&floatinfo_desc) < 0) {
return _PyStatus_ERR("can't init float info type");
}
}
Expand Down
2 changes: 1 addition & 1 deletion Objects/longobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -6135,7 +6135,7 @@ _PyLong_InitTypes(PyInterpreterState *interp)

/* initialize int_info */
if (Int_InfoType.tp_name == NULL) {
if (PyStructSequence_InitType2(&Int_InfoType, &int_info_desc) < 0) {
if (_PyStructSequence_InitBuiltin(&Int_InfoType, &int_info_desc) < 0) {
return _PyStatus_ERR("can't init int info type");
}
}
Expand Down
4 changes: 2 additions & 2 deletions Objects/object.c
Original file line number Diff line number Diff line change
Expand Up @@ -1975,8 +1975,8 @@ _PyTypes_InitTypes(PyInterpreterState *interp)
// All other static types (unless initialized elsewhere)
for (size_t i=0; i < Py_ARRAY_LENGTH(static_types); i++) {
PyTypeObject *type = static_types[i];
if (PyType_Ready(type) < 0) {
return _PyStatus_ERR("Can't initialize types");
if (_PyStaticType_InitBuiltin(type) < 0) {
return _PyStatus_ERR("Can't initialize builtin type");
}
if (type == &PyType_Type) {
// Sanitify checks of the two most important types
Expand Down
121 changes: 81 additions & 40 deletions Objects/structseq.c
Original file line number Diff line number Diff line change
Expand Up @@ -432,11 +432,21 @@ initialize_structseq_dict(PyStructSequence_Desc *desc, PyObject* dict,
return -1;
}

static void
initialize_members(PyStructSequence_Desc *desc, PyMemberDef* members,
Py_ssize_t n_members) {
Py_ssize_t i, k;
static PyMemberDef *
initialize_members(PyStructSequence_Desc *desc,
Py_ssize_t *pn_members, Py_ssize_t *pn_unnamed_members)
{
PyMemberDef *members;
Py_ssize_t n_members, n_unnamed_members;

n_members = count_members(desc, &n_unnamed_members);
members = PyMem_NEW(PyMemberDef, n_members - n_unnamed_members + 1);
if (members == NULL) {
PyErr_NoMemory();
return NULL;
}

Py_ssize_t i, k;
for (i = k = 0; i < n_members; ++i) {
if (desc->fields[i].name == PyStructSequence_UnnamedField) {
continue;
Expand All @@ -453,30 +463,17 @@ initialize_members(PyStructSequence_Desc *desc, PyMemberDef* members,
k++;
}
members[k].name = NULL;

*pn_members = n_members;
*pn_unnamed_members = n_unnamed_members;
return members;
}


int
_PyStructSequence_InitType(PyTypeObject *type, PyStructSequence_Desc *desc,
unsigned long tp_flags)
static void
initialize_static_fields(PyTypeObject *type, PyStructSequence_Desc *desc,
PyMemberDef *tp_members, unsigned long tp_flags)
{
PyMemberDef *members;
Py_ssize_t n_members, n_unnamed_members;

#ifdef Py_TRACE_REFS
/* if the type object was chained, unchain it first
before overwriting its storage */
if (type->ob_base.ob_base._ob_next) {
_Py_ForgetReference((PyObject *)type);
}
#endif

/* PyTypeObject has already been initialized */
if (Py_REFCNT(type) != 0) {
PyErr_BadInternalCall();
return -1;
}

type->tp_name = desc->name;
type->tp_basicsize = sizeof(PyStructSequence) - sizeof(PyObject *);
type->tp_itemsize = sizeof(PyObject *);
Expand All @@ -488,36 +485,83 @@ _PyStructSequence_InitType(PyTypeObject *type, PyStructSequence_Desc *desc,
type->tp_new = structseq_new;
type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | tp_flags;
type->tp_traverse = (traverseproc) structseq_traverse;
type->tp_members = tp_members;
}

n_members = count_members(desc, &n_unnamed_members);
members = PyMem_NEW(PyMemberDef, n_members - n_unnamed_members + 1);
if (members == NULL) {
PyErr_NoMemory();
return -1;
}
initialize_members(desc, members, n_members);
type->tp_members = members;

static int
initialize_static_type(PyTypeObject *type, PyStructSequence_Desc *desc,
Py_ssize_t n_members, Py_ssize_t n_unnamed_members) {
/* initialize_static_fields() should have been called already. */
if (PyType_Ready(type) < 0) {
PyMem_Free(members);
return -1;
}
Py_INCREF(type);

if (initialize_structseq_dict(
desc, type->tp_dict, n_members, n_unnamed_members) < 0) {
PyMem_Free(members);
Py_DECREF(type);
return -1;
}

return 0;
}

int
_PyStructSequence_InitBuiltinWithFlags(PyTypeObject *type,
PyStructSequence_Desc *desc,
unsigned long tp_flags)
{
PyMemberDef *members;
Py_ssize_t n_members, n_unnamed_members;

members = initialize_members(desc, &n_members, &n_unnamed_members);
if (members == NULL) {
return -1;
}
initialize_static_fields(type, desc, members, tp_flags);
if (_PyStaticType_InitBuiltin(type) < 0) {
PyErr_Format(PyExc_RuntimeError,
"Can't initialize builtin type %s",
desc->name);
return -1;
}
if (initialize_static_type(type, desc, n_members, n_unnamed_members) < 0) {
PyMem_Free(members);
return -1;
}
return 0;
}

int
PyStructSequence_InitType2(PyTypeObject *type, PyStructSequence_Desc *desc)
{
return _PyStructSequence_InitType(type, desc, 0);
PyMemberDef *members;
Py_ssize_t n_members, n_unnamed_members;

#ifdef Py_TRACE_REFS
/* if the type object was chained, unchain it first
before overwriting its storage */
if (type->ob_base.ob_base._ob_next) {
_Py_ForgetReference((PyObject *)type);
}
#endif

/* PyTypeObject has already been initialized */
if (Py_REFCNT(type) != 0) {
PyErr_BadInternalCall();
return -1;
}

members = initialize_members(desc, &n_members, &n_unnamed_members);
if (members == NULL) {
return -1;
}
initialize_static_fields(type, desc, members, 0);
if (initialize_static_type(type, desc, n_members, n_unnamed_members) < 0) {
PyMem_Free(members);
return -1;
}
return 0;
}

void
Expand Down Expand Up @@ -569,13 +613,10 @@ _PyStructSequence_NewType(PyStructSequence_Desc *desc, unsigned long tp_flags)
Py_ssize_t n_members, n_unnamed_members;

/* Initialize MemberDefs */
n_members = count_members(desc, &n_unnamed_members);
members = PyMem_NEW(PyMemberDef, n_members - n_unnamed_members + 1);
members = initialize_members(desc, &n_members, &n_unnamed_members);
if (members == NULL) {
PyErr_NoMemory();
return NULL;
}
initialize_members(desc, members, n_members);

/* Initialize Slots */
slots[0] = (PyType_Slot){Py_tp_dealloc, (destructor)structseq_dealloc};
Expand Down
48 changes: 44 additions & 4 deletions Objects/typeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ lookup_maybe_method(PyObject *self, PyObject *attr, int *unbound);
static int
slot_tp_setattro(PyObject *self, PyObject *name, PyObject *value);


Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

/*
* finds the beginning of the docstring's introspection signature.
* if present, returns a pointer pointing to the first '('.
Expand Down Expand Up @@ -204,7 +205,7 @@ static struct type_cache*
get_type_cache(void)
{
PyInterpreterState *interp = _PyInterpreterState_GET();
return &interp->type_cache;
return &interp->types.type_cache;
}


Expand All @@ -223,7 +224,7 @@ type_cache_clear(struct type_cache *cache, PyObject *value)
void
_PyType_InitCache(PyInterpreterState *interp)
{
struct type_cache *cache = &interp->type_cache;
struct type_cache *cache = &interp->types.type_cache;
for (Py_ssize_t i = 0; i < (1 << MCACHE_SIZE_EXP); i++) {
struct type_cache_entry *entry = &cache->hashtable[i];
assert(entry->name == NULL);
Expand All @@ -240,7 +241,7 @@ _PyType_InitCache(PyInterpreterState *interp)
static unsigned int
_PyType_ClearCache(PyInterpreterState *interp)
{
struct type_cache *cache = &interp->type_cache;
struct type_cache *cache = &interp->types.type_cache;
#if MCACHE_STATS
size_t total = cache->hits + cache->collisions + cache->misses;
fprintf(stderr, "-- Method cache hits = %zd (%d%%)\n",
Expand Down Expand Up @@ -272,7 +273,7 @@ PyType_ClearCache(void)
void
_PyTypes_Fini(PyInterpreterState *interp)
{
struct type_cache *cache = &interp->type_cache;
struct type_cache *cache = &interp->types.type_cache;
type_cache_clear(cache, NULL);
if (_Py_IsMainInterpreter(interp)) {
clear_slotdefs();
Expand Down Expand Up @@ -4221,6 +4222,11 @@ _PyStaticType_Dealloc(PyTypeObject *type)
return;
}

static_builtin_type_state *state = _PyStaticType_GetState(type);
if (state != NULL) {
state->type = NULL;
}

type_dealloc_common(type);

Py_CLEAR(type->tp_dict);
Expand All @@ -4235,6 +4241,8 @@ _PyStaticType_Dealloc(PyTypeObject *type)
}

type->tp_flags &= ~Py_TPFLAGS_READY;
// Reset tp_static_builtin_index after each finalization.
type->tp_static_builtin_index = 0;
}


Expand Down Expand Up @@ -6650,6 +6658,38 @@ PyType_Ready(PyTypeObject *type)
return 0;
}

int
_PyStaticType_InitBuiltin(PyTypeObject *self)
{
/* It should only be called once for each builtin type. */
assert(self->tp_static_builtin_index == 0);

/* For static types we store some state in an array on each interpreter. */
PyInterpreterState *interp = _PyInterpreterState_GET();
interp->types.num_builtins_initialized++;
assert(interp->types.num_builtins_initialized < _Py_MAX_STATIC_BUILTIN_TYPES);

/* We use 1-based indexing so 0 can mean "not initialized". */
self->tp_static_builtin_index = interp->types.num_builtins_initialized;

/* Now we initialize the type's per-interpreter state. */
static_builtin_type_state *state = _PyStaticType_GetState(self);
assert(state != NULL);
state->type = self;

return PyType_Ready(self);
}

static_builtin_type_state *
_PyStaticType_GetState(PyTypeObject *self)
{
if (self->tp_static_builtin_index == 0) {
return NULL;
}
PyInterpreterState *interp = _PyInterpreterState_GET();
return &(interp->types.builtins[self->tp_static_builtin_index - 1]);
}


static int
add_subclass(PyTypeObject *base, PyTypeObject *type)
Expand Down
Loading