Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions Lib/test/test_free_threading/test_dict.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down
6 changes: 5 additions & 1 deletion Objects/dictobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -7802,7 +7802,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);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it worth a comment about what the conflicting reads are?

Py_XDECREF(value);
}
}
}
Expand Down
Loading