Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 209a0a7

Browse filesBrowse files
gh-95795: Move types.next_version_tag to PyInterpreterState (gh-102343)
Core static types will continue to use the global value. All other types will use the per-interpreter value. They all share the same range, where the global types use values < 2^16 and each interpreter uses values higher than that.
1 parent 0dc8b50 commit 209a0a7
Copy full SHA for 209a0a7

File tree

Expand file treeCollapse file tree

4 files changed

+94
-43
lines changed
Filter options
Expand file treeCollapse file tree

4 files changed

+94
-43
lines changed

‎Include/internal/pycore_runtime.h

Copy file name to clipboardExpand all lines: Include/internal/pycore_runtime.h
+2-7Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ extern "C" {
2525
#include "pycore_signal.h" // struct _signals_runtime_state
2626
#include "pycore_time.h" // struct _time_runtime_state
2727
#include "pycore_tracemalloc.h" // struct _tracemalloc_runtime_state
28+
#include "pycore_typeobject.h" // struct types_runtime_state
2829
#include "pycore_unicodeobject.h" // struct _Py_unicode_runtime_ids
2930

3031
struct _getargs_runtime_state {
@@ -150,13 +151,7 @@ typedef struct pyruntimestate {
150151
struct _py_object_runtime_state object_state;
151152
struct _Py_float_runtime_state float_state;
152153
struct _Py_unicode_runtime_state unicode_state;
153-
154-
struct {
155-
/* Used to set PyTypeObject.tp_version_tag */
156-
// bpo-42745: next_version_tag remains shared by all interpreters
157-
// because of static types.
158-
unsigned int next_version_tag;
159-
} types;
154+
struct _types_runtime_state types;
160155

161156
/* All the objects that are shared by the runtime's interpreters. */
162157
struct _Py_static_objects static_objects;

‎Include/internal/pycore_runtime_init.h

Copy file name to clipboardExpand all lines: Include/internal/pycore_runtime_init.h
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,9 @@ extern PyTypeObject _PyExc_MemoryError;
112112
.func_state = { \
113113
.next_version = 1, \
114114
}, \
115+
.types = { \
116+
.next_version_tag = _Py_TYPE_BASE_VERSION_TAG, \
117+
}, \
115118
.static_objects = { \
116119
.singletons = { \
117120
._not_used = 1, \

‎Include/internal/pycore_typeobject.h

Copy file name to clipboardExpand all lines: Include/internal/pycore_typeobject.h
+39-20Lines changed: 39 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,17 @@ extern "C" {
1111
#endif
1212

1313

14-
/* runtime lifecycle */
15-
16-
extern PyStatus _PyTypes_InitTypes(PyInterpreterState *);
17-
extern void _PyTypes_FiniTypes(PyInterpreterState *);
18-
extern void _PyTypes_Fini(PyInterpreterState *);
19-
14+
/* state */
2015

21-
/* other API */
22-
23-
/* Length of array of slotdef pointers used to store slots with the
24-
same __name__. There should be at most MAX_EQUIV-1 slotdef entries with
25-
the same __name__, for any __name__. Since that's a static property, it is
26-
appropriate to declare fixed-size arrays for this. */
27-
#define MAX_EQUIV 10
16+
#define _Py_TYPE_BASE_VERSION_TAG (2<<16)
17+
#define _Py_MAX_GLOBAL_TYPE_VERSION_TAG (_Py_TYPE_BASE_VERSION_TAG - 1)
2818

29-
typedef struct wrapperbase pytype_slotdef;
19+
struct _types_runtime_state {
20+
/* Used to set PyTypeObject.tp_version_tag for core static types. */
21+
// bpo-42745: next_version_tag remains shared by all interpreters
22+
// because of static types.
23+
unsigned int next_version_tag;
24+
};
3025

3126

3227
// Type attribute lookup cache: speed up attribute and method lookups,
@@ -57,6 +52,36 @@ typedef struct {
5752
PyObject *tp_weaklist;
5853
} static_builtin_state;
5954

55+
struct types_state {
56+
/* Used to set PyTypeObject.tp_version_tag.
57+
It starts at _Py_MAX_GLOBAL_TYPE_VERSION_TAG + 1,
58+
where all those lower numbers are used for core static types. */
59+
unsigned int next_version_tag;
60+
61+
struct type_cache type_cache;
62+
size_t num_builtins_initialized;
63+
static_builtin_state builtins[_Py_MAX_STATIC_BUILTIN_TYPES];
64+
};
65+
66+
67+
/* runtime lifecycle */
68+
69+
extern PyStatus _PyTypes_InitTypes(PyInterpreterState *);
70+
extern void _PyTypes_FiniTypes(PyInterpreterState *);
71+
extern void _PyTypes_Fini(PyInterpreterState *);
72+
73+
74+
/* other API */
75+
76+
/* Length of array of slotdef pointers used to store slots with the
77+
same __name__. There should be at most MAX_EQUIV-1 slotdef entries with
78+
the same __name__, for any __name__. Since that's a static property, it is
79+
appropriate to declare fixed-size arrays for this. */
80+
#define MAX_EQUIV 10
81+
82+
typedef struct wrapperbase pytype_slotdef;
83+
84+
6085
static inline PyObject **
6186
_PyStaticType_GET_WEAKREFS_LISTPTR(static_builtin_state *state)
6287
{
@@ -78,12 +103,6 @@ _PyType_GetModuleState(PyTypeObject *type)
78103
return mod->md_state;
79104
}
80105

81-
struct types_state {
82-
struct type_cache type_cache;
83-
size_t num_builtins_initialized;
84-
static_builtin_state builtins[_Py_MAX_STATIC_BUILTIN_TYPES];
85-
};
86-
87106

88107
extern int _PyStaticType_InitBuiltin(PyTypeObject *type);
89108
extern static_builtin_state * _PyStaticType_GetState(PyTypeObject *);

‎Objects/typeobject.c

Copy file name to clipboardExpand all lines: Objects/typeobject.c
+50-16Lines changed: 50 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@ class object "PyObject *" "&PyBaseObject_Type"
4545
PyUnicode_IS_READY(name) && \
4646
(PyUnicode_GET_LENGTH(name) <= MCACHE_MAX_ATTR_SIZE)
4747

48-
#define next_version_tag (_PyRuntime.types.next_version_tag)
48+
#define NEXT_GLOBAL_VERSION_TAG _PyRuntime.types.next_version_tag
49+
#define NEXT_VERSION_TAG(interp) \
50+
(interp)->types.next_version_tag
4951

5052
typedef struct PySlot_Offset {
5153
short subslot_offset;
@@ -332,7 +334,7 @@ _PyType_ClearCache(PyInterpreterState *interp)
332334
// use Py_SETREF() rather than using slower Py_XSETREF().
333335
type_cache_clear(cache, Py_None);
334336

335-
return next_version_tag - 1;
337+
return NEXT_VERSION_TAG(interp) - 1;
336338
}
337339

338340

@@ -401,7 +403,7 @@ PyType_ClearWatcher(int watcher_id)
401403
return 0;
402404
}
403405

404-
static int assign_version_tag(PyTypeObject *type);
406+
static int assign_version_tag(PyInterpreterState *interp, PyTypeObject *type);
405407

406408
int
407409
PyType_Watch(int watcher_id, PyObject* obj)
@@ -416,7 +418,7 @@ PyType_Watch(int watcher_id, PyObject* obj)
416418
return -1;
417419
}
418420
// ensure we will get a callback on the next modification
419-
assign_version_tag(type);
421+
assign_version_tag(interp, type);
420422
type->tp_watched |= (1 << watcher_id);
421423
return 0;
422424
}
@@ -549,7 +551,9 @@ type_mro_modified(PyTypeObject *type, PyObject *bases) {
549551
}
550552
}
551553
return;
554+
552555
clear:
556+
assert(!(type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN));
553557
type->tp_flags &= ~Py_TPFLAGS_VALID_VERSION_TAG;
554558
type->tp_version_tag = 0; /* 0 is not a valid version tag */
555559
if (PyType_HasFeature(type, Py_TPFLAGS_HEAPTYPE)) {
@@ -560,7 +564,7 @@ type_mro_modified(PyTypeObject *type, PyObject *bases) {
560564
}
561565

562566
static int
563-
assign_version_tag(PyTypeObject *type)
567+
assign_version_tag(PyInterpreterState *interp, PyTypeObject *type)
564568
{
565569
/* Ensure that the tp_version_tag is valid and set
566570
Py_TPFLAGS_VALID_VERSION_TAG. To respect the invariant, this
@@ -574,18 +578,30 @@ assign_version_tag(PyTypeObject *type)
574578
return 0;
575579
}
576580

577-
if (next_version_tag == 0) {
578-
/* We have run out of version numbers */
579-
return 0;
581+
if (type->tp_flags & Py_TPFLAGS_IMMUTABLETYPE) {
582+
/* static types */
583+
if (NEXT_GLOBAL_VERSION_TAG > _Py_MAX_GLOBAL_TYPE_VERSION_TAG) {
584+
/* We have run out of version numbers */
585+
return 0;
586+
}
587+
type->tp_version_tag = NEXT_GLOBAL_VERSION_TAG++;
588+
assert (type->tp_version_tag <= _Py_MAX_GLOBAL_TYPE_VERSION_TAG);
589+
}
590+
else {
591+
/* heap types */
592+
if (NEXT_VERSION_TAG(interp) == 0) {
593+
/* We have run out of version numbers */
594+
return 0;
595+
}
596+
type->tp_version_tag = NEXT_VERSION_TAG(interp)++;
597+
assert (type->tp_version_tag != 0);
580598
}
581-
type->tp_version_tag = next_version_tag++;
582-
assert (type->tp_version_tag != 0);
583599

584600
PyObject *bases = type->tp_bases;
585601
Py_ssize_t n = PyTuple_GET_SIZE(bases);
586602
for (Py_ssize_t i = 0; i < n; i++) {
587603
PyObject *b = PyTuple_GET_ITEM(bases, i);
588-
if (!assign_version_tag(_PyType_CAST(b)))
604+
if (!assign_version_tag(interp, _PyType_CAST(b)))
589605
return 0;
590606
}
591607
type->tp_flags |= Py_TPFLAGS_VALID_VERSION_TAG;
@@ -594,7 +610,8 @@ assign_version_tag(PyTypeObject *type)
594610

595611
int PyUnstable_Type_AssignVersionTag(PyTypeObject *type)
596612
{
597-
return assign_version_tag(type);
613+
PyInterpreterState *interp = _PyInterpreterState_GET();
614+
return assign_version_tag(interp, type);
598615
}
599616

600617

@@ -2346,7 +2363,15 @@ mro_internal(PyTypeObject *type, PyObject **p_old_mro)
23462363
from the custom MRO */
23472364
type_mro_modified(type, type->tp_bases);
23482365

2349-
PyType_Modified(type);
2366+
// XXX Expand this to Py_TPFLAGS_IMMUTABLETYPE?
2367+
if (!(type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN)) {
2368+
PyType_Modified(type);
2369+
}
2370+
else {
2371+
/* For static builtin types, this is only called during init
2372+
before the method cache has been populated. */
2373+
assert(_PyType_HasFeature(type, Py_TPFLAGS_VALID_VERSION_TAG));
2374+
}
23502375

23512376
if (p_old_mro != NULL)
23522377
*p_old_mro = old_mro; /* transfer the ownership */
@@ -4181,6 +4206,7 @@ _PyType_Lookup(PyTypeObject *type, PyObject *name)
41814206
{
41824207
PyObject *res;
41834208
int error;
4209+
PyInterpreterState *interp = _PyInterpreterState_GET();
41844210

41854211
unsigned int h = MCACHE_HASH_METHOD(type, name);
41864212
struct type_cache *cache = get_type_cache();
@@ -4215,7 +4241,7 @@ _PyType_Lookup(PyTypeObject *type, PyObject *name)
42154241
return NULL;
42164242
}
42174243

4218-
if (MCACHE_CACHEABLE_NAME(name) && assign_version_tag(type)) {
4244+
if (MCACHE_CACHEABLE_NAME(name) && assign_version_tag(interp, type)) {
42194245
h = MCACHE_HASH_METHOD(type, name);
42204246
struct type_cache_entry *entry = &cache->hashtable[h];
42214247
entry->version = type->tp_version_tag;
@@ -6676,8 +6702,11 @@ type_ready_mro(PyTypeObject *type)
66766702
assert(type->tp_mro != NULL);
66776703
assert(PyTuple_Check(type->tp_mro));
66786704

6679-
/* All bases of statically allocated type should be statically allocated */
6705+
/* All bases of statically allocated type should be statically allocated,
6706+
and static builtin types must have static builtin bases. */
66806707
if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE)) {
6708+
assert(type->tp_flags & Py_TPFLAGS_IMMUTABLETYPE);
6709+
int isbuiltin = type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN;
66816710
PyObject *mro = type->tp_mro;
66826711
Py_ssize_t n = PyTuple_GET_SIZE(mro);
66836712
for (Py_ssize_t i = 0; i < n; i++) {
@@ -6689,6 +6718,7 @@ type_ready_mro(PyTypeObject *type)
66896718
type->tp_name, base->tp_name);
66906719
return -1;
66916720
}
6721+
assert(!isbuiltin || (base->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN));
66926722
}
66936723
}
66946724
return 0;
@@ -7000,7 +7030,11 @@ PyType_Ready(PyTypeObject *type)
70007030
int
70017031
_PyStaticType_InitBuiltin(PyTypeObject *self)
70027032
{
7003-
self->tp_flags = self->tp_flags | _Py_TPFLAGS_STATIC_BUILTIN;
7033+
self->tp_flags |= _Py_TPFLAGS_STATIC_BUILTIN;
7034+
7035+
assert(NEXT_GLOBAL_VERSION_TAG <= _Py_MAX_GLOBAL_TYPE_VERSION_TAG);
7036+
self->tp_version_tag = NEXT_GLOBAL_VERSION_TAG++;
7037+
self->tp_flags |= Py_TPFLAGS_VALID_VERSION_TAG;
70047038

70057039
static_builtin_state_init(self);
70067040

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.