From 5ea60dd25a06fede63c6162c78d74aa9215d8196 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Wed, 14 May 2025 12:12:12 +0530 Subject: [PATCH 1/3] use atomic store in PyObject_GenericSetDict --- Objects/object.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Objects/object.c b/Objects/object.c index 723b0427e69251..e8bc40b3cf3ee5 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -1931,7 +1931,9 @@ PyObject_GenericSetDict(PyObject *obj, PyObject *value, void *context) return -1; } Py_BEGIN_CRITICAL_SECTION(obj); - Py_XSETREF(*dictptr, Py_NewRef(value)); + PyObject *olddict = *dictptr; + FT_ATOMIC_STORE_PTR_RELEASE(*dictptr, Py_NewRef(value)); + Py_XDECREF(olddict); Py_END_CRITICAL_SECTION(); return 0; } From 60c8434daa2aa794f1bf52dc1f87c1e6c69aa1e7 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Wed, 14 May 2025 17:00:26 +0530 Subject: [PATCH 2/3] add test --- Lib/test/test_free_threading/test_dict.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Lib/test/test_free_threading/test_dict.py b/Lib/test/test_free_threading/test_dict.py index 476cc3178d843f..5d5d4e226caa40 100644 --- a/Lib/test/test_free_threading/test_dict.py +++ b/Lib/test/test_free_threading/test_dict.py @@ -228,6 +228,22 @@ def reader_func(): self.assertEqual(count, 0) + def test_racing_object_get_set_dict(self): + e = Exception() + + def writer(): + for i in range(10000): + e.__dict__ = {1:2} + + def reader(): + for i in range(10000): + e.__dict__ + + t1 = Thread(target=writer) + t2 = Thread(target=reader) + + with threading_helper.start_threads([t1, t2]): + pass if __name__ == "__main__": unittest.main() From 665bad24790e09628da0041bff50e0f31f48e9dc Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Thu, 15 May 2025 14:16:07 +0530 Subject: [PATCH 3/3] use _PyObject_XDecRefDelayed --- Objects/object.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Objects/object.c b/Objects/object.c index e8bc40b3cf3ee5..af1aa217f75462 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -1933,7 +1933,11 @@ PyObject_GenericSetDict(PyObject *obj, PyObject *value, void *context) Py_BEGIN_CRITICAL_SECTION(obj); PyObject *olddict = *dictptr; FT_ATOMIC_STORE_PTR_RELEASE(*dictptr, Py_NewRef(value)); +#ifdef Py_GIL_DISABLED + _PyObject_XDecRefDelayed(olddict); +#else Py_XDECREF(olddict); +#endif Py_END_CRITICAL_SECTION(); return 0; }