Skip to content

Commit 8d24e30

Browse files
add comments
1 parent a9d6917 commit 8d24e30

2 files changed

Lines changed: 22 additions & 13 deletions

File tree

Include/internal/pycore_typecache.h

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,24 @@ extern "C" {
1414
#define _Py_TYPECACHE_MINSIZE 8
1515

1616
struct type_cache_entry {
17-
PyObject *name;
18-
PyObject *value;
17+
PyObject *name; // name of the attribute or method, interned string, NULL if the entry is empty
18+
PyObject *value; // borrowed reference or NULL
1919
};
2020

21+
// Per-type attribute lookup cache: speed up attribute and method lookups,
22+
// see _PyTypeCache_Lookup().
2123
struct type_cache {
22-
uint32_t mask;
23-
uint32_t version_tag;
24-
uint32_t available;
25-
uint32_t used;
26-
struct type_cache_entry hashtable[1];
24+
uint32_t mask; // mask for indexing into hashtable, i.e. size of hashtable is mask + 1
25+
uint32_t version_tag; // initialized from type->tp_version_tag
26+
uint32_t available; // number of available entries in hashtable
27+
uint32_t used; // number of used entries in hashtable
28+
struct type_cache_entry hashtable[1]; // hashtable entries, the total size is always power of 2 and at least _Py_TYPECACHE_MINSIZE
2729
};
2830

2931
struct _PyTypeCacheLookupResult {
30-
_PyStackRef value;
31-
int cache_hit;
32-
uint32_t version_tag;
32+
_PyStackRef value; // value is a stack reference to the cached attribute or method, or NULL if not found
33+
int cache_hit; // 1 if the cache entry is valid and matches the type's version tag, 0 otherwise
34+
uint32_t version_tag; // version tag of the type when the value was cached
3335
};
3436

3537

Python/typecache.c

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,18 +155,20 @@ cache_resize(PyTypeObject *type, struct type_cache *cache)
155155
if (new_cache == NULL) {
156156
return -1;
157157
}
158-
FT_ATOMIC_STORE_UINT_RELAXED(cache->version_tag, FT_ATOMIC_LOAD_UINT_RELAXED(type->tp_version_tag));
159158
for (uint32_t i = 0; i < old_size; i++) {
160159
if (cache->hashtable[i].name != NULL) {
161160
cache_insert(new_cache, cache->hashtable[i].name,
162161
cache->hashtable[i].value);
163162
}
164163
}
164+
new_cache->version_tag = cache->version_tag;
165165
cache_set(type, new_cache);
166166
cache_free_delayed(cache);
167167
return 0;
168168
}
169169

170+
// Insert a new entry to the type cache. If the cache is full, resize it before inserting the new entry.
171+
// The TYPE_LOCK should be held while calling this function.
170172
void
171173
_PyTypeCache_Insert(PyTypeObject *type, PyObject *name, PyObject *value)
172174
{
@@ -185,6 +187,10 @@ _PyTypeCache_Insert(PyTypeObject *type, PyObject *name, PyObject *value)
185187
FT_ATOMIC_STORE_UINT_RELAXED(cache->version_tag, FT_ATOMIC_LOAD_UINT_RELAXED(type->tp_version_tag));
186188
}
187189

190+
191+
// Lookup the given name in the type cache.
192+
// The cache is lock-free so it is possible that cache becomes stale during the lookup,
193+
// to prevent returning stale cache entry, the cache version is compared with the type version tag.
188194
struct _PyTypeCacheLookupResult
189195
_PyTypeCache_Lookup(PyTypeObject *type, PyObject *name)
190196
{
@@ -225,12 +231,13 @@ _PyTypeCache_Lookup(PyTypeObject *type, PyObject *name)
225231
return (struct _PyTypeCacheLookupResult){out_ref, 1, cache_version};
226232
}
227233

228-
234+
// Invalidate the type cache of the type.
235+
// The cache is set to the empty cache and the old cache is freed with QSBR.
236+
// The TYPE_LOCK should be held while calling this function.
229237
void
230238
_PyTypeCache_Invalidate(PyTypeObject *type)
231239
{
232240
struct type_cache *cache = cache_get(type);
233-
// if the type was modified, the cache is set to the empty cache and the old cache is freed after a delay.
234241
cache_set(type, &empty_cache);
235242
cache_free_delayed(cache);
236243
}

0 commit comments

Comments
 (0)