gh-151627: fix thread safety of OrderedDict iterator creation (#151688)

kumaraditya303 <[email protected]> Sat, 01 Aug 2026 05:57:28 -0400 (EDT)
Newsgroups gmane.comp.python.cvs
Message-ID <[email protected]>
https://github.com/python/cpython/commit/86dab7c55bb85269b764886ceda1f85e3fdc7a7d
commit: 86dab7c55bb85269b764886ceda1f85e3fdc7a7d
branch: main
author: Pedram Karimi <[email protected]>
committer: kumaraditya303 <[email protected]>
date: 2026-08-01T15:27:14+05:30
summary:

gh-151627: fix thread safety of OrderedDict iterator creation (#151688)

files:
A Misc/NEWS.d/next/Library/2026-06-19-08-30-00.gh-issue-151627.xY7kLm.rst
M Lib/test/test_free_threading/test_collections.py
M Objects/odictobject.c

diff --git a/Lib/test/test_free_threading/test_collections.py b/Lib/test/test_free_threading/test_collections.py
index 849b0480e232fc2..a0a73c965d072a2 100644
--- a/Lib/test/test_free_threading/test_collections.py
+++ b/Lib/test/test_free_threading/test_collections.py
@@ -1,5 +1,5 @@
 import unittest
-from collections import deque
+from collections import OrderedDict, deque
 from copy import copy
 from test.support import threading_helper
 
@@ -49,5 +49,30 @@ def mutate():
         )
 
 
+class TestOrderedDict(unittest.TestCase):
+    def test_iterator_update_clear_race(self):
+        # gh-151627: OrderedDict iterator construction must not race with
+        # concurrent clear()/update() operations that mutate the linked list.
+        od = OrderedDict((i, i) for i in range(100))
+
+        def mutate():
+            for i in range(5000):
+                od.clear()
+                od.update(((i, i), (i + 1, i + 1), (i + 2, i + 2)))
+
+        def iterate():
+            for _ in range(5000):
+                try:
+                    for _ in od:
+                        pass
+                    list(reversed(od))
+                except RuntimeError:
+                    pass
+
+        threading_helper.run_concurrently(
+            [mutate, *[iterate for _ in range(8)]],
+        )
+
+
 if __name__ == "__main__":
     unittest.main()
diff --git a/Misc/NEWS.d/next/Library/2026-06-19-08-30-00.gh-issue-151627.xY7kLm.rst b/Misc/NEWS.d/next/Library/2026-06-19-08-30-00.gh-issue-151627.xY7kLm.rst
new file mode 100644
index 000000000000000..2c5d39eec12347b
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-06-19-08-30-00.gh-issue-151627.xY7kLm.rst
@@ -0,0 +1,2 @@
+Fix a crash in :class:`collections.OrderedDict` iterators in free-threaded
+builds when the dictionary is concurrently cleared or updated.
diff --git a/Objects/odictobject.c b/Objects/odictobject.c
index c51fd4b7195da42..a860ade3b148e5f 100644
--- a/Objects/odictobject.c
+++ b/Objects/odictobject.c
@@ -1958,11 +1958,13 @@ odictiter_new(PyODictObject *od, int kind)
     }
 
     di->kind = kind;
+    Py_BEGIN_CRITICAL_SECTION(od);
     node = reversed ? _odict_LAST(od) : _odict_FIRST(od);
     di->di_current = node ? Py_NewRef(_odictnode_KEY(node)) : NULL;
     di->di_size = PyODict_SIZE(od);
     di->di_state = od->od_state;
     di->di_odict = (PyODictObject*)Py_NewRef(od);
+    Py_END_CRITICAL_SECTION();
 
     _PyObject_GC_TRACK(di);
     return (PyObject *)di;

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]