mirror of
https://github.com/python/cpython.git
synced 2026-10-28 10:25:05 +08:00
gh-160496: Clear inline values with atomic release stores (#160510)
This commit is contained in:
1 parent
c436e8d835
commit
a9049ee4fb
2 files changed
+38
-1
No files matched your search
@@ -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()
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user