Skip to content

Commit 9e5726a

Browse files
[3.15] gh-160496: Clear inline values with atomic release stores (GH-160510) (#160710)
gh-160496: Clear inline values with atomic release stores (GH-160510) (cherry picked from commit a9049ee) Co-authored-by: mpage <mpage@meta.com>
1 parent 4aabbdf commit 9e5726a

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
@@ -7731,7 +7731,11 @@ clear_inline_values(PyDictValues *values)
77317731
if (values->valid) {
77327732
FT_ATOMIC_STORE_UINT8(values->valid, 0);
77337733
for (Py_ssize_t i = 0; i < values->capacity; i++) {
7734-
Py_CLEAR(values->values[i]);
7734+
PyObject *value = values->values[i];
7735+
// The attribute read fast-path may read these without holding the
7736+
// per-object lock.
7737+
FT_ATOMIC_STORE_PTR_RELEASE(values->values[i], NULL);
7738+
Py_XDECREF(value);
77357739
}
77367740
}
77377741
}

0 commit comments

Comments
 (0)