@@ -51,8 +51,8 @@ class object "PyObject *" "&PyBaseObject_Type"
5151 MCACHE_HASH(FT_ATOMIC_LOAD_UINT32_RELAXED((type)->tp_version_tag), \
5252 ((Py_ssize_t)(name)) >> 3)
5353#define MCACHE_CACHEABLE_NAME (name ) \
54- PyUnicode_CheckExact(name) && \
55- (PyUnicode_GET_LENGTH(name) <= MCACHE_MAX_ATTR_SIZE)
54+ ( PyUnicode_CheckExact(name) && \
55+ (PyUnicode_GET_LENGTH(name) <= MCACHE_MAX_ATTR_SIZE) )
5656
5757#define NEXT_VERSION_TAG (interp ) \
5858 (interp)->types.next_version_tag
@@ -5708,8 +5708,6 @@ PyObject_GetItemData(PyObject *obj)
57085708static int
57095709find_name_in_mro (PyTypeObject * type , PyObject * name , _PyStackRef * out )
57105710{
5711- ASSERT_TYPE_LOCK_HELD ();
5712-
57135711 Py_hash_t hash = _PyObject_HashFast (name );
57145712 if (hash == -1 ) {
57155713 PyErr_Clear ();
@@ -5860,6 +5858,14 @@ _PyType_LookupRefAndVersion(PyTypeObject *type, PyObject *name, unsigned int *ve
58605858 return PyStackRef_AsPyObjectSteal (out );
58615859}
58625860
5861+ static int
5862+ should_assign_version_tag (PyTypeObject * type , PyObject * name , unsigned int version_tag )
5863+ {
5864+ return (version_tag == 0
5865+ && FT_ATOMIC_LOAD_UINT16_RELAXED (type -> tp_versions_used ) < MAX_VERSIONS_PER_CLASS
5866+ && MCACHE_CACHEABLE_NAME (name ));
5867+ }
5868+
58635869unsigned int
58645870_PyType_LookupStackRefAndVersion (PyTypeObject * type , PyObject * name , _PyStackRef * out )
58655871{
@@ -5908,41 +5914,39 @@ _PyType_LookupStackRefAndVersion(PyTypeObject *type, PyObject *name, _PyStackRef
59085914 /* We may end up clearing live exceptions below, so make sure it's ours. */
59095915 assert (!PyErr_Occurred ());
59105916
5911- // We need to atomically do the lookup and capture the version before
5912- // anyone else can modify our mro or mutate the type.
5913-
59145917 int res ;
59155918 PyInterpreterState * interp = _PyInterpreterState_GET ();
5916- int has_version = 0 ;
5917- unsigned int assigned_version = 0 ;
59185919
5919- BEGIN_TYPE_LOCK ();
5920- // We must assign the version before doing the lookup. If
5921- // find_name_in_mro() blocks and releases the critical section
5922- // then the type version can change.
5923- if (MCACHE_CACHEABLE_NAME (name )) {
5924- has_version = assign_version_tag (interp , type );
5925- assigned_version = type -> tp_version_tag ;
5926- }
5927- res = find_name_in_mro (type , name , out );
5928- END_TYPE_LOCK ();
5920+ unsigned int version_tag = FT_ATOMIC_LOAD_UINT (type -> tp_version_tag );
5921+ if (should_assign_version_tag (type , name , version_tag )) {
5922+ BEGIN_TYPE_LOCK ();
5923+ assign_version_tag (interp , type );
5924+ version_tag = type -> tp_version_tag ;
5925+ res = find_name_in_mro (type , name , out );
5926+ END_TYPE_LOCK ();
5927+ }
5928+ else {
5929+ res = find_name_in_mro (type , name , out );
5930+ }
59295931
59305932 /* Only put NULL results into cache if there was no error. */
59315933 if (res < 0 ) {
59325934 * out = PyStackRef_NULL ;
59335935 return 0 ;
59345936 }
59355937
5936- if (has_version ) {
5937- PyObject * res_obj = PyStackRef_AsPyObjectBorrow (* out );
5938+ if (version_tag == 0 || !MCACHE_CACHEABLE_NAME (name )) {
5939+ return 0 ;
5940+ }
5941+
5942+ PyObject * res_obj = PyStackRef_AsPyObjectBorrow (* out );
59385943#if Py_GIL_DISABLED
5939- update_cache_gil_disabled (entry , name , assigned_version , res_obj );
5944+ update_cache_gil_disabled (entry , name , version_tag , res_obj );
59405945#else
5941- PyObject * old_value = update_cache (entry , name , assigned_version , res_obj );
5942- Py_DECREF (old_value );
5946+ PyObject * old_value = update_cache (entry , name , version_tag , res_obj );
5947+ Py_DECREF (old_value );
59435948#endif
5944- }
5945- return has_version ? assigned_version : 0 ;
5949+ return version_tag ;
59465950}
59475951
59485952/* Internal API to look for a name through the MRO.
0 commit comments