diff --git a/Lib/test/test_free_threading/test_bisect.py b/Lib/test/test_free_threading/test_bisect.py new file mode 100644 index 00000000000000..de6ea89d885f9b --- /dev/null +++ b/Lib/test/test_free_threading/test_bisect.py @@ -0,0 +1,43 @@ +import unittest + +from itertools import cycle + +from test.support import import_helper, threading_helper, subTests +from test.support.threading_helper import run_concurrently + +bisect = import_helper.import_module("bisect") + + +NTHREADS = 10 + + +@threading_helper.requires_working_threading() +class TestBisect(unittest.TestCase): + @subTests("insort_func", [bisect.insort_left, bisect.insort_right]) + def test_racing_insort(self, insort_func): + lst = list(range(10)) + insort_items_iter = cycle(list(range(11))) + + def worker(lst, insort_items_iter): + for _ in range(10): + insort_item = next(insort_items_iter) + insort_func(lst, insort_item) + + run_concurrently( + worker_func=worker, + nthreads=NTHREADS, + args=(lst, insort_items_iter), + ) + + self.assertTrue(self.is_sorted_ascending(lst)) + + @staticmethod + def is_sorted_ascending(lst): + """ + Check if the list is sorted in ascending order (non-decreasing). + """ + return all(lst[i - 1] <= lst[i] for i in range(1, len(lst))) + + +if __name__ == "__main__": + unittest.main() diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-07-22-11-09-16.gh-issue-116738.uAgWNZ.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-07-22-11-09-16.gh-issue-116738.uAgWNZ.rst new file mode 100644 index 00000000000000..654545cf9da202 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-07-22-11-09-16.gh-issue-116738.uAgWNZ.rst @@ -0,0 +1,2 @@ +Make functions in :mod:`bisect` thread-safe on the :term:`free threaded +` build. diff --git a/Modules/_bisectmodule.c b/Modules/_bisectmodule.c index 9b146265445d9a..90192c2508e8c5 100644 --- a/Modules/_bisectmodule.c +++ b/Modules/_bisectmodule.c @@ -179,6 +179,7 @@ _bisect_bisect_right_impl(PyObject *module, PyObject *a, PyObject *x, } /*[clinic input] +@critical_section a _bisect.insort_right a: object @@ -201,7 +202,7 @@ A custom key function can be supplied to customize the sort order. static PyObject * _bisect_insort_right_impl(PyObject *module, PyObject *a, PyObject *x, Py_ssize_t lo, Py_ssize_t hi, PyObject *key) -/*[clinic end generated code: output=ac3bf26d07aedda2 input=f60777d2b6ddb239]*/ +/*[clinic end generated code: output=ac3bf26d07aedda2 input=93bd855be0c1add7]*/ { PyObject *result, *key_x; Py_ssize_t index; @@ -365,6 +366,7 @@ _bisect_bisect_left_impl(PyObject *module, PyObject *a, PyObject *x, /*[clinic input] +@critical_section a _bisect.insort_left a: object @@ -387,7 +389,7 @@ A custom key function can be supplied to customize the sort order. static PyObject * _bisect_insort_left_impl(PyObject *module, PyObject *a, PyObject *x, Py_ssize_t lo, Py_ssize_t hi, PyObject *key) -/*[clinic end generated code: output=b1d33e5e7ffff11e input=0a700a82edbd472c]*/ +/*[clinic end generated code: output=b1d33e5e7ffff11e input=93e06f5a323e09ef]*/ { PyObject *result, *key_x; Py_ssize_t index; diff --git a/Modules/clinic/_bisectmodule.c.h b/Modules/clinic/_bisectmodule.c.h index 314208bc41d0c1..bc9d7d020d9423 100644 --- a/Modules/clinic/_bisectmodule.c.h +++ b/Modules/clinic/_bisectmodule.c.h @@ -7,6 +7,7 @@ preserve # include "pycore_runtime.h" // _Py_ID() #endif #include "pycore_abstract.h" // _PyNumber_Index() +#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION() #include "pycore_modsupport.h" // _PyArg_UnpackKeywords() PyDoc_STRVAR(_bisect_bisect_right__doc__, @@ -222,7 +223,9 @@ _bisect_insort_right(PyObject *module, PyObject *const *args, Py_ssize_t nargs, } key = args[4]; skip_optional_kwonly: + Py_BEGIN_CRITICAL_SECTION(a); return_value = _bisect_insort_right_impl(module, a, x, lo, hi, key); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -441,9 +444,11 @@ _bisect_insort_left(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P } key = args[4]; skip_optional_kwonly: + Py_BEGIN_CRITICAL_SECTION(a); return_value = _bisect_insort_left_impl(module, a, x, lo, hi, key); + Py_END_CRITICAL_SECTION(); exit: return return_value; } -/*[clinic end generated code: output=729385c6a23828ab input=a9049054013a1b77]*/ +/*[clinic end generated code: output=6111121f07c8300b input=a9049054013a1b77]*/