@@ -936,6 +936,19 @@ PyType_ClearWatcher(int watcher_id)
936
936
return 0 ;
937
937
}
938
938
939
+ #ifndef NDEBUG
940
+ /* Returns true if tp_flags and tp_version_tag are consistent */
941
+ static bool version_tag_consistency (PyTypeObject * type )
942
+ {
943
+ if (_PyType_HasFeature (type , Py_TPFLAGS_VALID_VERSION_TAG )) {
944
+ return type -> tp_version_tag != 0 ;
945
+ }
946
+ else {
947
+ return type -> tp_version_tag == 0 ;
948
+ }
949
+ }
950
+ #endif
951
+
939
952
static int assign_version_tag (PyInterpreterState * interp , PyTypeObject * type );
940
953
941
954
int
@@ -952,6 +965,7 @@ PyType_Watch(int watcher_id, PyObject* obj)
952
965
}
953
966
// ensure we will get a callback on the next modification
954
967
BEGIN_TYPE_LOCK ()
968
+ assert (version_tag_consistency (type ));
955
969
assign_version_tag (interp , type );
956
970
type -> tp_watched |= (1 << watcher_id );
957
971
END_TYPE_LOCK ()
@@ -1070,7 +1084,6 @@ type_modified_unlocked(PyTypeObject *type)
1070
1084
}
1071
1085
}
1072
1086
1073
- type -> tp_flags &= ~Py_TPFLAGS_VALID_VERSION_TAG ;
1074
1087
_PyType_SetVersion (type , 0 ); /* 0 is not a valid version tag */
1075
1088
if (PyType_HasFeature (type , Py_TPFLAGS_HEAPTYPE )) {
1076
1089
// This field *must* be invalidated if the type is modified (see the
@@ -1082,6 +1095,7 @@ type_modified_unlocked(PyTypeObject *type)
1082
1095
void
1083
1096
PyType_Modified (PyTypeObject * type )
1084
1097
{
1098
+ assert (version_tag_consistency (type ));
1085
1099
// Quick check without the lock held
1086
1100
if (!_PyType_HasFeature (type , Py_TPFLAGS_VALID_VERSION_TAG )) {
1087
1101
return ;
@@ -1147,7 +1161,6 @@ type_mro_modified(PyTypeObject *type, PyObject *bases) {
1147
1161
1148
1162
clear :
1149
1163
assert (!(type -> tp_flags & _Py_TPFLAGS_STATIC_BUILTIN ));
1150
- type -> tp_flags &= ~Py_TPFLAGS_VALID_VERSION_TAG ;
1151
1164
_PyType_SetVersion (type , 0 ); /* 0 is not a valid version tag */
1152
1165
if (PyType_HasFeature (type , Py_TPFLAGS_HEAPTYPE )) {
1153
1166
// This field *must* be invalidated if the type is modified (see the
@@ -1168,6 +1181,7 @@ This is similar to func_version_cache.
1168
1181
void
1169
1182
_PyType_SetVersion (PyTypeObject * tp , unsigned int version )
1170
1183
{
1184
+ assert (version_tag_consistency (tp ));
1171
1185
#ifndef Py_GIL_DISABLED
1172
1186
PyInterpreterState * interp = _PyInterpreterState_GET ();
1173
1187
// lookup the old version and set to null
@@ -1178,6 +1192,13 @@ _PyType_SetVersion(PyTypeObject *tp, unsigned int version)
1178
1192
* slot = NULL ;
1179
1193
}
1180
1194
#endif
1195
+ if (version ) {
1196
+ tp -> tp_flags |= Py_TPFLAGS_VALID_VERSION_TAG ;
1197
+ tp -> tp_versions_used ++ ;
1198
+ }
1199
+ else {
1200
+ tp -> tp_flags &= ~Py_TPFLAGS_VALID_VERSION_TAG ;
1201
+ }
1181
1202
FT_ATOMIC_STORE_UINT32_RELAXED (tp -> tp_version_tag , version );
1182
1203
#ifndef Py_GIL_DISABLED
1183
1204
if (version != 0 ) {
@@ -1219,6 +1240,7 @@ _PyType_GetVersionForCurrentState(PyTypeObject *tp)
1219
1240
static int
1220
1241
assign_version_tag (PyInterpreterState * interp , PyTypeObject * type )
1221
1242
{
1243
+ assert (version_tag_consistency (type ));
1222
1244
ASSERT_TYPE_LOCK_HELD ();
1223
1245
1224
1246
/* Ensure that the tp_version_tag is valid and set
@@ -1235,7 +1257,14 @@ assign_version_tag(PyInterpreterState *interp, PyTypeObject *type)
1235
1257
if (type -> tp_versions_used >= MAX_VERSIONS_PER_CLASS ) {
1236
1258
return 0 ;
1237
1259
}
1238
- type -> tp_versions_used ++ ;
1260
+
1261
+ PyObject * bases = lookup_tp_bases (type );
1262
+ Py_ssize_t n = PyTuple_GET_SIZE (bases );
1263
+ for (Py_ssize_t i = 0 ; i < n ; i ++ ) {
1264
+ PyObject * b = PyTuple_GET_ITEM (bases , i );
1265
+ if (!assign_version_tag (interp , _PyType_CAST (b )))
1266
+ return 0 ;
1267
+ }
1239
1268
if (type -> tp_flags & Py_TPFLAGS_IMMUTABLETYPE ) {
1240
1269
/* static types */
1241
1270
if (NEXT_GLOBAL_VERSION_TAG > _Py_MAX_GLOBAL_TYPE_VERSION_TAG ) {
@@ -1254,15 +1283,7 @@ assign_version_tag(PyInterpreterState *interp, PyTypeObject *type)
1254
1283
_PyType_SetVersion (type , NEXT_VERSION_TAG (interp )++ );
1255
1284
assert (type -> tp_version_tag != 0 );
1256
1285
}
1257
-
1258
- PyObject * bases = lookup_tp_bases (type );
1259
- Py_ssize_t n = PyTuple_GET_SIZE (bases );
1260
- for (Py_ssize_t i = 0 ; i < n ; i ++ ) {
1261
- PyObject * b = PyTuple_GET_ITEM (bases , i );
1262
- if (!assign_version_tag (interp , _PyType_CAST (b )))
1263
- return 0 ;
1264
- }
1265
- type -> tp_flags |= Py_TPFLAGS_VALID_VERSION_TAG ;
1286
+ assert (version_tag_consistency (type ));
1266
1287
return 1 ;
1267
1288
}
1268
1289
@@ -3284,6 +3305,7 @@ static int
3284
3305
mro_internal_unlocked (PyTypeObject * type , int initial , PyObject * * p_old_mro )
3285
3306
{
3286
3307
ASSERT_TYPE_LOCK_HELD ();
3308
+ assert (version_tag_consistency (type ));
3287
3309
3288
3310
PyObject * new_mro , * old_mro ;
3289
3311
int reent ;
@@ -5432,6 +5454,7 @@ _PyType_LookupRef(PyTypeObject *type, PyObject *name)
5432
5454
unsigned int h = MCACHE_HASH_METHOD (type , name );
5433
5455
struct type_cache * cache = get_type_cache ();
5434
5456
struct type_cache_entry * entry = & cache -> hashtable [h ];
5457
+ assert (version_tag_consistency (type ));
5435
5458
#ifdef Py_GIL_DISABLED
5436
5459
// synchronize-with other writing threads by doing an acquire load on the sequence
5437
5460
while (1 ) {
@@ -5898,7 +5921,6 @@ fini_static_type(PyInterpreterState *interp, PyTypeObject *type,
5898
5921
5899
5922
if (final ) {
5900
5923
type -> tp_flags &= ~Py_TPFLAGS_READY ;
5901
- type -> tp_flags &= ~Py_TPFLAGS_VALID_VERSION_TAG ;
5902
5924
_PyType_SetVersion (type , 0 );
5903
5925
}
5904
5926
@@ -8516,11 +8538,11 @@ init_static_type(PyInterpreterState *interp, PyTypeObject *self,
8516
8538
8517
8539
assert (NEXT_GLOBAL_VERSION_TAG <= _Py_MAX_GLOBAL_TYPE_VERSION_TAG );
8518
8540
_PyType_SetVersion (self , NEXT_GLOBAL_VERSION_TAG ++ );
8519
- self -> tp_flags |= Py_TPFLAGS_VALID_VERSION_TAG ;
8520
8541
}
8521
8542
else {
8522
8543
assert (!initial );
8523
8544
assert (self -> tp_flags & _Py_TPFLAGS_STATIC_BUILTIN );
8545
+ assert (self -> tp_version_tag != 0 );
8524
8546
assert (self -> tp_flags & Py_TPFLAGS_VALID_VERSION_TAG );
8525
8547
}
8526
8548
0 commit comments