diff --git a/Lib/test/test_free_threading/test_itertools.py b/Lib/test/test_free_threading/test_itertools.py index 670d4ca8835e0d..f8553906e19f42 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 accumulate, batched, chain, combinations_with_replacement, cycle, permutations, zip_longest +from itertools import accumulate, batched, chain, combinations_with_replacement, cycle, islice, permutations, zip_longest from test.support import threading_helper @@ -55,6 +55,13 @@ def test_combinations_with_replacement(self): it = combinations_with_replacement(tuple(range(2)), 2) threading_helper.run_concurrently(work_iterator, nthreads=6, args=[it]) + @threading_helper.reap_threads + def test_islice(self): + number_of_iterations = 6 + for _ in range(number_of_iterations): + it = islice(tuple(range(10)), 1, 8, 2) + threading_helper.run_concurrently(work_iterator, nthreads=10, args=[it]) + @threading_helper.reap_threads def test_permutations(self): number_of_iterations = 6 diff --git a/Misc/NEWS.d/next/Library/2026-04-10-00-00-00.gh-issue-123471.islice_ft.rst b/Misc/NEWS.d/next/Library/2026-04-10-00-00-00.gh-issue-123471.islice_ft.rst new file mode 100644 index 00000000000000..5061c89259a4f3 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-04-10-00-00-00.gh-issue-123471.islice_ft.rst @@ -0,0 +1,2 @@ +Make concurrent iteration over :class:`itertools.islice` safe under +free-threading. diff --git a/Modules/itertoolsmodule.c b/Modules/itertoolsmodule.c index a6bfa78a461bb0..3e54d795458a47 100644 --- a/Modules/itertoolsmodule.c +++ b/Modules/itertoolsmodule.c @@ -1624,7 +1624,7 @@ islice_traverse(PyObject *op, visitproc visit, void *arg) } static PyObject * -islice_next(PyObject *op) +islice_next_lock_held(PyObject *op) { isliceobject *lz = isliceobject_CAST(op); PyObject *item; @@ -1663,6 +1663,16 @@ islice_next(PyObject *op) return NULL; } +static PyObject * +islice_next(PyObject *op) +{ + PyObject *result; + Py_BEGIN_CRITICAL_SECTION(op); + result = islice_next_lock_held(op); + Py_END_CRITICAL_SECTION(); + return result; +} + PyDoc_STRVAR(islice_doc, "islice(iterable, stop) --> islice object\n\ islice(iterable, start, stop[, step]) --> islice object\n\