@@ -347,23 +347,43 @@ _PyStaticType_GetBuiltins(void)
347
347
static void
348
348
type_set_flags (PyTypeObject * tp , unsigned long flags )
349
349
{
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 ;
359
354
}
360
355
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.
361
366
static void
362
367
type_set_flags_with_mask (PyTypeObject * tp , unsigned long mask , unsigned long flags )
363
368
{
364
369
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 ));
365
373
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
367
387
}
368
388
369
389
static void
@@ -1318,7 +1338,6 @@ int PyUnstable_Type_AssignVersionTag(PyTypeObject *type)
1318
1338
static PyMemberDef type_members [] = {
1319
1339
{"__basicsize__" , Py_T_PYSSIZET , offsetof(PyTypeObject ,tp_basicsize ),Py_READONLY },
1320
1340
{"__itemsize__" , Py_T_PYSSIZET , offsetof(PyTypeObject , tp_itemsize ), Py_READONLY },
1321
- {"__flags__" , Py_T_ULONG , offsetof(PyTypeObject , tp_flags ), Py_READONLY },
1322
1341
/* Note that this value is misleading for static builtin types,
1323
1342
since the memory at this offset will always be NULL. */
1324
1343
{"__weakrefoffset__" , Py_T_PYSSIZET ,
@@ -1584,9 +1603,9 @@ type_set_abstractmethods(PyObject *tp, PyObject *value, void *Py_UNUSED(closure)
1584
1603
BEGIN_TYPE_LOCK ();
1585
1604
type_modified_unlocked (type );
1586
1605
if (abstract )
1587
- type_add_flags (type , Py_TPFLAGS_IS_ABSTRACT );
1606
+ type_set_flags_with_mask (type , 0 , Py_TPFLAGS_IS_ABSTRACT );
1588
1607
else
1589
- type_clear_flags (type , Py_TPFLAGS_IS_ABSTRACT );
1608
+ type_set_flags_with_mask (type , Py_TPFLAGS_IS_ABSTRACT , 0 );
1590
1609
END_TYPE_LOCK ();
1591
1610
1592
1611
return 0 ;
@@ -2109,6 +2128,24 @@ type_set_type_params(PyObject *tp, PyObject *value, void *Py_UNUSED(closure))
2109
2128
return result ;
2110
2129
}
2111
2130
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
+ }
2112
2149
2113
2150
/*[clinic input]
2114
2151
type.__instancecheck__ -> bool
@@ -2157,6 +2194,7 @@ static PyGetSetDef type_getsets[] = {
2157
2194
{"__annotations__" , type_get_annotations , type_set_annotations , NULL },
2158
2195
{"__annotate__" , type_get_annotate , type_set_annotate , NULL },
2159
2196
{"__type_params__" , type_get_type_params , type_set_type_params , NULL },
2197
+ {"__flags__" , type_get_flags , NULL , NULL },
2160
2198
{0 }
2161
2199
};
2162
2200
@@ -3687,7 +3725,13 @@ type_init(PyObject *cls, PyObject *args, PyObject *kwds)
3687
3725
unsigned long
3688
3726
PyType_GetFlags (PyTypeObject * type )
3689
3727
{
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 ;
3691
3735
}
3692
3736
3693
3737
@@ -4005,6 +4049,7 @@ type_new_alloc(type_new_ctx *ctx)
4005
4049
4006
4050
#ifdef Py_GIL_DISABLED
4007
4051
et -> unique_id = _PyObject_AssignUniqueId ((PyObject * )et );
4052
+ et -> ht_flags = type -> tp_flags ;
4008
4053
#endif
4009
4054
4010
4055
return type ;
@@ -4428,6 +4473,7 @@ type_new_init(type_new_ctx *ctx)
4428
4473
return NULL ;
4429
4474
}
4430
4475
4476
+ static int type_ready (PyTypeObject * type , int initial );
4431
4477
4432
4478
static PyObject *
4433
4479
type_new_impl (type_new_ctx * ctx )
@@ -4441,13 +4487,28 @@ type_new_impl(type_new_ctx *ctx)
4441
4487
goto error ;
4442
4488
}
4443
4489
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
+
4444
4499
/* 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 );
4447
4505
}
4448
4506
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
+ }
4451
4512
4452
4513
if (!_PyDict_HasOnlyStringKeys (type -> tp_dict )) {
4453
4514
if (PyErr_WarnFormat (
@@ -6603,7 +6664,7 @@ object_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
6603
6664
}
6604
6665
}
6605
6666
6606
- if (type -> tp_flags & Py_TPFLAGS_IS_ABSTRACT ) {
6667
+ if (_PyType_HasFeatureSafe ( type , Py_TPFLAGS_IS_ABSTRACT ) ) {
6607
6668
PyObject * abstract_methods ;
6608
6669
PyObject * sorted_methods ;
6609
6670
PyObject * joined ;
@@ -8100,7 +8161,7 @@ inherit_slots(PyTypeObject *type, PyTypeObject *base)
8100
8161
8101
8162
/* Inherit Py_TPFLAGS_HAVE_VECTORCALL if tp_call is not overridden */
8102
8163
if (!type -> tp_call &&
8103
- _PyType_HasFeature (base , Py_TPFLAGS_HAVE_VECTORCALL ))
8164
+ _PyType_HasFeatureSafe (base , Py_TPFLAGS_HAVE_VECTORCALL ))
8104
8165
{
8105
8166
type_add_flags (type , Py_TPFLAGS_HAVE_VECTORCALL );
8106
8167
}
@@ -8169,8 +8230,6 @@ inherit_slots(PyTypeObject *type, PyTypeObject *base)
8169
8230
static int add_operators (PyTypeObject * type );
8170
8231
static int add_tp_new_wrapper (PyTypeObject * type );
8171
8232
8172
- #define COLLECTION_FLAGS (Py_TPFLAGS_SEQUENCE | Py_TPFLAGS_MAPPING)
8173
-
8174
8233
static int
8175
8234
type_ready_pre_checks (PyTypeObject * type )
8176
8235
{
@@ -8451,7 +8510,7 @@ type_ready_inherit_as_structs(PyTypeObject *type, PyTypeObject *base)
8451
8510
static void
8452
8511
inherit_patma_flags (PyTypeObject * type , PyTypeObject * base ) {
8453
8512
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 );
8455
8514
}
8456
8515
}
8457
8516
@@ -8718,6 +8777,13 @@ type_ready(PyTypeObject *type, int initial)
8718
8777
}
8719
8778
}
8720
8779
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
+
8721
8787
/* All done -- set the ready flag */
8722
8788
type_add_flags (type , Py_TPFLAGS_READY );
8723
8789
stop_readying (type );
@@ -11159,7 +11225,7 @@ update_one_slot(PyTypeObject *type, pytype_slotdef *p)
11159
11225
generic = p -> function ;
11160
11226
if (p -> function == slot_tp_call ) {
11161
11227
/* 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 );
11163
11229
}
11164
11230
}
11165
11231
Py_DECREF (descr );
@@ -11228,9 +11294,6 @@ update_slot(PyTypeObject *type, PyObject *name)
11228
11294
static void
11229
11295
fixup_slot_dispatchers (PyTypeObject * type )
11230
11296
{
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.
11234
11297
BEGIN_TYPE_LOCK ();
11235
11298
11236
11299
assert (!PyErr_Occurred ());
0 commit comments