#endif
-/* runtime lifecycle */
-
-extern PyStatus _PyTypes_InitTypes(PyInterpreterState *);
-extern void _PyTypes_FiniTypes(PyInterpreterState *);
-extern void _PyTypes_Fini(PyInterpreterState *);
-
+/* state */
-/* other API */
-
-/* Length of array of slotdef pointers used to store slots with the
- same __name__. There should be at most MAX_EQUIV-1 slotdef entries with
- the same __name__, for any __name__. Since that's a static property, it is
- appropriate to declare fixed-size arrays for this. */
-#define MAX_EQUIV 10
+#define _Py_TYPE_BASE_VERSION_TAG (2<<16)
+#define _Py_MAX_GLOBAL_TYPE_VERSION_TAG (_Py_TYPE_BASE_VERSION_TAG - 1)
-typedef struct wrapperbase pytype_slotdef;
+struct _types_runtime_state {
+ /* Used to set PyTypeObject.tp_version_tag for core static types. */
+ // bpo-42745: next_version_tag remains shared by all interpreters
+ // because of static types.
+ unsigned int next_version_tag;
+};
// Type attribute lookup cache: speed up attribute and method lookups,
PyObject *tp_weaklist;
} static_builtin_state;
+struct types_state {
+ /* Used to set PyTypeObject.tp_version_tag.
+ It starts at _Py_MAX_GLOBAL_TYPE_VERSION_TAG + 1,
+ where all those lower numbers are used for core static types. */
+ unsigned int next_version_tag;
+
+ struct type_cache type_cache;
+ size_t num_builtins_initialized;
+ static_builtin_state builtins[_Py_MAX_STATIC_BUILTIN_TYPES];
+};
+
+
+/* runtime lifecycle */
+
+extern PyStatus _PyTypes_InitTypes(PyInterpreterState *);
+extern void _PyTypes_FiniTypes(PyInterpreterState *);
+extern void _PyTypes_Fini(PyInterpreterState *);
+
+
+/* other API */
+
+/* Length of array of slotdef pointers used to store slots with the
+ same __name__. There should be at most MAX_EQUIV-1 slotdef entries with
+ the same __name__, for any __name__. Since that's a static property, it is
+ appropriate to declare fixed-size arrays for this. */
+#define MAX_EQUIV 10
+
+typedef struct wrapperbase pytype_slotdef;
+
+
static inline PyObject **
_PyStaticType_GET_WEAKREFS_LISTPTR(static_builtin_state *state)
{
return mod->md_state;
}
-struct types_state {
- struct type_cache type_cache;
- size_t num_builtins_initialized;
- static_builtin_state builtins[_Py_MAX_STATIC_BUILTIN_TYPES];
-};
-
extern int _PyStaticType_InitBuiltin(PyTypeObject *type);
extern static_builtin_state * _PyStaticType_GetState(PyTypeObject *);
PyUnicode_IS_READY(name) && \
(PyUnicode_GET_LENGTH(name) <= MCACHE_MAX_ATTR_SIZE)
-#define next_version_tag (_PyRuntime.types.next_version_tag)
+#define NEXT_GLOBAL_VERSION_TAG _PyRuntime.types.next_version_tag
+#define NEXT_VERSION_TAG(interp) \
+ (interp)->types.next_version_tag
typedef struct PySlot_Offset {
short subslot_offset;
// use Py_SETREF() rather than using slower Py_XSETREF().
type_cache_clear(cache, Py_None);
- return next_version_tag - 1;
+ return NEXT_VERSION_TAG(interp) - 1;
}
return 0;
}
-static int assign_version_tag(PyTypeObject *type);
+static int assign_version_tag(PyInterpreterState *interp, PyTypeObject *type);
int
PyType_Watch(int watcher_id, PyObject* obj)
return -1;
}
// ensure we will get a callback on the next modification
- assign_version_tag(type);
+ assign_version_tag(interp, type);
type->tp_watched |= (1 << watcher_id);
return 0;
}
}
}
return;
+
clear:
+ assert(!(type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN));
type->tp_flags &= ~Py_TPFLAGS_VALID_VERSION_TAG;
type->tp_version_tag = 0; /* 0 is not a valid version tag */
if (PyType_HasFeature(type, Py_TPFLAGS_HEAPTYPE)) {
}
static int
-assign_version_tag(PyTypeObject *type)
+assign_version_tag(PyInterpreterState *interp, PyTypeObject *type)
{
/* Ensure that the tp_version_tag is valid and set
Py_TPFLAGS_VALID_VERSION_TAG. To respect the invariant, this
return 0;
}
- if (next_version_tag == 0) {
- /* We have run out of version numbers */
- return 0;
+ if (type->tp_flags & Py_TPFLAGS_IMMUTABLETYPE) {
+ /* static types */
+ if (NEXT_GLOBAL_VERSION_TAG > _Py_MAX_GLOBAL_TYPE_VERSION_TAG) {
+ /* We have run out of version numbers */
+ return 0;
+ }
+ type->tp_version_tag = NEXT_GLOBAL_VERSION_TAG++;
+ assert (type->tp_version_tag <= _Py_MAX_GLOBAL_TYPE_VERSION_TAG);
+ }
+ else {
+ /* heap types */
+ if (NEXT_VERSION_TAG(interp) == 0) {
+ /* We have run out of version numbers */
+ return 0;
+ }
+ type->tp_version_tag = NEXT_VERSION_TAG(interp)++;
+ assert (type->tp_version_tag != 0);
}
- type->tp_version_tag = next_version_tag++;
- assert (type->tp_version_tag != 0);
PyObject *bases = type->tp_bases;
Py_ssize_t n = PyTuple_GET_SIZE(bases);
for (Py_ssize_t i = 0; i < n; i++) {
PyObject *b = PyTuple_GET_ITEM(bases, i);
- if (!assign_version_tag(_PyType_CAST(b)))
+ if (!assign_version_tag(interp, _PyType_CAST(b)))
return 0;
}
type->tp_flags |= Py_TPFLAGS_VALID_VERSION_TAG;
int PyUnstable_Type_AssignVersionTag(PyTypeObject *type)
{
- return assign_version_tag(type);
+ PyInterpreterState *interp = _PyInterpreterState_GET();
+ return assign_version_tag(interp, type);
}
from the custom MRO */
type_mro_modified(type, type->tp_bases);
- PyType_Modified(type);
+ // XXX Expand this to Py_TPFLAGS_IMMUTABLETYPE?
+ if (!(type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN)) {
+ PyType_Modified(type);
+ }
+ else {
+ /* For static builtin types, this is only called during init
+ before the method cache has been populated. */
+ assert(_PyType_HasFeature(type, Py_TPFLAGS_VALID_VERSION_TAG));
+ }
if (p_old_mro != NULL)
*p_old_mro = old_mro; /* transfer the ownership */
{
PyObject *res;
int error;
+ PyInterpreterState *interp = _PyInterpreterState_GET();
unsigned int h = MCACHE_HASH_METHOD(type, name);
struct type_cache *cache = get_type_cache();
return NULL;
}
- if (MCACHE_CACHEABLE_NAME(name) && assign_version_tag(type)) {
+ if (MCACHE_CACHEABLE_NAME(name) && assign_version_tag(interp, type)) {
h = MCACHE_HASH_METHOD(type, name);
struct type_cache_entry *entry = &cache->hashtable[h];
entry->version = type->tp_version_tag;
assert(type->tp_mro != NULL);
assert(PyTuple_Check(type->tp_mro));
- /* All bases of statically allocated type should be statically allocated */
+ /* All bases of statically allocated type should be statically allocated,
+ and static builtin types must have static builtin bases. */
if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE)) {
+ assert(type->tp_flags & Py_TPFLAGS_IMMUTABLETYPE);
+ int isbuiltin = type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN;
PyObject *mro = type->tp_mro;
Py_ssize_t n = PyTuple_GET_SIZE(mro);
for (Py_ssize_t i = 0; i < n; i++) {
type->tp_name, base->tp_name);
return -1;
}
+ assert(!isbuiltin || (base->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN));
}
}
return 0;
int
_PyStaticType_InitBuiltin(PyTypeObject *self)
{
- self->tp_flags = self->tp_flags | _Py_TPFLAGS_STATIC_BUILTIN;
+ self->tp_flags |= _Py_TPFLAGS_STATIC_BUILTIN;
+
+ assert(NEXT_GLOBAL_VERSION_TAG <= _Py_MAX_GLOBAL_TYPE_VERSION_TAG);
+ self->tp_version_tag = NEXT_GLOBAL_VERSION_TAG++;
+ self->tp_flags |= Py_TPFLAGS_VALID_VERSION_TAG;
static_builtin_state_init(self);