Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit d0d2f73

Browse files
committed
Share __dict__ and __weakref__ descriptors
1 parent 58c7a39 commit d0d2f73

4 files changed

Lines changed: 78 additions & 45 deletions

File tree

Include/internal/pycore_interp_structs.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -691,6 +691,13 @@ struct _Py_interp_cached_objects {
691691
PyTypeObject *paramspecargs_type;
692692
PyTypeObject *paramspeckwargs_type;
693693
PyTypeObject *constevaluator_type;
694+
695+
/* Descriptors for __dict__ and __weakref__ */
696+
#ifdef Py_GIL_DISABLED
697+
PyMutex descriptor_mutex;
698+
#endif
699+
PyObject *dict_descriptor;
700+
PyObject *weakref_descriptor;
694701
};
695702

696703
struct _Py_interp_static_objects {

Include/internal/pycore_typeobject.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ extern void _PyTypes_FiniTypes(PyInterpreterState *);
4040
extern void _PyTypes_FiniExtTypes(PyInterpreterState *interp);
4141
extern void _PyTypes_Fini(PyInterpreterState *);
4242
extern void _PyTypes_AfterFork(void);
43+
extern void _PyTypes_FiniCachedDescriptors(PyInterpreterState *);
4344

4445
static inline PyObject **
4546
_PyStaticType_GET_WEAKREFS_LISTPTR(managed_static_type_state *state)

Objects/typeobject.c

Lines changed: 69 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -4036,29 +4036,15 @@ subtype_getweakref(PyObject *obj, void *context)
40364036
return Py_NewRef(result);
40374037
}
40384038

