mirror of https://github.com/python/cpython
bpo-44914: Maintain invariants of type version tags. (GH-27773)
* Do not invalidate type versions unnecessarily.
This commit is contained in:
parent
62bc716fde
commit
1a511dc92d
|
@ -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.
|
|
@ -45,7 +45,7 @@ class object "PyObject *" "&PyBaseObject_Type"
|
||||||
|
|
||||||
// bpo-42745: next_version_tag remains shared by all interpreters because of static types
|
// bpo-42745: next_version_tag remains shared by all interpreters because of static types
|
||||||
// Used to set PyTypeObject.tp_version_tag
|
// 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 {
|
typedef struct PySlot_Offset {
|
||||||
short subslot_offset;
|
short subslot_offset;
|
||||||
|
@ -233,24 +233,14 @@ get_type_cache(void)
|
||||||
|
|
||||||
|
|
||||||
static 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++) {
|
for (Py_ssize_t i = 0; i < (1 << MCACHE_SIZE_EXP); i++) {
|
||||||
struct type_cache_entry *entry = &cache->hashtable[i];
|
struct type_cache_entry *entry = &cache->hashtable[i];
|
||||||
entry->version = 0;
|
entry->version = 0;
|
||||||
if (use_none) {
|
Py_XSETREF(entry->name, _Py_XNewRef(value));
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
entry->value = NULL;
|
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);
|
sizeof(cache->hashtable) / 1024);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
unsigned int cur_version_tag = next_version_tag - 1;
|
// Set to None, rather than NULL, so _PyType_Lookup() can
|
||||||
if (_Py_IsMainInterpreter(interp)) {
|
// use Py_SETREF() rather than using slower Py_XSETREF().
|
||||||
next_version_tag = 0;
|
type_cache_clear(cache, Py_None);
|
||||||
}
|
|
||||||
|
|
||||||
type_cache_clear(cache, 0);
|
return next_version_tag - 1;
|
||||||
|
|
||||||
return cur_version_tag;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -309,7 +296,8 @@ PyType_ClearCache(void)
|
||||||
void
|
void
|
||||||
_PyType_Fini(PyInterpreterState *interp)
|
_PyType_Fini(PyInterpreterState *interp)
|
||||||
{
|
{
|
||||||
_PyType_ClearCache(interp);
|
struct type_cache *cache = &interp->type_cache;
|
||||||
|
type_cache_clear(cache, NULL);
|
||||||
if (_Py_IsMainInterpreter(interp)) {
|
if (_Py_IsMainInterpreter(interp)) {
|
||||||
clear_slotdefs();
|
clear_slotdefs();
|
||||||
}
|
}
|
||||||
|
@ -426,14 +414,12 @@ assign_version_tag(struct type_cache *cache, PyTypeObject *type)
|
||||||
if (!_PyType_HasFeature(type, Py_TPFLAGS_READY))
|
if (!_PyType_HasFeature(type, Py_TPFLAGS_READY))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
type->tp_version_tag = next_version_tag++;
|
if (next_version_tag == 0) {
|
||||||
/* for stress-testing: next_version_tag &= 0xFF; */
|
/* We have run out of version numbers */
|
||||||
|
|
||||||
if (type->tp_version_tag == 0) {
|
|
||||||
// Wrap-around or just starting Python - clear the whole cache
|
|
||||||
type_cache_clear(cache, 1);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
type->tp_version_tag = next_version_tag++;
|
||||||
|
assert (type->tp_version_tag != 0);
|
||||||
|
|
||||||
bases = type->tp_bases;
|
bases = type->tp_bases;
|
||||||
n = PyTuple_GET_SIZE(bases);
|
n = PyTuple_GET_SIZE(bases);
|
||||||
|
|
Loading…
Reference in New Issue