mirror of
				https://github.com/python/cpython.git
				synced 2025-11-03 19:34:08 +00:00 
			
		
		
		
	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
					
				
					 2 changed files with 18 additions and 27 deletions
				
			
		| 
						 | 
					@ -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…
	
	Add table
		Add a link
		
	
		Reference in a new issue