Skip to content

Commit bd59f2d

Browse files
add comments
1 parent c9bdf4b commit bd59f2d

2 files changed

Lines changed: 21 additions & 4 deletions

File tree

Objects/typeobject.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6652,7 +6652,10 @@ type_setattro(PyObject *self, PyObject *name, PyObject *value)
66526652
done:
66536653
Py_DECREF(name);
66546654
Py_XDECREF(descr);
6655-
Py_XDECREF(old_value);
6655+
// delay decref of the old value as lock-free type cache readers may access it
6656+
if (old_value != NULL && !_Py_IsImmortal(old_value)) {
6657+
_PyObject_XDecRefDelayed(old_value);
6658+
}
66566659
return res;
66576660
}
66586661

Python/typecache.c

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
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

2433
static 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+
190204
void _PyTypeCache_Invalidate(PyTypeObject *type) {
191205
struct type_cache *cache = get_cache(type);
192206
set_cache(type, &empty_cache);

0 commit comments

Comments
 (0)