Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Include/cpython/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,8 @@ struct _typeobject {
* This function must escape to any code that can result in
* the GC being run, such as Py_DECREF. */
_Py_iteritemfunc _tp_iteritem;

void *_tp_cache;
};

#define _Py_ATTR_CACHE_UNUSED (30000) // (see tp_versions_used)
Expand Down
23 changes: 4 additions & 19 deletions Include/internal/pycore_interp_structs.h
Original file line number Diff line number Diff line change
Expand Up @@ -548,23 +548,6 @@ struct _types_runtime_state {
};


// Type attribute lookup cache: speed up attribute and method lookups,
// see _PyType_Lookup().
struct type_cache_entry {
unsigned int version; // initialized from type->tp_version_tag
#ifdef Py_GIL_DISABLED
_PySeqLock sequence;
#endif
PyObject *name; // reference to exactly a str or None
PyObject *value; // borrowed reference or NULL
};

#define MCACHE_SIZE_EXP 12

struct type_cache {
struct type_cache_entry hashtable[1 << MCACHE_SIZE_EXP];
};

typedef struct {
PyTypeObject *type;
int isbuiltin;
Expand All @@ -579,6 +562,10 @@ typedef struct {
are also some diagnostic uses for the list of weakrefs,
so we still keep it. */
PyObject *tp_weaklist;
/* Per-interpreter attribute lookup cache (struct type_cache *).
For static builtin types the cache must be per-interpreter
because tp_dict and the values it stores are per-interpreter. */
void *_tp_cache;
} managed_static_type_state;

#define TYPE_VERSION_CACHE_SIZE (1<<12) /* Must be a power of 2 */
Expand All @@ -589,8 +576,6 @@ struct types_state {
where all those lower numbers are used for core static types. */
unsigned int next_version_tag;

struct type_cache type_cache;

/* Every static builtin type is initialized for each interpreter
during its own initialization, including for the main interpreter
during global runtime initialization. This is done by calling
Expand Down
2 changes: 0 additions & 2 deletions Include/internal/pycore_object.h
Original file line number Diff line number Diff line change
Expand Up @@ -291,8 +291,6 @@ _PyType_HasFeature(PyTypeObject *type, unsigned long feature) {
return ((type->tp_flags) & feature) != 0;
}

extern void _PyType_InitCache(PyInterpreterState *interp);

extern PyStatus _PyObject_InitState(PyInterpreterState *interp);
extern void _PyObject_FiniState(PyInterpreterState *interp);
extern bool _PyRefchain_IsTraced(PyInterpreterState *interp, PyObject *obj);
Expand Down
44 changes: 44 additions & 0 deletions Include/internal/pycore_typecache.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#ifndef PY_INTERNAL_TYPECACHE_H
#define PY_INTERNAL_TYPECACHE_H
#ifdef __cplusplus
extern "C" {
#endif

#ifndef Py_BUILD_CORE
# error "this header requires Py_BUILD_CORE define"
#endif

#include "pycore_stackref.h"


#define _Py_TYPECACHE_MINSIZE 8

struct type_cache_entry {
PyObject *name;
PyObject *value;
};

struct type_cache {
uint32_t mask;
uint32_t version_tag;
uint32_t available;
uint32_t used;
struct type_cache_entry hashtable[1];
};

struct _PyTypeCacheLookupResult {
_PyStackRef value;
int cache_hit;
uint32_t version_tag;
};


extern void _PyTypeCache_InitType(PyTypeObject *type);
extern void _PyTypeCache_Insert(PyTypeObject *type, PyObject *name, PyObject *value);
extern struct _PyTypeCacheLookupResult _PyTypeCache_Lookup(PyTypeObject *type, PyObject *name);
extern void _PyTypeCache_Invalidate(PyTypeObject *type);

#ifdef __cplusplus
}
#endif
#endif /* PY_INTERNAL_TYPECACHE_H */
18 changes: 18 additions & 0 deletions Lib/test/test_free_threading/test_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,24 @@ def reader_func():

self.run_one(writer_func, reader_func)

def test_attr_cache_mortal(self):
class C:
x = object()

class D(C):
pass

def writer_func():
for _ in range(3000):
C.x = object()

def reader_func():
for _ in range(3000):
C.x
D.x

self.run_one(writer_func, reader_func)

def test___class___modification(self):
loops = 200

Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_sys.py
Original file line number Diff line number Diff line change
Expand Up @@ -1788,7 +1788,7 @@ def delx(self): del self.__x
check((1,2,3), vsize('') + self.P + 3*self.P)
# type
# static type: PyTypeObject
fmt = 'P2nPI13Pl4Pn9Pn12PI2Pc'
fmt = 'P2nPI13Pl4Pn9Pn12PI2PcP'
s = vsize(fmt)
check(int, s)
typeid = 'n' if support.Py_GIL_DISABLED else ''
Expand Down
2 changes: 2 additions & 0 deletions Makefile.pre.in
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,7 @@ PYTHON_OBJS= \
Python/thread.o \
Python/traceback.o \
Python/tracemalloc.o \
Python/typecache.o \
Python/uniqueid.o \
Python/getopt.o \
Python/pystrcmp.o \
Expand Down Expand Up @@ -1411,6 +1412,7 @@ PYTHON_HEADERS= \
$(srcdir)/Include/internal/pycore_tracemalloc.h \
$(srcdir)/Include/internal/pycore_tstate.h \
$(srcdir)/Include/internal/pycore_tuple.h \
$(srcdir)/Include/internal/pycore_typecache.h \
$(srcdir)/Include/internal/pycore_typedefs.h \
$(srcdir)/Include/internal/pycore_typeobject.h \
$(srcdir)/Include/internal/pycore_typevarobject.h \
Expand Down
Loading
Loading