Skip to content

Commit a9049ee

Browse files
authored
gh-160496: Clear inline values with atomic release stores (#160510)
1 parent c436e8d commit a9049ee

2 files changed

Lines changed: 38 additions & 1 deletion

File tree

‎Lib/test/test_free_threading/test_dict.py‎

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,39 @@ def reader_func():
228228

229229
self.assertEqual(count, 0)
230230

231+
def test_racing_replace_inline_values_and_lookup(self):
232+
# gh-160496: replacing an unmaterialized __dict__ clears inline
233+
# values while attribute lookup can still read them without a lock.
234+
class C:
235+
pass
236+
237+
objects = [C() for _ in range(1000)]
238+
for obj in objects:
239+
obj.x = [0]
240+
241+
# Inline values are invalidated only once per object, so synchronize
242+
# the threads for each fresh instance without accessing its __dict__.
243+
barrier = Barrier(2)
244+
observed = []
245+
246+
def writer():
247+
for obj in objects:
248+
barrier.wait()
249+
obj.__dict__ = {"x": [1]}
250+
barrier.wait()
251+
252+
def reader():
253+
for obj in objects:
254+
barrier.wait()
255+
observed.append(getattr(obj, "x", None))
256+
barrier.wait()
257+
258+
threading_helper.run_concurrently([writer, reader])
259+
for value in observed:
260+
self.assertIn(value, ([0], [1]))
261+
for obj in objects:
262+
self.assertEqual(obj.x, [1])
263+
231264
def test_racing_object_get_set_dict(self):
232265
e = Exception()
233266

‎Objects/dictobject.c‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7804,7 +7804,11 @@ clear_inline_values(PyDictValues *values)
78047804
if (values->valid) {
78057805
FT_ATOMIC_STORE_UINT8(values->valid, 0);
78067806
for (Py_ssize_t i = 0; i < values->capacity; i++) {
7807-
Py_CLEAR(values->values[i]);
7807+
PyObject *value = values->values[i];
7808+
// The attribute read fast-path may read these without holding the
7809+
// per-object lock.
7810+
FT_ATOMIC_STORE_PTR_RELEASE(values->values[i], NULL);
7811+
Py_XDECREF(value);
78087812
}
78097813
}
78107814
}

0 commit comments

Comments
 (0)