Skip to content

Commit 72dab63

Browse files
address code review
1 parent 728021c commit 72dab63

3 files changed

Lines changed: 24 additions & 18 deletions

File tree

Include/internal/pycore_typecache.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ extern "C" {
1111
#include "pycore_stackref.h"
1212

1313

14-
#define _Py_TYPECACHE_MINSIZE 8
14+
#define _Py_TYPECACHE_MINSIZE (1 << 3)
15+
#define _Py_TYPECACHE_MAXSIZE (1 << 16)
1516

1617
struct type_cache_entry {
1718
PyObject *name; // name of the attribute or method, interned string, NULL if the entry is empty
@@ -25,7 +26,7 @@ struct type_cache {
2526
uint32_t version_tag; // initialized from type->tp_version_tag
2627
uint32_t available; // number of available entries in hashtable
2728
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
29+
struct type_cache_entry hashtable[]; // hashtable entries, the total size is always power of 2 and at least _Py_TYPECACHE_MINSIZE
2930
};
3031

3132
struct _PyTypeCacheLookupResult {

Objects/typeobject.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6854,6 +6854,7 @@ type_dealloc(PyObject *self)
68546854
Py_XDECREF(type->tp_mro);
68556855
Py_XDECREF(type->tp_cache);
68566856
clear_tp_subclasses(type);
6857+
_PyTypeCache_Invalidate(type);
68576858

68586859
/* A type's tp_doc is heap allocated, unlike the tp_doc slots
68596860
* of most other objects. It's okay to cast it to char *.

Python/typecache.c

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ static struct {
2222
.mask = _Py_TYPECACHE_MINSIZE - 1,
2323
.available = 0,
2424
.used = 0,
25+
.version_tag = 0,
2526
},
2627
};
2728
// The empty cache is statically allocated and shared across all the types,
@@ -96,13 +97,13 @@ cache_slot(PyTypeObject *type)
9697
static inline struct type_cache *
9798
cache_get(PyTypeObject *type)
9899
{
99-
return (struct type_cache *)FT_ATOMIC_LOAD_PTR(*cache_slot(type));
100+
return (struct type_cache *)FT_ATOMIC_LOAD_PTR_ACQUIRE(*cache_slot(type));
100101
}
101102

102103
static inline void
103104
cache_set(PyTypeObject *type, struct type_cache *cache)
104105
{
105-
FT_ATOMIC_STORE_PTR(*cache_slot(type), cache);
106+
FT_ATOMIC_STORE_PTR_RELEASE(*cache_slot(type), cache);
106107
}
107108

108109
void
@@ -124,8 +125,8 @@ cache_insert(struct type_cache *cache, PyObject *name,
124125
// On free-threading, all interned strings are immortal.
125126
Py_INCREF(name);
126127
#endif
127-
FT_ATOMIC_STORE_PTR(cache->hashtable[index].value, value);
128-
FT_ATOMIC_STORE_PTR(cache->hashtable[index].name, name);
128+
cache->hashtable[index].value = value;
129+
FT_ATOMIC_STORE_PTR_RELEASE(cache->hashtable[index].name, name);
129130
cache->used++;
130131
cache->available--;
131132
return;
@@ -151,17 +152,20 @@ cache_resize(PyTypeObject *type, struct type_cache *cache)
151152
// double the cache size when resizing
152153
new_size = old_size * 2;
153154
}
155+
if (new_size > _Py_TYPECACHE_MAXSIZE) {
156+
// The cache is too big, don't resize and just return.
157+
return -1;
158+
}
154159
struct type_cache *new_cache = cache_allocate(new_size);
155160
if (new_cache == NULL) {
156161
return -1;
157162
}
158163
for (uint32_t i = 0; i < old_size; i++) {
159164
if (cache->hashtable[i].name != NULL) {
160-
cache_insert(new_cache, cache->hashtable[i].name,
161-
cache->hashtable[i].value);
165+
cache_insert(new_cache, cache->hashtable[i].name, cache->hashtable[i].value);
162166
}
163167
}
164-
new_cache->version_tag = cache->version_tag;
168+
new_cache->version_tag = type->tp_version_tag;
165169
cache_set(type, new_cache);
166170
cache_free_delayed(cache);
167171
return 0;
@@ -183,8 +187,8 @@ _PyTypeCache_Insert(PyTypeObject *type, PyObject *name, PyObject *value)
183187
cache = cache_get(type);
184188
assert(cache->available > 0);
185189
}
190+
assert(cache->version_tag == type->tp_version_tag);
186191
cache_insert(cache, name, value);
187-
FT_ATOMIC_STORE_UINT_RELAXED(cache->version_tag, FT_ATOMIC_LOAD_UINT_RELAXED(type->tp_version_tag));
188192
}
189193

190194

@@ -205,10 +209,7 @@ _PyTypeCache_Lookup(PyTypeObject *type, PyObject *name)
205209
uint32_t index = hash & cache->mask;
206210
_PyStackRef out_ref;
207211
for (;;) {
208-
PyObject *entry_name = FT_ATOMIC_LOAD_PTR(cache->hashtable[index].name);
209-
if (entry_name == NULL) {
210-
return miss;
211-
}
212+
PyObject *entry_name = FT_ATOMIC_LOAD_PTR_ACQUIRE(cache->hashtable[index].name);
212213
if (entry_name == name) {
213214
#ifdef Py_GIL_DISABLED
214215
if (!_Py_TryXGetStackRef(&cache->hashtable[index].value, &out_ref)) {
@@ -220,15 +221,18 @@ _PyTypeCache_Lookup(PyTypeObject *type, PyObject *name)
220221
#endif
221222
break;
222223
}
224+
else if (entry_name == NULL) {
225+
return miss;
226+
}
223227
index = (index + 1) & cache->mask;
224228
}
225-
// to maintain consistency with find_name_in_mro and prevent stale cache reads
226-
uint32_t cache_version = FT_ATOMIC_LOAD_UINT_RELAXED(cache->version_tag);
227-
if (cache_version != FT_ATOMIC_LOAD_UINT_RELAXED(type->tp_version_tag)) {
229+
// Check the cache version against the type version tag to maintain
230+
// consistency with find_name_in_mro and prevent stale cache reads
231+
if (cache->version_tag != FT_ATOMIC_LOAD_UINT_RELAXED(type->tp_version_tag)) {
228232
PyStackRef_XCLOSE(out_ref);
229233
return miss;
230234
}
231-
return (struct _PyTypeCacheLookupResult){out_ref, 1, cache_version};
235+
return (struct _PyTypeCacheLookupResult){out_ref, 1, cache->version_tag};
232236
}
233237

234238
// Invalidate the type cache of the type.

0 commit comments

Comments
 (0)