Skip to content

Commit 3ea7276

Browse files
[3.14] gh-129824: fix data races in subinterpreters under TSAN (GH-135794) (#136266)
gh-129824: fix data races in subinterpreters under TSAN (GH-135794) This fixes the data races in typeobject.c in subinterpreters under free-threading. The type flags and slots are only modified in the main interpreter as all static types are first initialised in main interpreter. (cherry picked from commit b582d75) Co-authored-by: Kumar Aditya <[email protected]>
1 parent d86ca7b commit 3ea7276

File tree

1 file changed

+44
-17
lines changed

1 file changed

+44
-17
lines changed

Objects/typeobject.c

Lines changed: 44 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ class object "PyObject *" "&PyBaseObject_Type"
5454
PyUnicode_CheckExact(name) && \
5555
(PyUnicode_GET_LENGTH(name) <= MCACHE_MAX_ATTR_SIZE)
5656

57-
#define NEXT_GLOBAL_VERSION_TAG _PyRuntime.types.next_version_tag
5857
#define NEXT_VERSION_TAG(interp) \
5958
(interp)->types.next_version_tag
6059

@@ -157,8 +156,8 @@ static_ext_type_lookup(PyInterpreterState *interp, size_t index,
157156
assert(index < _Py_MAX_MANAGED_STATIC_EXT_TYPES);
158157

159158
size_t full_index = index + _Py_MAX_MANAGED_STATIC_BUILTIN_TYPES;
160-
int64_t interp_count =
161-
_PyRuntime.types.managed_static.types[full_index].interp_count;
159+
int64_t interp_count = _Py_atomic_load_int64(
160+
&_PyRuntime.types.managed_static.types[full_index].interp_count);
162161
assert((interp_count == 0) ==
163162
(_PyRuntime.types.managed_static.types[full_index].type == NULL));
164163
*p_interp_count = interp_count;
@@ -235,7 +234,7 @@ managed_static_type_state_init(PyInterpreterState *interp, PyTypeObject *self,
235234
: index + _Py_MAX_MANAGED_STATIC_BUILTIN_TYPES;
236235

237236
assert((initial == 1) ==
238-
(_PyRuntime.types.managed_static.types[full_index].interp_count == 0));
237+
(_Py_atomic_load_int64(&_PyRuntime.types.managed_static.types[full_index].interp_count) == 0));
239238
(void)_Py_atomic_add_int64(
240239
&_PyRuntime.types.managed_static.types[full_index].interp_count, 1);
241240

@@ -284,7 +283,7 @@ managed_static_type_state_clear(PyInterpreterState *interp, PyTypeObject *self,
284283
: &(interp->types.for_extensions.initialized[index]);
285284
assert(state != NULL);
286285

287-
assert(_PyRuntime.types.managed_static.types[full_index].interp_count > 0);
286+
assert(_Py_atomic_load_int64(&_PyRuntime.types.managed_static.types[full_index].interp_count) > 0);
288287
assert(_PyRuntime.types.managed_static.types[full_index].type == state->type);
289288

290289
assert(state->type != NULL);
@@ -294,7 +293,7 @@ managed_static_type_state_clear(PyInterpreterState *interp, PyTypeObject *self,
294293
(void)_Py_atomic_add_int64(
295294
&_PyRuntime.types.managed_static.types[full_index].interp_count, -1);
296295
if (final) {
297-
assert(!_PyRuntime.types.managed_static.types[full_index].interp_count);
296+
assert(!_Py_atomic_load_int64(&_PyRuntime.types.managed_static.types[full_index].interp_count));
298297
_PyRuntime.types.managed_static.types[full_index].type = NULL;
299298

300299
managed_static_type_index_clear(self);
@@ -1258,6 +1257,19 @@ _PyType_GetVersionForCurrentState(PyTypeObject *tp)
12581257
#error "_Py_ATTR_CACHE_UNUSED must be bigger than max"
12591258
#endif
12601259

1260+
static inline unsigned int
1261+
next_global_version_tag(void)
1262+
{
1263+
unsigned int old;
1264+
do {
1265+
old = _Py_atomic_load_uint_relaxed(&_PyRuntime.types.next_version_tag);
1266+
if (old >= _Py_MAX_GLOBAL_TYPE_VERSION_TAG) {
1267+
return 0;
1268+
}
1269+
} while (!_Py_atomic_compare_exchange_uint(&_PyRuntime.types.next_version_tag, &old, old + 1));
1270+
return old + 1;
1271+
}
1272+
12611273
static int
12621274
assign_version_tag(PyInterpreterState *interp, PyTypeObject *type)
12631275
{
@@ -1288,11 +1300,12 @@ assign_version_tag(PyInterpreterState *interp, PyTypeObject *type)
12881300
}
12891301
if (type->tp_flags & Py_TPFLAGS_IMMUTABLETYPE) {
12901302
/* static types */
1291-
if (NEXT_GLOBAL_VERSION_TAG > _Py_MAX_GLOBAL_TYPE_VERSION_TAG) {
1303+
unsigned int next_version_tag = next_global_version_tag();
1304+
if (next_version_tag == 0) {
12921305
/* We have run out of version numbers */
12931306
return 0;
12941307
}
1295-
set_version_unlocked(type, NEXT_GLOBAL_VERSION_TAG++);
1308+
set_version_unlocked(type, next_version_tag);
12961309
assert (type->tp_version_tag <= _Py_MAX_GLOBAL_TYPE_VERSION_TAG);
12971310
}
12981311
else {
@@ -8704,7 +8717,11 @@ type_ready_set_new(PyTypeObject *type, int initial)
87048717
&& base == &PyBaseObject_Type
87058718
&& !(type->tp_flags & Py_TPFLAGS_HEAPTYPE))
87068719
{
8707-
type_add_flags(type, Py_TPFLAGS_DISALLOW_INSTANTIATION);
8720+
if (initial) {
8721+
type_add_flags(type, Py_TPFLAGS_DISALLOW_INSTANTIATION);
8722+
} else {
8723+
assert(type->tp_flags & Py_TPFLAGS_DISALLOW_INSTANTIATION);
8724+
}
87088725
}
87098726

87108727
if (!(type->tp_flags & Py_TPFLAGS_DISALLOW_INSTANTIATION)) {
@@ -8718,13 +8735,17 @@ type_ready_set_new(PyTypeObject *type, int initial)
87188735
}
87198736
}
87208737
else {
8721-
// tp_new is NULL: inherit tp_new from base
8722-
type->tp_new = base->tp_new;
8738+
if (initial) {
8739+
// tp_new is NULL: inherit tp_new from base
8740+
type->tp_new = base->tp_new;
8741+
}
87238742
}
87248743
}
87258744
else {
87268745
// Py_TPFLAGS_DISALLOW_INSTANTIATION sets tp_new to NULL
8727-
type->tp_new = NULL;
8746+
if (initial) {
8747+
type->tp_new = NULL;
8748+
}
87288749
}
87298750
return 0;
87308751
}
@@ -8857,7 +8878,12 @@ type_ready(PyTypeObject *type, int initial)
88578878
}
88588879

88598880
/* All done -- set the ready flag */
8860-
type_add_flags(type, Py_TPFLAGS_READY);
8881+
if (initial) {
8882+
type_add_flags(type, Py_TPFLAGS_READY);
8883+
} else {
8884+
assert(type->tp_flags & Py_TPFLAGS_READY);
8885+
}
8886+
88618887
stop_readying(type);
88628888

88638889
assert(_PyType_CheckConsistency(type));
@@ -8906,15 +8932,16 @@ init_static_type(PyInterpreterState *interp, PyTypeObject *self,
89068932
assert(!(self->tp_flags & Py_TPFLAGS_MANAGED_DICT));
89078933
assert(!(self->tp_flags & Py_TPFLAGS_MANAGED_WEAKREF));
89088934

8909-
if ((self->tp_flags & Py_TPFLAGS_READY) == 0) {
8910-
assert(initial);
8935+
if (initial) {
8936+
assert((self->tp_flags & Py_TPFLAGS_READY) == 0);
89118937

89128938
type_add_flags(self, _Py_TPFLAGS_STATIC_BUILTIN);
89138939
type_add_flags(self, Py_TPFLAGS_IMMUTABLETYPE);
89148940

8915-
assert(NEXT_GLOBAL_VERSION_TAG <= _Py_MAX_GLOBAL_TYPE_VERSION_TAG);
89168941
if (self->tp_version_tag == 0) {
8917-
_PyType_SetVersion(self, NEXT_GLOBAL_VERSION_TAG++);
8942+
unsigned int next_version_tag = next_global_version_tag();
8943+
assert(next_version_tag != 0);
8944+
_PyType_SetVersion(self, next_version_tag);
89188945
}
89198946
}
89208947
else {

0 commit comments

Comments
 (0)