https://github.com/python/cpython/commit/e99bc7fd44bbbf2464c37d5a57777ac0e1264c37
commit: e99bc7fd44bbbf2464c37d5a57777ac0e1264c37
branch: main
author: Kumar Aditya <[email protected]>
committer: kumaraditya303 <[email protected]>
date: 2025-08-01T13:40:40Z
summary:

gh-133467: fix data race in `type_set_name` (#137302)

Fix data race in `type_set_name` by assigning name under stop the world pause 
making it thread safe in free-threading.

files:
M Lib/test/test_free_threading/test_type.py
M Objects/typeobject.c
M Tools/tsan/suppressions_free_threading.txt

diff --git a/Lib/test/test_free_threading/test_type.py 
b/Lib/test/test_free_threading/test_type.py
index ae996e7db3c7fd..2d995751005d71 100644
--- a/Lib/test/test_free_threading/test_type.py
+++ b/Lib/test/test_free_threading/test_type.py
@@ -127,6 +127,20 @@ class ClassB(Base):
         obj.__class__ = ClassB
 
 
+    def test_name_change(self):
+        class Foo:
+            pass
+
+        def writer():
+            for _ in range(1000):
+                Foo.__name__ = 'Bar'
+
+        def reader():
+            for _ in range(1000):
+                Foo.__name__
+
+        self.run_one(writer, reader)
+
     def run_one(self, writer_func, reader_func):
         barrier = threading.Barrier(NTHREADS)
 
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index d952a58d94af55..6e67b6e01cb8b8 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -1535,9 +1535,13 @@ type_set_name(PyObject *tp, PyObject *value, void 
*Py_UNUSED(closure))
         return -1;
     }
 
+    PyInterpreterState *interp = _PyInterpreterState_GET();
+    _PyEval_StopTheWorld(interp);
     type->tp_name = tp_name;
-    Py_SETREF(((PyHeapTypeObject*)type)->ht_name, Py_NewRef(value));
-
+    PyObject *old_name = ((PyHeapTypeObject*)type)->ht_name;
+    ((PyHeapTypeObject*)type)->ht_name = Py_NewRef(value);
+    _PyEval_StartTheWorld(interp);
+    Py_DECREF(old_name);
     return 0;
 }
 
@@ -10706,9 +10710,11 @@ slot_tp_descr_get(PyObject *self, PyObject *obj, 
PyObject *type)
 
     get = _PyType_LookupRef(tp, &_Py_ID(__get__));
     if (get == NULL) {
+#ifndef Py_GIL_DISABLED
         /* Avoid further slowdowns */
         if (tp->tp_descr_get == slot_tp_descr_get)
             tp->tp_descr_get = NULL;
+#endif
         return Py_NewRef(self);
     }
     if (obj == NULL)
diff --git a/Tools/tsan/suppressions_free_threading.txt 
b/Tools/tsan/suppressions_free_threading.txt
index 93421b623b92f9..52d7c25a5bb37a 100644
--- a/Tools/tsan/suppressions_free_threading.txt
+++ b/Tools/tsan/suppressions_free_threading.txt
@@ -44,7 +44,5 @@ race:PyObject_Realloc
 
 # gh-133467.  Some of these could be hard to trigger.
 race_top:_Py_slot_tp_getattr_hook
-race_top:slot_tp_descr_get
-race_top:type_set_name
 race_top:set_tp_bases
 race_top:type_set_bases_unlocked

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to