8000 bpo-44914: Maintain invariants of type version tags. (GH-27773) · python/cpython@1a511dc · GitHub
[go: up one dir, main page]

Skip to content

Commit 1a511dc

Browse files
authored
bpo-44914: Maintain invariants of type version tags. (GH-27773)
* Do not invalidate type versions unnecessarily.
1 parent 62bc716 commit 1a511dc

File tree

2 files changed

+18
-27
lines changed

2 files changed

+18
-27
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Class version tags are no longer recycled.
2+
3+
This means that a version tag serves as a unique identifier for the state of
4+
a class. We rely on this for effective specialization of the LOAD_ATTR and
5+
other instructions.

Objects/typeobject.c

Lines changed: 13 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ class object "PyObject *" "&PyBaseObject_Type"
4545

4646
// bpo-42745: next_version_tag remains shared by all interpreters because of static types
4747
// Used to set PyTypeObject.tp_version_tag
48-
static unsigned int next_version_tag = 0;
48+
static unsigned int next_version_tag = 1;
4949

5050
typedef struct PySlot_Offset {
5151
short subslot_offset;
@@ -233,24 +233,14 @@ get_type_cache(void)
233233

234234

235235
static void
236-
type_cache_clear(struct type_cache *cache, int use_none)
236+
type_cache_clear(struct type_cache *cache, PyObject *value)
237237
{
238238
for (Py_ssize_t i = 0; i < (1 << MCACHE_SIZE_EXP); i++) {
239239
struct type_cache_entry *entry = &cache->hashtable[i];
240240
entry->version = 0;
241-
if (use_none) {
242-
// Set to None so _PyType_Lookup() can use Py_SETREF(),
243-
// rather than using slower Py_XSETREF().
244-
Py_XSETREF(entry->name, Py_NewRef(Py_None));
245-
}
246-
else {
247-
Py_CLEAR(entry->name);
248-
}
241+
Py_XSETREF(entry->name, _Py_XNewRef(value));
249242
entry->value = NULL;
250243
}
251-
252-
// Mark all version tags as invalid
253-
PyType_Modified(&PyBaseObject_Type);
254244
}
255245

256246

@@ -287,14 +277,11 @@ _PyType_ClearCache(PyInterpreterState *interp)
287277
sizeof(cache->hashtable) / 1024);
288278
#endif
289279

290-
unsigned int cur_version_tag = next_version_tag - 1;
291-
if (_Py_IsMainInterpreter(interp)) {
292-
next_version_tag = 0;
293-
}
280+
// Set to None, rather than NULL, so _PyType_Lookup() can
281+
// use Py_SETREF() rather than using slower Py_XSETREF().
282+
type_cache_clear(cache, Py_None);
294283

295-
type_cache_clear(cache, 0);
296-
297-
return cur_version_tag;
284+
return next_version_tag - 1;
298285
}
299286

300287

@@ -309,7 +296,8 @@ PyType_ClearCache(void)
309296
void
310297
_PyType_Fini(PyInterpreterState *interp)
311298
{
312-
_PyType_ClearCache(interp);
299+
struct type_cache *cache = &interp->type_cache;
300+
type_cache_clear(cache, NULL);
313301
if (_Py_IsMainInterpreter(interp)) {
314302
clear_slotdefs();
315303
}
@@ -426,14 +414,12 @@ assign_version_tag(struct type_cache *cache, PyTypeObject *type)
426414
if (!_PyType_HasFeature(type, Py_TPFLAGS_READY))
427415
return 0;
428416

429-
type->tp_version_tag = next_version_tag++;
430-
/* for stress-testing: next_version_tag &= 0xFF; */
431-
432-
if (type->tp_version_tag == 0) {
433-
// Wrap-around or just starting Python - clear the whole cache
434-
type_cache_clear(cache, 1);
417+
if (next_version_tag == 0) {
418+
/* We have run out of version numbers */
435419
return 0;
436420
}
421+
type->tp_version_tag = next_version_tag++;
422+
assert (type->tp_version_tag != 0);
437423

438424
bases = type->tp_bases;
439425
n = PyTuple_GET_SIZE(bases);

0 commit comments

Comments
 (0)
0