gh-160496: Clear inline values with atomic release stores (#160510)

This commit is contained in:
mpage authored and GitHub committed 2026-10-26 19:46:46 +05:30
1 parent c436e8d835
commit a9049ee4fb
2 files changed
+38 -1

No files matched your search

+33
View File
@@ -228,6 +228,39 @@ class TestDict(TestCase):
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()
+5 -1
View File
@@ -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);
}
}
}