Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitc38ceb0

Browse files
[3.12]gh-105020: Share tp_bases and tp_mro Between Interpreters For All Static Builtin Types (gh-105115) (gh-105124)
Ingh-103912 we added tp_bases and tp_mro to each PyInterpreterState.types.builtins entry. However, doing so ignored the fact that both PyTypeObject fields are public API, and not documented as internal (as opposed to tp_subclasses). We address that here by reverting back to shared objects, making them immortal in the process.(cherry picked from commit7be667d)Co-authored-by: Eric Snow ericsnowcurrently@gmail.com
1 parent83c7386 commitc38ceb0

File tree

7 files changed

+26544
-26462
lines changed

7 files changed

+26544
-26462
lines changed

‎Doc/data/python3.12.abi

Lines changed: 26422 additions & 26428 deletions
Large diffs are not rendered by default.

‎Include/internal/pycore_object.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,21 @@ static inline void _Py_SetImmortal(PyObject *op)
7676
}
7777
#define_Py_SetImmortal(op) _Py_SetImmortal(_PyObject_CAST(op))
7878

79+
/* _Py_ClearImmortal() should only be used during runtime finalization. */
80+
staticinlinevoid_Py_ClearImmortal(PyObject*op)
81+
{
82+
if (op) {
83+
assert(op->ob_refcnt==_Py_IMMORTAL_REFCNT);
84+
op->ob_refcnt=1;
85+
Py_DECREF(op);
86+
}
87+
}
88+
#define_Py_ClearImmortal(op) \
89+
do { \
90+
_Py_ClearImmortal(_PyObject_CAST(op)); \
91+
op = NULL; \
92+
} while (0)
93+
7994
staticinlinevoid
8095
_Py_DECREF_SPECIALIZED(PyObject*op,constdestructordestruct)
8196
{

‎Include/internal/pycore_typeobject.h

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,9 @@ typedef struct {
4646
PyTypeObject*type;
4747
intreadying;
4848
intready;
49-
// XXX tp_dict, tp_bases, and tp_mrocan probably be statically
50-
//allocated,instead of dynamically and stored on the interpreter.
49+
// XXX tp_dictcan probably be statically allocated,
50+
// instead of dynamically and stored on the interpreter.
5151
PyObject*tp_dict;
52-
PyObject*tp_bases;
53-
PyObject*tp_mro;
5452
PyObject*tp_subclasses;
5553
/* We never clean up weakrefs for static builtin types since
5654
they will effectively never get triggered. However, there

‎Lib/test/test_capi/test_misc.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1593,6 +1593,39 @@ def test_module_state_shared_in_global(self):
15931593
self.assertEqual(main_attr_id,subinterp_attr_id)
15941594

15951595

1596+
classBuiltinStaticTypesTests(unittest.TestCase):
1597+
1598+
TYPES= [
1599+
object,
1600+
type,
1601+
int,
1602+
str,
1603+
dict,
1604+
type(None),
1605+
bool,
1606+
BaseException,
1607+
Exception,
1608+
Warning,
1609+
DeprecationWarning,# Warning subclass
1610+
]
1611+
1612+
deftest_tp_bases_is_set(self):
1613+
# PyTypeObject.tp_bases is documented as public API.
1614+
# See https://github.com/python/cpython/issues/105020.
1615+
fortypeobjinself.TYPES:
1616+
withself.subTest(typeobj):
1617+
bases=_testcapi.type_get_tp_bases(typeobj)
1618+
self.assertIsNot(bases,None)
1619+
1620+
deftest_tp_mro_is_set(self):
1621+
# PyTypeObject.tp_bases is documented as public API.
1622+
# See https://github.com/python/cpython/issues/105020.
1623+
fortypeobjinself.TYPES:
1624+
withself.subTest(typeobj):
1625+
mro=_testcapi.type_get_tp_mro(typeobj)
1626+
self.assertIsNot(mro,None)
1627+
1628+
15961629
classTestThreadState(unittest.TestCase):
15971630

15981631
@threading_helper.reap_threads
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
``PyTypeObject.tp_bases`` (and ``tp_mro``) for builtin static types are now
2+
shared by all interpreters, whereas in 3.12-beta1 they were stored on
3+
``PyInterpreterState``. Also note that now the tuples are immortal objects.

‎Modules/_testcapimodule.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2606,6 +2606,27 @@ type_assign_version(PyObject *self, PyObject *type)
26062606
}
26072607

26082608

2609+
staticPyObject*
2610+
type_get_tp_bases(PyObject*self,PyObject*type)
2611+
{
2612+
PyObject*bases= ((PyTypeObject*)type)->tp_bases;
2613+
if (bases==NULL) {
2614+
Py_RETURN_NONE;
2615+
}
2616+
returnPy_NewRef(bases);
2617+
}
2618+
2619+
staticPyObject*
2620+
type_get_tp_mro(PyObject*self,PyObject*type)
2621+
{
2622+
PyObject*mro= ((PyTypeObject*)type)->tp_mro;
2623+
if (mro==NULL) {
2624+
Py_RETURN_NONE;
2625+
}
2626+
returnPy_NewRef(mro);
2627+
}
2628+
2629+
26092630
// Test PyThreadState C API
26102631
staticPyObject*
26112632
test_tstate_capi(PyObject*self,PyObject*Py_UNUSED(args))
@@ -3361,6 +3382,8 @@ static PyMethodDef TestMethods[] = {
33613382
{"test_py_is_funcs",test_py_is_funcs,METH_NOARGS},
33623383
{"type_get_version",type_get_version,METH_O,PyDoc_STR("type->tp_version_tag")},
33633384
{"type_assign_version",type_assign_version,METH_O,PyDoc_STR("PyUnstable_Type_AssignVersionTag")},
3385+
{"type_get_tp_bases",type_get_tp_bases,METH_O},
3386+
{"type_get_tp_mro",type_get_tp_mro,METH_O},
33643387
{"test_tstate_capi",test_tstate_capi,METH_NOARGS,NULL},
33653388
{"frame_getlocals",frame_getlocals,METH_O,NULL},
33663389
{"frame_getglobals",frame_getglobals,METH_O,NULL},

‎Objects/typeobject.c

Lines changed: 46 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -268,12 +268,6 @@ clear_tp_dict(PyTypeObject *self)
268268
staticinlinePyObject*
269269
lookup_tp_bases(PyTypeObject*self)
270270
{
271-
if (self->tp_flags&_Py_TPFLAGS_STATIC_BUILTIN) {
272-
PyInterpreterState*interp=_PyInterpreterState_GET();
273-
static_builtin_state*state=_PyStaticType_GetState(interp,self);
274-
assert(state!=NULL);
275-
returnstate->tp_bases;
276-
}
277271
returnself->tp_bases;
278272
}
279273

@@ -287,12 +281,22 @@ _PyType_GetBases(PyTypeObject *self)
287281
staticinlinevoid
288282
set_tp_bases(PyTypeObject*self,PyObject*bases)
289283
{
284+
assert(PyTuple_CheckExact(bases));
290285
if (self->tp_flags&_Py_TPFLAGS_STATIC_BUILTIN) {
291-
PyInterpreterState*interp=_PyInterpreterState_GET();
292-
static_builtin_state*state=_PyStaticType_GetState(interp,self);
293-
assert(state!=NULL);
294-
state->tp_bases=bases;
295-
return;
286+
// XXX tp_bases can probably be statically allocated for each
287+
// static builtin type.
288+
assert(_Py_IsMainInterpreter(_PyInterpreterState_GET()));
289+
assert(self->tp_bases==NULL);
290+
if (PyTuple_GET_SIZE(bases)==0) {
291+
assert(self->tp_base==NULL);
292+
}
293+
else {
294+
assert(PyTuple_GET_SIZE(bases)==1);
295+
assert(PyTuple_GET_ITEM(bases,0)== (PyObject*)self->tp_base);
296+
assert(self->tp_base->tp_flags&_Py_TPFLAGS_STATIC_BUILTIN);
297+
assert(_Py_IsImmortal(self->tp_base));
298+
}
299+
_Py_SetImmortal(bases);
296300
}
297301
self->tp_bases=bases;
298302
}
@@ -301,10 +305,14 @@ static inline void
301305
clear_tp_bases(PyTypeObject*self)
302306
{
303307
if (self->tp_flags&_Py_TPFLAGS_STATIC_BUILTIN) {
304-
PyInterpreterState*interp=_PyInterpreterState_GET();
305-
static_builtin_state*state=_PyStaticType_GetState(interp,self);
306-
assert(state!=NULL);
307-
Py_CLEAR(state->tp_bases);
308+
if (_Py_IsMainInterpreter(_PyInterpreterState_GET())) {
309+
if (self->tp_bases!=NULL
310+
&&PyTuple_GET_SIZE(self->tp_bases)>0)
311+
{
312+
assert(_Py_IsImmortal(self->tp_bases));
313+
_Py_ClearImmortal(self->tp_bases);
314+
}
315+
}
308316
return;
309317
}
310318
Py_CLEAR(self->tp_bases);
@@ -314,12 +322,6 @@ clear_tp_bases(PyTypeObject *self)
314322
staticinlinePyObject*
315323
lookup_tp_mro(PyTypeObject*self)
316324
{
317-
if (self->tp_flags&_Py_TPFLAGS_STATIC_BUILTIN) {
318-
PyInterpreterState*interp=_PyInterpreterState_GET();
319-
static_builtin_state*state=_PyStaticType_GetState(interp,self);
320-
assert(state!=NULL);
321-
returnstate->tp_mro;
322-
}
323325
returnself->tp_mro;
324326
}
325327

@@ -333,12 +335,14 @@ _PyType_GetMRO(PyTypeObject *self)
333335
staticinlinevoid
334336
set_tp_mro(PyTypeObject*self,PyObject*mro)
335337
{
338+
assert(PyTuple_CheckExact(mro));
336339
if (self->tp_flags&_Py_TPFLAGS_STATIC_BUILTIN) {
337-
PyInterpreterState*interp=_PyInterpreterState_GET();
338-
static_builtin_state*state=_PyStaticType_GetState(interp,self);
339-
assert(state!=NULL);
340-
state->tp_mro=mro;
341-
return;
340+
// XXX tp_mro can probably be statically allocated for each
341+
// static builtin type.
342+
assert(_Py_IsMainInterpreter(_PyInterpreterState_GET()));
343+
assert(self->tp_mro==NULL);
344+
/* Other checks are done via set_tp_bases. */
345+
_Py_SetImmortal(mro);
342346
}
343347
self->tp_mro=mro;
344348
}
@@ -347,10 +351,14 @@ static inline void
347351
clear_tp_mro(PyTypeObject*self)
348352
{
349353
if (self->tp_flags&_Py_TPFLAGS_STATIC_BUILTIN) {
350-
PyInterpreterState*interp=_PyInterpreterState_GET();
351-
static_builtin_state*state=_PyStaticType_GetState(interp,self);
352-
assert(state!=NULL);
353-
Py_CLEAR(state->tp_mro);
354+
if (_Py_IsMainInterpreter(_PyInterpreterState_GET())) {
355+
if (self->tp_mro!=NULL
356+
&&PyTuple_GET_SIZE(self->tp_mro)>0)
357+
{
358+
assert(_Py_IsImmortal(self->tp_mro));
359+
_Py_ClearImmortal(self->tp_mro);
360+
}
361+
}
354362
return;
355363
}
356364
Py_CLEAR(self->tp_mro);
@@ -7153,6 +7161,14 @@ type_ready_preheader(PyTypeObject *type)
71537161
staticint
71547162
type_ready_mro(PyTypeObject*type)
71557163
{
7164+
if (type->tp_flags&_Py_TPFLAGS_STATIC_BUILTIN) {
7165+
if (!_Py_IsMainInterpreter(_PyInterpreterState_GET())) {
7166+
assert(lookup_tp_mro(type)!=NULL);
7167+
return0;
7168+
}
7169+
assert(lookup_tp_mro(type)==NULL);
7170+
}
7171+
71567172
/* Calculate method resolution order */
71577173
if (mro_internal(type,NULL)<0) {
71587174
return-1;

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp