https://github.com/python/cpython/commit/d3b5f838ec44d0297a0990e11838f39395aa7d60
commit: d3b5f838ec44d0297a0990e11838f39395aa7d60
branch: 3.13
author: Miss Islington (bot) <[email protected]>
committer: kumaraditya303 <[email protected]>
date: 2025-07-30T11:50:39Z
summary:

[3.13] GH-116738: document thread-safety of bisect (GH-136555) (#137222)

* GH-116738: document thread-safety of bisect (GH-136555)
(cherry picked from commit 5236b0281b91a874b14cf15f3fdef9b7beffb22f)

Co-authored-by: Neil Schemenauer <[email protected]>
Co-authored-by: Kumar Aditya <[email protected]>

files:
A Lib/test/test_free_threading/test_bisect.py
M Doc/library/bisect.rst

diff --git a/Doc/library/bisect.rst b/Doc/library/bisect.rst
index 78da563397b625..d02ffe469adb1a 100644
--- a/Doc/library/bisect.rst
+++ b/Doc/library/bisect.rst
@@ -24,6 +24,16 @@ method to determine whether a value has been found.  
Instead, the
 functions only call the :meth:`~object.__lt__` method and will return an 
insertion
 point between values in an array.
 
+.. note::
+
+   The functions in this module are not thread-safe. If multiple threads
+   concurrently use :mod:`bisect` functions on the same sequence, this
+   may result in undefined behaviour. Likewise, if the provided sequence
+   is mutated by a different thread while a :mod:`bisect` function
+   is operating on it, the result is undefined. For example, using
+   :py:func:`~bisect.insort_left` on the same list from multiple threads
+   may result in the list becoming unsorted.
+
 .. _bisect functions:
 
 The following functions are provided:
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..b8645d1c8c227a
--- /dev/null
+++ b/Lib/test/test_free_threading/test_bisect.py
@@ -0,0 +1,79 @@
+import unittest
+from test.support import import_helper, threading_helper
+import random
+from threading import Thread, Barrier
+
+py_bisect = import_helper.import_fresh_module('bisect', blocked=['_bisect'])
+c_bisect = import_helper.import_fresh_module('bisect', fresh=['_bisect'])
+
+
+NTHREADS = 4
+OBJECT_COUNT = 500
+
+
+class TestBase:
+    def do_racing_insort(self, insert_method):
+        def insert(data):
+            for _ in range(OBJECT_COUNT):
+                x = random.randint(-OBJECT_COUNT, OBJECT_COUNT)
+                insert_method(data, x)
+
+        data = list(range(OBJECT_COUNT))
+        self.run_concurrently(
+            worker_func=insert, args=(data,), nthreads=NTHREADS
+        )
+        if False:
+            # These functions are not thread-safe and so the list can become
+            # unsorted.  However, we don't want Python to crash if these
+            # functions are used concurrently on the same sequence.  This
+            # should also not produce any TSAN warnings.
+            self.assertTrue(self.is_sorted_ascending(data))
+
+    def test_racing_insert_right(self):
+        self.do_racing_insort(self.mod.insort_right)
+
+    def test_racing_insert_left(self):
+        self.do_racing_insort(self.mod.insort_left)
+
+    @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)))
+
+    def run_concurrently(self, worker_func, args, nthreads):
+        """
+        Run the worker function concurrently in multiple threads.
+        """
+        barrier = Barrier(nthreads)
+
+        def wrapper_func(*args):
+            # Wait for all threads to reach this point before proceeding.
+            barrier.wait()
+            worker_func(*args)
+
+        with threading_helper.catch_threading_exception() as cm:
+            workers = (
+                Thread(target=wrapper_func, args=args)
+                for _ in range(nthreads)
+            )
+            with threading_helper.start_threads(workers):
+                pass
+
+            # Worker threads should not raise any exceptions
+            self.assertIsNone(cm.exc_value)
+
+
+@threading_helper.requires_working_threading()
+class TestPyBisect(unittest.TestCase, TestBase):
+    mod = py_bisect
+
+
+@threading_helper.requires_working_threading()
+class TestCBisect(unittest.TestCase, TestBase):
+    mod = c_bisect
+
+
+if __name__ == "__main__":
+    unittest.main()

_______________________________________________
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]

Reply via email to