1- #include "Python.h"
1+ // Lock-free per type method cache implementation.
2+
3+ // The cache is used for method and attribute lookups on type objects.
4+ // The stored names are always interned strings, and the
5+ // stored values are borrowed references to the corresponding method or attribute object.
6+ // For static types, the cache is stored on the per-interpreter managed_static_type_state,
7+ // and for heap types the cache is stored in the `PyTypeObject._tp_cache` field.
28
9+ #include "Python.h"
310#include "pycore_typecache.h"
411#include "pycore_interp.h" // PyInterpreterState
5- #include "pycore_object.h" // _PyObject_XDecRefDelayed()
612#include "pycore_pymem.h"
713#include "pycore_pystate.h" // _PyInterpreterState_GET()
814#include "pycore_pyatomic_ft_wrappers.h"
@@ -18,7 +24,10 @@ static struct {
1824 .used = 0 ,
1925 },
2026};
21-
27+ // The empty cache is statically allocated and shared across all the types,
28+ // when a type is modified, the cache of type is set to the empty cache
29+ // and when a cache entry is inserted to the empty cache, a new cache is
30+ // allocated for the type and the entry is inserted to the new cache.
2231#define empty_cache (empty_cache_storage.cache)
2332
2433static inline uint32_t cache_size (struct type_cache * cache )
@@ -95,7 +104,10 @@ static inline void type_cache_insert(struct type_cache *cache, PyObject *name,
95104 uint32_t index = hash & cache -> mask ;
96105 for (;;) {
97106 if (cache -> hashtable [index ].name == NULL ) {
107+ #ifndef Py_GIL_DISABLED
108+ // On free-threading, all interned strings are immortal.
98109 Py_INCREF (name );
110+ #endif
99111 FT_ATOMIC_STORE_PTR (cache -> hashtable [index ].value , value );
100112 FT_ATOMIC_STORE_PTR (cache -> hashtable [index ].name , name );
101113 cache -> used ++ ;
@@ -179,6 +191,7 @@ struct _PyTypeCacheLookupResult _PyTypeCache_Lookup(PyTypeObject *type, PyObject
179191 }
180192 index = (index + 1 ) & cache -> mask ;
181193 }
194+ // to maintain consistency with find_name_in_mro and prevent stale cache reads
182195 uint32_t cache_version = FT_ATOMIC_LOAD_UINT_RELAXED (cache -> version_tag );
183196 if (cache_version != FT_ATOMIC_LOAD_UINT_RELAXED (type -> tp_version_tag )) {
184197 PyStackRef_XCLOSE (out_ref );
@@ -187,6 +200,7 @@ struct _PyTypeCacheLookupResult _PyTypeCache_Lookup(PyTypeObject *type, PyObject
187200 return (struct _PyTypeCacheLookupResult ){out_ref , 1 , cache_version };
188201}
189202
203+
190204void _PyTypeCache_Invalidate (PyTypeObject * type ) {
191205 struct type_cache * cache = get_cache (type );
192206 set_cache (type , & empty_cache );
0 commit comments