@@ -973,43 +973,21 @@ PyType_Unwatch(int watcher_id, PyObject* obj)
973
973
return 0 ;
974
974
}
975
975
976
- #ifdef Py_GIL_DISABLED
977
-
978
976
static void
979
- type_modification_starting_unlocked (PyTypeObject * type )
977
+ set_version_unlocked (PyTypeObject * tp , unsigned int version )
980
978
{
981
979
ASSERT_TYPE_LOCK_HELD ();
982
-
983
- /* Clear version tags on all types, but leave the valid
984
- version tag intact. This prepares for a modification so that
985
- any concurrent readers of the type cache will not see invalid
986
- values.
987
- */
988
- if (!_PyType_HasFeature (type , Py_TPFLAGS_VALID_VERSION_TAG )) {
989
- return ;
980
+ #ifndef Py_GIL_DISABLED
981
+ if (version ) {
982
+ tp -> tp_versions_used ++ ;
990
983
}
991
-
992
- PyObject * subclasses = lookup_tp_subclasses (type );
993
- if (subclasses != NULL ) {
994
- assert (PyDict_CheckExact (subclasses ));
995
-
996
- Py_ssize_t i = 0 ;
997
- PyObject * ref ;
998
- while (PyDict_Next (subclasses , & i , NULL , & ref )) {
999
- PyTypeObject * subclass = type_from_ref (ref );
1000
- if (subclass == NULL ) {
1001
- continue ;
1002
- }
1003
- type_modification_starting_unlocked (subclass );
1004
- Py_DECREF (subclass );
1005
- }
984
+ #else
985
+ if (version ) {
986
+ _Py_atomic_add_uint16 (& tp -> tp_versions_used , 1 );
1006
987
}
1007
-
1008
- /* 0 is not a valid version tag */
1009
- _Py_atomic_store_uint32_release (& type -> tp_version_tag , 0 );
1010
- }
1011
-
1012
988
#endif
989
+ FT_ATOMIC_STORE_UINT32_RELAXED (tp -> tp_version_tag , version );
990
+ }
1013
991
1014
992
static void
1015
993
type_modified_unlocked (PyTypeObject * type )
@@ -1020,16 +998,16 @@ type_modified_unlocked(PyTypeObject *type)
1020
998
1021
999
Invariants:
1022
1000
1023
- - before Py_TPFLAGS_VALID_VERSION_TAG can be set on a type,
1001
+ - before tp_version_tag can be set on a type,
1024
1002
it must first be set on all super types.
1025
1003
1026
- This function clears the Py_TPFLAGS_VALID_VERSION_TAG of a
1004
+ This function clears the tp_version_tag of a
1027
1005
type (so it must first clear it on all subclasses). The
1028
- tp_version_tag value is meaningless unless this flag is set .
1006
+ tp_version_tag value is meaningless when equal to zero .
1029
1007
We don't assign new version tags eagerly, but only as
1030
1008
needed.
1031
1009
*/
1032
- if (! _PyType_HasFeature ( type , Py_TPFLAGS_VALID_VERSION_TAG ) ) {
1010
+ if (type -> tp_version_tag == 0 ) {
1033
1011
return ;
1034
1012
}
1035
1013
@@ -1069,8 +1047,7 @@ type_modified_unlocked(PyTypeObject *type)
1069
1047
}
1070
1048
}
1071
1049
1072
- type -> tp_flags &= ~Py_TPFLAGS_VALID_VERSION_TAG ;
1073
- FT_ATOMIC_STORE_UINT32_RELAXED (type -> tp_version_tag , 0 ); /* 0 is not a valid version tag */
1050
+ set_version_unlocked (type , 0 ); /* 0 is not a valid version tag */
1074
1051
if (PyType_HasFeature (type , Py_TPFLAGS_HEAPTYPE )) {
1075
1052
// This field *must* be invalidated if the type is modified (see the
1076
1053
// comment on struct _specialization_cache):
@@ -1082,7 +1059,7 @@ void
1082
1059
PyType_Modified (PyTypeObject * type )
1083
1060
{
1084
1061
// Quick check without the lock held
1085
- if (! _PyType_HasFeature ( type , Py_TPFLAGS_VALID_VERSION_TAG ) ) {
1062
+ if (type -> tp_version_tag == 0 ) {
1086
1063
return ;
1087
1064
}
1088
1065
@@ -1146,8 +1123,7 @@ type_mro_modified(PyTypeObject *type, PyObject *bases) {
1146
1123
1147
1124
clear :
1148
1125
assert (!(type -> tp_flags & _Py_TPFLAGS_STATIC_BUILTIN ));
1149
- type -> tp_flags &= ~Py_TPFLAGS_VALID_VERSION_TAG ;
1150
- FT_ATOMIC_STORE_UINT32_RELAXED (type -> tp_version_tag , 0 ); /* 0 is not a valid version tag */
1126
+ set_version_unlocked (type , 0 ); /* 0 is not a valid version tag */
1151
1127
if (PyType_HasFeature (type , Py_TPFLAGS_HEAPTYPE )) {
1152
1128
// This field *must* be invalidated if the type is modified (see the
1153
1129
// comment on struct _specialization_cache):
@@ -1162,12 +1138,11 @@ assign_version_tag(PyInterpreterState *interp, PyTypeObject *type)
1162
1138
{
1163
1139
ASSERT_TYPE_LOCK_HELD ();
1164
1140
1165
- /* Ensure that the tp_version_tag is valid and set
1166
- Py_TPFLAGS_VALID_VERSION_TAG. To respect the invariant, this
1167
- must first be done on all super classes. Return 0 if this
1168
- cannot be done, 1 if Py_TPFLAGS_VALID_VERSION_TAG.
1141
+ /* Ensure that the tp_version_tag is valid.
1142
+ * To respect the invariant, this must first be done on all super classes.
1143
+ * Return 0 if this cannot be done, 1 if tp_version_tag is set.
1169
1144
*/
1170
- if (_PyType_HasFeature ( type , Py_TPFLAGS_VALID_VERSION_TAG ) ) {
1145
+ if (type -> tp_version_tag != 0 ) {
1171
1146
return 1 ;
1172
1147
}
1173
1148
if (!_PyType_HasFeature (type , Py_TPFLAGS_READY )) {
@@ -1176,15 +1151,22 @@ assign_version_tag(PyInterpreterState *interp, PyTypeObject *type)
1176
1151
if (type -> tp_versions_used >= MAX_VERSIONS_PER_CLASS ) {
1177
1152
return 0 ;
1178
1153
}
1179
- type -> tp_versions_used ++ ;
1154
+
1155
+ PyObject * bases = lookup_tp_bases (type );
1156
+ Py_ssize_t n = PyTuple_GET_SIZE (bases );
1157
+ for (Py_ssize_t i = 0 ; i < n ; i ++ ) {
1158
+ PyObject * b = PyTuple_GET_ITEM (bases , i );
1159
+ if (!assign_version_tag (interp , _PyType_CAST (b ))) {
1160
+ return 0 ;
1161
+ }
1162
+ }
1180
1163
if (type -> tp_flags & Py_TPFLAGS_IMMUTABLETYPE ) {
1181
1164
/* static types */
1182
1165
if (NEXT_GLOBAL_VERSION_TAG > _Py_MAX_GLOBAL_TYPE_VERSION_TAG ) {
1183
1166
/* We have run out of version numbers */
1184
1167
return 0 ;
1185
1168
}
1186
- FT_ATOMIC_STORE_UINT32_RELAXED (type -> tp_version_tag ,
1187
- NEXT_GLOBAL_VERSION_TAG ++ );
1169
+ set_version_unlocked (type , NEXT_GLOBAL_VERSION_TAG ++ );
1188
1170
assert (type -> tp_version_tag <= _Py_MAX_GLOBAL_TYPE_VERSION_TAG );
1189
1171
}
1190
1172
else {
@@ -1193,19 +1175,9 @@ assign_version_tag(PyInterpreterState *interp, PyTypeObject *type)
1193
1175
/* We have run out of version numbers */
1194
1176
return 0 ;
1195
1177
}
1196
- FT_ATOMIC_STORE_UINT32_RELAXED (type -> tp_version_tag ,
1197
- NEXT_VERSION_TAG (interp )++ );
1178
+ set_version_unlocked (type , NEXT_VERSION_TAG (interp )++ );
1198
1179
assert (type -> tp_version_tag != 0 );
1199
1180
}
1200
-
1201
- PyObject * bases = lookup_tp_bases (type );
1202
- Py_ssize_t n = PyTuple_GET_SIZE (bases );
1203
- for (Py_ssize_t i = 0 ; i < n ; i ++ ) {
1204
- PyObject * b = PyTuple_GET_ITEM (bases , i );
1205
- if (!assign_version_tag (interp , _PyType_CAST (b )))
1206
- return 0 ;
1207
- }
1208
- type -> tp_flags |= Py_TPFLAGS_VALID_VERSION_TAG ;
1209
1181
return 1 ;
1210
1182
}
1211
1183
@@ -3126,7 +3098,7 @@ mro_internal_unlocked(PyTypeObject *type, int initial, PyObject **p_old_mro)
3126
3098
else {
3127
3099
/* For static builtin types, this is only called during init
3128
3100
before the method cache has been populated. */
3129
- assert (_PyType_HasFeature ( type , Py_TPFLAGS_VALID_VERSION_TAG ) );
3101
+ assert (type -> tp_version_tag );
3130
3102
}
3131
3103
3132
3104
if (p_old_mro != NULL )
@@ -5275,7 +5247,7 @@ _PyType_LookupRef(PyTypeObject *type, PyObject *name)
5275
5247
#else
5276
5248
if (entry -> version == type -> tp_version_tag &&
5277
5249
entry -> name == name ) {
5278
- assert (_PyType_HasFeature ( type , Py_TPFLAGS_VALID_VERSION_TAG ) );
5250
+ assert (type -> tp_version_tag );
5279
5251
OBJECT_STAT_INC_COND (type_cache_hits , !is_dunder_name (name ));
5280
5252
OBJECT_STAT_INC_COND (type_cache_dunder_hits , is_dunder_name (name ));
5281
5253
Py_XINCREF (entry -> value );
@@ -5298,7 +5270,6 @@ _PyType_LookupRef(PyTypeObject *type, PyObject *name)
5298
5270
if (MCACHE_CACHEABLE_NAME (name )) {
5299
5271
has_version = assign_version_tag (interp , type );
5300
5272
version = type -> tp_version_tag ;
5301
- assert (!has_version || _PyType_HasFeature (type , Py_TPFLAGS_VALID_VERSION_TAG ));
5302
5273
}
5303
5274
END_TYPE_LOCK ()
5304
5275
@@ -5582,23 +5553,15 @@ type_setattro(PyObject *self, PyObject *name, PyObject *value)
5582
5553
return -1 ;
5583
5554
}
5584
5555
5585
- #ifdef Py_GIL_DISABLED
5586
- // In free-threaded builds readers can race with the lock-free portion
5587
- // of the type cache and the assignment into the dict. We clear all of the
5588
- // versions initially so no readers will succeed in the lock-free case.
5589
- // They'll then block on the type lock until the update below is done.
5590
- type_modification_starting_unlocked (type );
5591
- #endif
5592
-
5593
- res = _PyDict_SetItem_LockHeld ((PyDictObject * )dict , name , value );
5594
-
5595
5556
/* Clear the VALID_VERSION flag of 'type' and all its
5596
5557
subclasses. This could possibly be unified with the
5597
5558
update_subclasses() recursion in update_slot(), but carefully:
5598
5559
they each have their own conditions on which to stop
5599
5560
recursing into subclasses. */
5600
5561
type_modified_unlocked (type );
5601
5562
5563
+ res = _PyDict_SetItem_LockHeld ((PyDictObject * )dict , name , value );
5564
+
5602
5565
if (res == 0 ) {
5603
5566
if (is_dunder_name (name )) {
5604
5567
res = update_slot (type , name );
@@ -5710,7 +5673,6 @@ fini_static_type(PyInterpreterState *interp, PyTypeObject *type,
5710
5673
5711
5674
if (final ) {
5712
5675
type -> tp_flags &= ~Py_TPFLAGS_READY ;
5713
- type -> tp_flags &= ~Py_TPFLAGS_VALID_VERSION_TAG ;
5714
5676
type -> tp_version_tag = 0 ;
5715
5677
}
5716
5678
@@ -8329,12 +8291,11 @@ init_static_type(PyInterpreterState *interp, PyTypeObject *self,
8329
8291
8330
8292
assert (NEXT_GLOBAL_VERSION_TAG <= _Py_MAX_GLOBAL_TYPE_VERSION_TAG );
8331
8293
self -> tp_version_tag = NEXT_GLOBAL_VERSION_TAG ++ ;
8332
- self -> tp_flags |= Py_TPFLAGS_VALID_VERSION_TAG ;
8333
8294
}
8334
8295
else {
8335
8296
assert (!initial );
8336
8297
assert (self -> tp_flags & _Py_TPFLAGS_STATIC_BUILTIN );
8337
- assert (self -> tp_flags & Py_TPFLAGS_VALID_VERSION_TAG );
8298
+ assert (self -> tp_version_tag != 0 );
8338
8299
}
8339
8300
8340
8301
managed_static_type_state_init (interp , self , isbuiltin , initial );
0 commit comments