]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-123471: make concurrent iteration over itertools.accumulate thread-safe (#144486)
authorPieter Eendebak <pieter.eendebak@gmail.com>
Mon, 16 Mar 2026 08:53:37 +0000 (09:53 +0100)
committerGitHub <noreply@github.com>
Mon, 16 Mar 2026 08:53:37 +0000 (08:53 +0000)
Lib/test/test_free_threading/test_itertools.py
Misc/NEWS.d/next/Library/2026-02-04-20-30-59.gh-issue-123471.1dnPvs.rst [new file with mode: 0644]
Modules/itertoolsmodule.c

index bb6047e8669475fe4715c373dff70ad10cd24669..20135dd3165acf8edeba78d974b3db699bee6faf 100644 (file)
@@ -1,5 +1,5 @@
 import unittest
-from itertools import batched, chain, combinations_with_replacement, cycle, permutations
+from itertools import accumulate, batched, chain, combinations_with_replacement, cycle, permutations
 from test.support import threading_helper
 
 
@@ -16,6 +16,13 @@ def work_iterator(it):
 
 class ItertoolsThreading(unittest.TestCase):
 
+    @threading_helper.reap_threads
+    def test_accumulate(self):
+        number_of_iterations = 10
+        for _ in range(number_of_iterations):
+            it = accumulate(tuple(range(40)))
+            threading_helper.run_concurrently(work_iterator, nthreads=10, args=[it])
+
     @threading_helper.reap_threads
     def test_batched(self):
         number_of_iterations = 10
diff --git a/Misc/NEWS.d/next/Library/2026-02-04-20-30-59.gh-issue-123471.1dnPvs.rst b/Misc/NEWS.d/next/Library/2026-02-04-20-30-59.gh-issue-123471.1dnPvs.rst
new file mode 100644 (file)
index 0000000..d650103
--- /dev/null
@@ -0,0 +1 @@
+Make concurrent iteration over :class:`itertools.accumulate` safe under free-threading.
index bc25bf6bfc1bd2b4f2f3619affb67f97bd7df9af..b37256c7928badd8ddedd02b2dd295f2f22fc1ef 100644 (file)
@@ -3073,7 +3073,7 @@ accumulate_traverse(PyObject *op, visitproc visit, void *arg)
 }
 
 static PyObject *
-accumulate_next(PyObject *op)
+accumulate_next_lock_held(PyObject *op)
 {
     accumulateobject *lz = accumulateobject_CAST(op);
     PyObject *val, *newtotal;
@@ -3105,6 +3105,16 @@ accumulate_next(PyObject *op)
     return newtotal;
 }
 
+static PyObject *
+accumulate_next(PyObject *op)
+{
+    PyObject *result;
+    Py_BEGIN_CRITICAL_SECTION(op);
+    result = accumulate_next_lock_held(op);
+    Py_END_CRITICAL_SECTION()
+    return result;
+}
+
 static PyType_Slot accumulate_slots[] = {
     {Py_tp_dealloc, accumulate_dealloc},
     {Py_tp_getattro, PyObject_GenericGetAttr},