@@ -347,23 +347,43 @@ _PyStaticType_GetBuiltins(void)
347347static void
348348type_set_flags (PyTypeObject * tp , unsigned long flags )
349349{
350- if (tp -> tp_flags & Py_TPFLAGS_READY ) {
351- // It's possible the type object has been exposed to other threads
352- // if it's been marked ready. In that case, the type lock should be
353- // held when flags are modified.
354- ASSERT_TYPE_LOCK_HELD ();
355- }
356- // Since PyType_HasFeature() reads the flags without holding the type
357- // lock, we need an atomic store here.
358- FT_ATOMIC_STORE_ULONG_RELAXED (tp -> tp_flags , flags );
350+ // In the free-threaded build, this can only be used on types that have
351+ // not yet been exposed to other threads. Use type_set_flags_with_mask()
352+ // for flags that need to be toggled afterwards.
353+ tp -> tp_flags = flags ;
359354}
360355
356+ // Flags used by pattern matching
357+ #define COLLECTION_FLAGS (Py_TPFLAGS_SEQUENCE | Py_TPFLAGS_MAPPING)
358+
359+ // This is the set of flags are allowed to be toggled after type_ready()
360+ // is called (and the type potentially exposed to other threads).
361+ #define POST_READY_FLAGS \
362+ (COLLECTION_FLAGS | Py_TPFLAGS_IS_ABSTRACT | Py_TPFLAGS_HAVE_VECTORCALL)
363+
364+ // Used to toggle type flags after the type has been created. Only the flags
365+ // in POST_READY_FLAGS are allowed to be toggled.
361366static void
362367type_set_flags_with_mask (PyTypeObject * tp , unsigned long mask , unsigned long flags )
363368{
364369 ASSERT_TYPE_LOCK_HELD ();
370+ // Non-heap types are immutable and so these flags can only be toggled
371+ // after creation on heap types.
372+ assert (_PyType_HasFeature (tp , Py_TPFLAGS_HEAPTYPE ));
365373 unsigned long new_flags = (tp -> tp_flags & ~mask ) | flags ;
366- type_set_flags (tp , new_flags );
374+ #ifdef Py_DEBUG
375+ unsigned long changed_flags = tp -> tp_flags ^ new_flags ;
376+ assert ((changed_flags & ~POST_READY_FLAGS ) == 0 );
377+ #endif
378+ #ifdef Py_GIL_DISABLED
379+ PyHeapTypeObject * ht = (PyHeapTypeObject * )tp ;
380+ FT_ATOMIC_STORE_ULONG_RELAXED (ht -> ht_flags , new_flags );
381+ #else
382+ // In this case the GIL protects us and we don't need the separate ht_flags
383+ // member and atomics to read and write. Instead we can just toggle the
384+ // flags directly in tp_flags.
385+ tp -> tp_flags = new_flags ;
386+ #endif
367387}
368388
369389static void
@@ -1318,7 +1338,6 @@ int PyUnstable_Type_AssignVersionTag(PyTypeObject *type)
13181338static PyMemberDef type_members [] = {
13191339 {"__basicsize__" , Py_T_PYSSIZET , offsetof(PyTypeObject ,tp_basicsize ),Py_READONLY },
13201340 {"__itemsize__" , Py_T_PYSSIZET , offsetof(PyTypeObject , tp_itemsize ), Py_READONLY },
1321- {"__flags__" , Py_T_ULONG , offsetof(PyTypeObject , tp_flags ), Py_READONLY },
13221341 /* Note that this value is misleading for static builtin types,
13231342 since the memory at this offset will always be NULL. */
13241343 {"__weakrefoffset__" , Py_T_PYSSIZET ,
@@ -1584,9 +1603,9 @@ type_set_abstractmethods(PyObject *tp, PyObject *value, void *Py_UNUSED(closure)
15841603 BEGIN_TYPE_LOCK ();
15851604 type_modified_unlocked (type );
15861605 if (abstract )
1587- type_add_flags (type , Py_TPFLAGS_IS_ABSTRACT );
1606+ type_set_flags_with_mask (type , 0 , Py_TPFLAGS_IS_ABSTRACT );
15881607 else
1589- type_clear_flags (type , Py_TPFLAGS_IS_ABSTRACT );
1608+ type_set_flags_with_mask (type , Py_TPFLAGS_IS_ABSTRACT , 0 );
15901609 END_TYPE_LOCK ();
15911610
15921611 return 0 ;
@@ -2109,6 +2128,24 @@ type_set_type_params(PyObject *tp, PyObject *value, void *Py_UNUSED(closure))
21092128 return result ;
21102129}
21112130
2131+ static PyObject *
2132+ type_get_flags (PyObject * tp , void * Py_UNUSED (closure ))
2133+ {
2134+ PyTypeObject * type = PyTypeObject_CAST (tp );
2135+ unsigned long flags ;
2136+ #ifdef Py_GIL_DISABLED
2137+ if (_PyType_HasFeature (type , Py_TPFLAGS_HEAPTYPE )) {
2138+ PyHeapTypeObject * ht = (PyHeapTypeObject * )type ;
2139+ flags = FT_ATOMIC_LOAD_ULONG_RELAXED (ht -> ht_flags );
2140+ }
2141+ else {
2142+ flags = type -> tp_flags ;
2143+ }
2144+ #else
2145+ flags = type -> tp_flags ;
2146+ #endif
2147+ return PyLong_FromLong (flags );
2148+ }
21122149
21132150/*[clinic input]
21142151type.__instancecheck__ -> bool
@@ -2157,6 +2194,7 @@ static PyGetSetDef type_getsets[] = {
21572194 {"__annotations__" , type_get_annotations , type_set_annotations , NULL },
21582195 {"__annotate__" , type_get_annotate , type_set_annotate , NULL },
21592196 {"__type_params__" , type_get_type_params , type_set_type_params , NULL },
2197+ {"__flags__" , type_get_flags , NULL , NULL },
21602198 {0 }
21612199};
21622200
@@ -3687,7 +3725,13 @@ type_init(PyObject *cls, PyObject *args, PyObject *kwds)
36873725unsigned long
36883726PyType_GetFlags (PyTypeObject * type )
36893727{
3690- return FT_ATOMIC_LOAD_ULONG_RELAXED (type -> tp_flags );
3728+ #ifdef Py_GIL_DISABLED
3729+ if (_PyType_HasFeature (type , Py_TPFLAGS_HEAPTYPE )) {
3730+ PyHeapTypeObject * ht = (PyHeapTypeObject * )type ;
3731+ return FT_ATOMIC_LOAD_ULONG_RELAXED (ht -> ht_flags );
3732+ }
3733+ #endif
3734+ return type -> tp_flags ;
36913735}
36923736
36933737
@@ -4005,6 +4049,7 @@ type_new_alloc(type_new_ctx *ctx)
40054049
40064050#ifdef Py_GIL_DISABLED
40074051 et -> unique_id = _PyObject_AssignUniqueId ((PyObject * )et );
4052+ et -> ht_flags = type -> tp_flags ;
40084053#endif
40094054
40104055 return type ;
@@ -4428,6 +4473,7 @@ type_new_init(type_new_ctx *ctx)
44284473 return NULL ;
44294474}
44304475
4476+ static int type_ready (PyTypeObject * type , int initial );
44314477
44324478static PyObject *
44334479type_new_impl (type_new_ctx * ctx )
@@ -4441,13 +4487,28 @@ type_new_impl(type_new_ctx *ctx)
44414487 goto error ;
44424488 }
44434489
4490+
4491+ int res ;
4492+
4493+ // This lock isn't strictly necessary because the type has not been
4494+ // exposed to anyone else yet, but update_one_slot calls find_name_in_mro
4495+ // where we'd like to assert that the type is locked. Also, type_ready()
4496+ // will assert that the type lock is held.
4497+ BEGIN_TYPE_LOCK ();
4498+
44444499 /* Initialize the rest */
4445- if (PyType_Ready (type ) < 0 ) {
4446- goto error ;
4500+ res = type_ready (type , 1 );
4501+
4502+ if (res >= 0 ) {
4503+ // Put the proper slots in place
4504+ fixup_slot_dispatchers (type );
44474505 }
44484506
4449- // Put the proper slots in place
4450- fixup_slot_dispatchers (type );
4507+ END_TYPE_LOCK ();
4508+
4509+ if (res < 0 ) {
4510+ goto error ;
4511+ }
44514512
44524513 if (!_PyDict_HasOnlyStringKeys (type -> tp_dict )) {
44534514 if (PyErr_WarnFormat (
@@ -6603,7 +6664,7 @@ object_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
66036664 }
66046665 }
66056666
6606- if (type -> tp_flags & Py_TPFLAGS_IS_ABSTRACT ) {
6667+ if (_PyType_HasFeatureSafe ( type , Py_TPFLAGS_IS_ABSTRACT ) ) {
66076668 PyObject * abstract_methods ;
66086669 PyObject * sorted_methods ;
66096670 PyObject * joined ;
@@ -8100,7 +8161,7 @@ inherit_slots(PyTypeObject *type, PyTypeObject *base)
81008161
81018162 /* Inherit Py_TPFLAGS_HAVE_VECTORCALL if tp_call is not overridden */
81028163 if (!type -> tp_call &&
8103- _PyType_HasFeature (base , Py_TPFLAGS_HAVE_VECTORCALL ))
8164+ _PyType_HasFeatureSafe (base , Py_TPFLAGS_HAVE_VECTORCALL ))
81048165 {
81058166 type_add_flags (type , Py_TPFLAGS_HAVE_VECTORCALL );
81068167 }
@@ -8169,8 +8230,6 @@ inherit_slots(PyTypeObject *type, PyTypeObject *base)
81698230static int add_operators (PyTypeObject * type );
81708231static int add_tp_new_wrapper (PyTypeObject * type );
81718232
8172- #define COLLECTION_FLAGS (Py_TPFLAGS_SEQUENCE | Py_TPFLAGS_MAPPING)
8173-
81748233static int
81758234type_ready_pre_checks (PyTypeObject * type )
81768235{
@@ -8451,7 +8510,7 @@ type_ready_inherit_as_structs(PyTypeObject *type, PyTypeObject *base)
84518510static void
84528511inherit_patma_flags (PyTypeObject * type , PyTypeObject * base ) {
84538512 if ((type -> tp_flags & COLLECTION_FLAGS ) == 0 ) {
8454- type_add_flags (type , base -> tp_flags & COLLECTION_FLAGS );
8513+ type_add_flags (type , PyType_GetFlags ( base ) & COLLECTION_FLAGS );
84558514 }
84568515}
84578516
@@ -8718,6 +8777,13 @@ type_ready(PyTypeObject *type, int initial)
87188777 }
87198778 }
87208779
8780+ #ifdef Py_GIL_DISABLED
8781+ if (_PyType_HasFeature (type , Py_TPFLAGS_HEAPTYPE )) {
8782+ PyHeapTypeObject * ht = (PyHeapTypeObject * )type ;
8783+ FT_ATOMIC_STORE_ULONG_RELAXED (ht -> ht_flags , type -> tp_flags );
8784+ }
8785+ #endif
8786+
87218787 /* All done -- set the ready flag */
87228788 type_add_flags (type , Py_TPFLAGS_READY );
87238789 stop_readying (type );
@@ -11159,7 +11225,7 @@ update_one_slot(PyTypeObject *type, pytype_slotdef *p)
1115911225 generic = p -> function ;
1116011226 if (p -> function == slot_tp_call ) {
1116111227 /* A generic __call__ is incompatible with vectorcall */
11162- type_clear_flags (type , Py_TPFLAGS_HAVE_VECTORCALL );
11228+ _PyType_SetFlags (type , Py_TPFLAGS_HAVE_VECTORCALL , 0 );
1116311229 }
1116411230 }
1116511231 Py_DECREF (descr );
@@ -11228,9 +11294,6 @@ update_slot(PyTypeObject *type, PyObject *name)
1122811294static void
1122911295fixup_slot_dispatchers (PyTypeObject * type )
1123011296{
11231- // This lock isn't strictly necessary because the type has not been
11232- // exposed to anyone else yet, but update_ont_slot calls find_name_in_mro
11233- // where we'd like to assert that the type is locked.
1123411297 BEGIN_TYPE_LOCK ();
1123511298
1123611299 assert (!PyErr_Occurred ());
0 commit comments