bpo-44914: Maintain invariants of type version tags. (GH-27773)

* Do not invalidate type versions unnecessarily.
This commit is contained in:
Mark Shannon 2021-08-16 12:21:34 +01:00 committed by GitHub
parent 62bc716fde
commit 1a511dc92d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 18 additions and 27 deletions

View File

@ -0,0 +1,5 @@
Class version tags are no longer recycled.
This means that a version tag serves as a unique identifier for the state of
a class. We rely on this for effective specialization of the LOAD_ATTR and
other instructions.

View File

@ -45,7 +45,7 @@ class object "PyObject *" "&PyBaseObject_Type"
// bpo-42745: next_version_tag remains shared by all interpreters because of static types
// Used to set PyTypeObject.tp_version_tag
static unsigned int next_version_tag = 0;
static unsigned int next_version_tag = 1;
typedef struct PySlot_Offset {
short subslot_offset;
@ -233,24 +233,14 @@ get_type_cache(void)
static void
type_cache_clear(struct type_cache *cache, int use_none)
type_cache_clear(struct type_cache *cache, PyObject *value)
{
for (Py_ssize_t i = 0; i < (1 << MCACHE_SIZE_EXP); i++) {
struct type_cache_entry *entry = &cache->hashtable[i];
entry->version = 0;
if (use_none) {
// Set to None so _PyType_Lookup() can use Py_SETREF(),
// rather than using slower Py_XSETREF().
Py_XSETREF(entry->name, Py_NewRef(Py_None));
}
else {
Py_CLEAR(entry->name);
}
Py_XSETREF(entry->name, _Py_XNewRef(value));
entry->value = NULL;
}
// Mark all version tags as invalid
PyType_Modified(&PyBaseObject_Type);
}
@ -287,14 +277,11 @@ _PyType_ClearCache(PyInterpreterState *interp)
sizeof(cache->hashtable) / 1024);
#endif
unsigned int cur_version_tag = next_version_tag - 1;
if (_Py_IsMainInterpreter(interp)) {
next_version_tag = 0;
}
// Set to None, rather than NULL, so _PyType_Lookup() can
// use Py_SETREF() rather than using slower Py_XSETREF().
type_cache_clear(cache, Py_None);
type_cache_clear(cache, 0);
return cur_version_tag;
return next_version_tag - 1;
}
@ -309,7 +296,8 @@ PyType_ClearCache(void)
void
_PyType_Fini(PyInterpreterState *interp)
{
_PyType_ClearCache(interp);
struct type_cache *cache = &interp->type_cache;
type_cache_clear(cache, NULL);
if (_Py_IsMainInterpreter(interp)) {
clear_slotdefs();
}
@ -426,14 +414,12 @@ assign_version_tag(struct type_cache *cache, PyTypeObject *type)
if (!_PyType_HasFeature(type, Py_TPFLAGS_READY))
return 0;
type->tp_version_tag = next_version_tag++;
/* for stress-testing: next_version_tag &= 0xFF; */
if (type->tp_version_tag == 0) {
// Wrap-around or just starting Python - clear the whole cache
type_cache_clear(cache, 1);
if (next_version_tag == 0) {
/* We have run out of version numbers */
return 0;
}
type->tp_version_tag = next_version_tag++;
assert (type->tp_version_tag != 0);
bases = type->tp_bases;
n = PyTuple_GET_SIZE(bases);