Skip to content

Commit 8976129

Browse files
committed
gh-153881: Atomically load dk_nentries in _PyObject_IsInstanceDictEmpty
1 parent 9d231cb commit 8976129

3 files changed

Lines changed: 22 additions & 1 deletion

File tree

Lib/test/test_free_threading/test_dict.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,5 +336,25 @@ def reader():
336336
with threading_helper.start_threads([t1, t2]):
337337
pass
338338

339+
def test_getstate_race_with_shared_keys(self):
340+
box = [None]
341+
enter = Barrier(2)
342+
leave = Barrier(2)
343+
344+
def reader():
345+
for _ in range(1000):
346+
enter.wait()
347+
box[0].__getstate__()
348+
leave.wait()
349+
350+
def writer():
351+
for i in range(1000):
352+
box[0] = type(f"C{i}", (), {})()
353+
enter.wait()
354+
setattr(box[0], str(i), 1)
355+
leave.wait()
356+
357+
threading_helper.run_concurrently([reader, writer])
358+
339359
if __name__ == "__main__":
340360
unittest.main()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix potential data race when calling :meth:`object.__getstate__`.

Objects/dictobject.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7722,7 +7722,7 @@ _PyObject_IsInstanceDictEmpty(PyObject *obj)
77227722
PyDictValues *values = _PyObject_InlineValues(obj);
77237723
if (FT_ATOMIC_LOAD_UINT8(values->valid)) {
77247724
PyDictKeysObject *keys = CACHED_KEYS(tp);
7725-
for (Py_ssize_t i = 0; i < keys->dk_nentries; i++) {
7725+
for (Py_ssize_t i = 0; i < LOAD_KEYS_NENTRIES(keys); i++) {
77267726
if (FT_ATOMIC_LOAD_PTR_RELAXED(values->values[i]) != NULL) {
77277727
return 0;
77287728
}

0 commit comments

Comments
 (0)