4039-
/* Three variants on the subtype_getsets list. */
4040-
4041-
static char subtype_getset_dict_name[] = "__dict__";
4042-
static char subtype_getset_weakref_name[] = "__weakref__";
4043-
4044-
static PyGetSetDef subtype_getsets_full[] = {
4045-
{subtype_getset_dict_name, subtype_dict, subtype_setdict,
4046-
PyDoc_STR("dictionary for instance variables")},
4047-
{subtype_getset_weakref_name, subtype_getweakref, NULL,
4048-
PyDoc_STR("list of weak references to the object")},
4049-
{0}
4039+
/* getset definitions for common descriptors */
4040+
static PyGetSetDef subtype_getset_dict = {
4041+
"__dict__", subtype_dict, subtype_setdict,
4042+
PyDoc_STR("dictionary for instance variables"),
40504043
};
40514044

4052-
static PyGetSetDef subtype_getsets_dict_only[] = {
4053-
{subtype_getset_dict_name, subtype_dict, subtype_setdict,
4054-
PyDoc_STR("dictionary for instance variables")},
4055-
{0}
4056-
};
4057-
4058-
static PyGetSetDef subtype_getsets_weakref_only[] = {
4059-
{subtype_getset_weakref_name, subtype_getweakref, NULL,
4060-
PyDoc_STR("list of weak references to the object")},
4061-
{0}
4045+
static PyGetSetDef subtype_getset_weakref = {
4046+
"__weakref__", subtype_getweakref, NULL,
4047+
PyDoc_STR("list of weak references to the object"),
40624048
};
40634049

40644050
static int
@@ -4594,10 +4580,36 @@ type_new_classmethod(PyObject *dict, PyObject *attr)
45944580
return 0;
45954581
}
45964582

4583+
/* Add __dict__ or __weakref__ descriptor */
4584+
static int
4585+
type_add_common_descriptor(PyInterpreterState *interp,
4586+
PyObject **cache,
4587+
PyGetSetDef *getset_def,
4588+
PyObject *dict)
4589+
{
4590+
#ifdef Py_GIL_DISABLED
4591+
PyMutex_Lock(&interp->cached_objects.descriptor_mutex);
4592+
#endif
4593+
PyObject *descr = *cache;
4594+
if (!descr) {
4595+
descr = PyDescr_NewGetSet(&PyBaseObject_Type, getset_def);
4596+
*cache = descr;
4597+
}
4598+
#ifdef Py_GIL_DISABLED
4599+
PyMutex_Unlock(&interp->cached_objects.descriptor_mutex);
4600+
#endif
4601+
if (!descr) {
4602+
return -1;
4603+
}
4604+
if (PyDict_SetDefaultRef(dict, PyDescr_NAME(descr), descr, NULL) < 0) {
4605+
return -1;
4606+
}
4607+
return 0;
4608+
}
45974609

45984610
/* Add descriptors for custom slots from __slots__, or for __dict__ */
45994611
static int
4600-
type_new_descriptors(const type_new_ctx *ctx, PyTypeObject *type)
4612+
type_new_descriptors(const type_new_ctx *ctx, PyTypeObject *type, PyObject *dict)
46014613
{
46024614
PyHeapTypeObject *et = (PyHeapTypeObject *)type;
46034615
Py_ssize_t slotoffset = ctx->base->tp_basicsize;
@@ -4635,25 +4647,38 @@ type_new_descriptors(const type_new_ctx *ctx, PyTypeObject *type)
46354647
type->tp_basicsize = slotoffset;
46364648
type->tp_itemsize = ctx->base->tp_itemsize;
46374649
type->tp_members = _PyHeapType_GET_MEMBERS(et);
4650+
4651+
PyInterpreterState *interp = _PyInterpreterState_GET();
4652+
4653+
if (type->tp_dictoffset) {
4654+
if (type_add_common_descriptor(
4655+
interp,
4656+
&interp->cached_objects.dict_descriptor,
4657+
&subtype_getset_dict,
4658+
dict) < 0)
4659+
{
4660+
return -1;
4661+
}
4662+
}
4663+
if (type->tp_weaklistoffset) {
4664+
if (type_add_common_descriptor(
4665+
interp,
4666+
&interp->cached_objects.weakref_descriptor,
4667+
&subtype_getset_weakref,
4668+
dict) < 0)
4669+
{
4670+
return -1;
4671+
}
4672+
}
4673+
46384674
return 0;
46394675
}
46404676

46414677

46424678
static void
46434679
type_new_set_slots(const type_new_ctx *ctx, PyTypeObject *type)
46444680
{
4645-
if (type->tp_weaklistoffset && type->tp_dictoffset) {
4646-
type->tp_getset = subtype_getsets_full;
4647-
}
4648-
else if (type->tp_weaklistoffset && !type->tp_dictoffset) {
4649-
type->tp_getset = subtype_getsets_weakref_only;
4650-
}
4651-
else if (!type->tp_weaklistoffset && type->tp_dictoffset) {
4652-
type->tp_getset = subtype_getsets_dict_only;
4653-
}
4654-
else {
4655-
type->tp_getset = NULL;
4656-
}
4681+
type->tp_getset = NULL;
46574682

46584683
/* Special case some slots */
46594684
if (type->tp_dictoffset != 0 || ctx->nslot > 0) {
@@ -4758,7 +4783,7 @@ type_new_set_attrs(const type_new_ctx *ctx, PyTypeObject *type)
47584783
return -1;
47594784
}
47604785

4761-
if (type_new_descriptors(ctx, type) < 0) {
4786+
if (type_new_descriptors(ctx, type, dict) < 0) {
47624787
return -1;
47634788
}
47644789

@@ -6642,6 +6667,14 @@ _PyStaticType_FiniBuiltin(PyInterpreterState *interp, PyTypeObject *type)
66426667
}
66436668

66446669

6670+
void
6671+
_PyTypes_FiniCachedDescriptors(PyInterpreterState *interp)
6672+
{
6673+
Py_CLEAR(interp->cached_objects.dict_descriptor);
6674+
Py_CLEAR(interp->cached_objects.weakref_descriptor);
6675+
}
6676+
6677+
66456678
static void
66466679
type_dealloc(PyObject *self)
66476680
{
@@ -8332,16 +8365,7 @@ type_add_getset(PyTypeObject *type)
83328365

83338366
PyObject *dict = lookup_tp_dict(type);
83348367
for (; gsp->name != NULL; gsp++) {
8335-
PyTypeObject *descr_type = type;
8336-
// Hack: dict and weakref descriptors are created for `object`,
8337-
// rather than this specific type.
8338-
// We identify their PyGetSetDef by pointer equality on name.
8339-
if (gsp->name == subtype_getset_dict_name
8340-
|| gsp->name == subtype_getset_weakref_name)
8341-
{
8342-
descr_type = &PyBaseObject_Type;
8343-
}
8344-
PyObject *descr = PyDescr_NewGetSet(descr_type, gsp);
8368+
PyObject *descr = PyDescr_NewGetSet(type, gsp);
83458369
if (descr == NULL) {
83468370
return -1;
83478371
}

Python/pylifecycle.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1906,6 +1906,7 @@ finalize_interp_clear(PyThreadState *tstate)
19061906
_PyXI_Fini(tstate->interp);
19071907
_PyExc_ClearExceptionGroupType(tstate->interp);
19081908
_Py_clear_generic_types(tstate->interp);
1909+
_PyTypes_FiniCachedDescriptors(tstate->interp);
19091910

19101911
/* Clear interpreter state and all thread states */
19111912
_PyInterpreterState_Clear(tstate);

0 commit comments

Comments
 (0)