diff --git a/Lib/test/test_free_threading/test_itertools.py b/Lib/test/test_free_threading/test_itertools.py index bb6047e8669475..20135dd3165acf 100644 --- a/Lib/test/test_free_threading/test_itertools.py +++ b/Lib/test/test_free_threading/test_itertools.py @@ -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 index 00000000000000..d650103e28ee68 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-02-04-20-30-59.gh-issue-123471.1dnPvs.rst @@ -0,0 +1 @@ +Make concurrent iteration over :class:`itertools.accumulate` safe under free-threading. diff --git a/Modules/itertoolsmodule.c b/Modules/itertoolsmodule.c index 7e73f76bc20b58..dbb19c731543d9 100644 --- a/Modules/itertoolsmodule.c +++ b/Modules/itertoolsmodule.c @@ -3063,7 +3063,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; @@ -3095,6 +3095,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},