Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
1 change: 0 additions & 1 deletion Lib/test/test_capi/test_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -1413,7 +1413,6 @@ def run_tasks():
self.assertNotIn(task.requester_tid, runner_tids)

@requires_subinterpreters
@support.skip_if_sanitizer("gh-129824: race on assign_version_tag", thread=True)
def test_isolated_subinterpreter(self):
# We exercise the most important permutations.

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Access and update ``_PyRuntime.types.next_version_tag`` in a thread-safe
manner.
32 changes: 27 additions & 5 deletions Objects/typeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ class object "PyObject *" "&PyBaseObject_Type"
PyUnicode_CheckExact(name) && \
(PyUnicode_GET_LENGTH(name) <= MCACHE_MAX_ATTR_SIZE)

#define NEXT_GLOBAL_VERSION_TAG _PyRuntime.types.next_version_tag
#define NEXT_VERSION_TAG(interp) \
(interp)->types.next_version_tag

Expand Down Expand Up @@ -1258,6 +1257,25 @@ _PyType_GetVersionForCurrentState(PyTypeObject *tp)
#error "_Py_ATTR_CACHE_UNUSED must be bigger than max"
#endif

static int
get_next_global_version(unsigned int *dest)
{
unsigned int v;
while (1) {
v = _Py_atomic_load_uint_relaxed(&_PyRuntime.types.next_version_tag);

/* Stop if passed the maximum or we successfully updated the field */
if (v > _Py_MAX_GLOBAL_TYPE_VERSION_TAG ||
_Py_atomic_compare_exchange_uint(&_PyRuntime.types.next_version_tag,
&v, v + 1)) {
break;
}
}

*dest = v;
return v <= _Py_MAX_GLOBAL_TYPE_VERSION_TAG;
}

static int
assign_version_tag(PyInterpreterState *interp, PyTypeObject *type)
{
Expand Down Expand Up @@ -1288,11 +1306,12 @@ assign_version_tag(PyInterpreterState *interp, PyTypeObject *type)
}
if (type->tp_flags & Py_TPFLAGS_IMMUTABLETYPE) {
/* static types */
if (NEXT_GLOBAL_VERSION_TAG > _Py_MAX_GLOBAL_TYPE_VERSION_TAG) {
unsigned int version;
if (!get_next_global_version(&version)) {
/* We have run out of version numbers */
return 0;
}
set_version_unlocked(type, NEXT_GLOBAL_VERSION_TAG++);
set_version_unlocked(type, version);
assert (type->tp_version_tag <= _Py_MAX_GLOBAL_TYPE_VERSION_TAG);
}
else {
Expand Down Expand Up @@ -8877,9 +8896,12 @@ init_static_type(PyInterpreterState *interp, PyTypeObject *self,
type_add_flags(self, _Py_TPFLAGS_STATIC_BUILTIN);
type_add_flags(self, Py_TPFLAGS_IMMUTABLETYPE);

assert(NEXT_GLOBAL_VERSION_TAG <= _Py_MAX_GLOBAL_TYPE_VERSION_TAG);
unsigned int version;
if (!get_next_global_version(&version)) {
assert("we have run out of version numbers");
}
if (self->tp_version_tag == 0) {
_PyType_SetVersion(self, NEXT_GLOBAL_VERSION_TAG++);
_PyType_SetVersion(self, version);
}
}
else {
Expand Down
Loading