https://github.com/python/cpython/commit/a9049ee4fb62138e4aa03c06f2e705a03677f92e
commit: a9049ee4fb62138e4aa03c06f2e705a03677f92e
branch: main
author: mpage <[email protected]>
committer: kumaraditya303 <[email protected]>
date: 2026-10-26T19:46:46+05:30
summary:
gh-160496: Clear inline values with atomic release stores (#160510)
files:
M Lib/test/test_free_threading/test_dict.py
M Objects/dictobject.c
diff --git a/Lib/test/test_free_threading/test_dict.py
b/Lib/test/test_free_threading/test_dict.py
index dde649e84664eef..faf0fa96f07a152 100644
--- a/Lib/test/test_free_threading/test_dict.py
+++ b/Lib/test/test_free_threading/test_dict.py
@@ -228,6 +228,39 @@ def reader_func():
self.assertEqual(count, 0)
+ def test_racing_replace_inline_values_and_lookup(self):
+ # gh-160496: replacing an unmaterialized __dict__ clears inline
+ # values while attribute lookup can still read them without a lock.
+ class C:
+ pass
+
+ objects = [C() for _ in range(1000)]
+ for obj in objects:
+ obj.x = [0]
+
+ # Inline values are invalidated only once per object, so synchronize
+ # the threads for each fresh instance without accessing its __dict__.
+ barrier = Barrier(2)
+ observed = []
+
+ def writer():
+ for obj in objects:
+ barrier.wait()
+ obj.__dict__ = {"x": [1]}
+ barrier.wait()
+
+ def reader():
+ for obj in objects:
+ barrier.wait()
+ observed.append(getattr(obj, "x", None))
+ barrier.wait()
+
+ threading_helper.run_concurrently([writer, reader])
+ for value in observed:
+ self.assertIn(value, ([0], [1]))
+ for obj in objects:
+ self.assertEqual(obj.x, [1])
+
def test_racing_object_get_set_dict(self):
e = Exception()
diff --git a/Objects/dictobject.c b/Objects/dictobject.c
index 929ea565f3b40df..1c3b98032b8af15 100644
--- a/Objects/dictobject.c
+++ b/Objects/dictobject.c
@@ -7804,7 +7804,11 @@ clear_inline_values(PyDictValues *values)
if (values->valid) {
FT_ATOMIC_STORE_UINT8(values->valid, 0);
for (Py_ssize_t i = 0; i < values->capacity; i++) {
- Py_CLEAR(values->values[i]);
+ PyObject *value = values->values[i];
+ // The attribute read fast-path may read these without holding the
+ // per-object lock.
+ FT_ATOMIC_STORE_PTR_RELEASE(values->values[i], NULL);
+ Py_XDECREF(value);
}
}
}
_______________________________________________
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]