From 5e4d174a460ad3d9a1713a5cc7901f6bbbbbcd3d Mon Sep 17 00:00:00 2001 From: Neil Schemenauer Date: Tue, 8 Jul 2025 19:50:41 -0700 Subject: [PATCH 1/5] GH-116738: document thread-safety of bisect --- Doc/library/bisect.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Doc/library/bisect.rst b/Doc/library/bisect.rst index 78da563397b625..72e6d34fa82dc3 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: From 4b7f113043babbd1fc5417986888a57823523750 Mon Sep 17 00:00:00 2001 From: Neil Schemenauer Date: Fri, 11 Jul 2025 11:04:25 -0700 Subject: [PATCH 2/5] Add free-threaded test for bisect. --- Lib/test/test_free_threading/test_bisect.py | 79 +++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 Lib/test/test_free_threading/test_bisect.py 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..d1a8eab6fb10a9 --- /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 +from threading import Thread, Barrier +import random + +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() From e405cf82ab16fccc2651ceb203b598d0b76904fd Mon Sep 17 00:00:00 2001 From: Neil Schemenauer Date: Mon, 14 Jul 2025 11:41:50 -0700 Subject: [PATCH 3/5] Adjust indent of "note" block. --- Doc/library/bisect.rst | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Doc/library/bisect.rst b/Doc/library/bisect.rst index 72e6d34fa82dc3..d02ffe469adb1a 100644 --- a/Doc/library/bisect.rst +++ b/Doc/library/bisect.rst @@ -24,15 +24,15 @@ 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. +.. 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: From 87a90f3c26d4be3ec4e22ef5884b911b3a015de1 Mon Sep 17 00:00:00 2001 From: Neil Schemenauer Date: Wed, 23 Jul 2025 09:13:24 -0700 Subject: [PATCH 4/5] Use `threading_helper.run_concurrently`. --- Lib/test/test_free_threading/test_bisect.py | 24 +-------------------- 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/Lib/test/test_free_threading/test_bisect.py b/Lib/test/test_free_threading/test_bisect.py index d1a8eab6fb10a9..99c9d46bd85cfc 100644 --- a/Lib/test/test_free_threading/test_bisect.py +++ b/Lib/test/test_free_threading/test_bisect.py @@ -19,7 +19,7 @@ def insert(data): insert_method(data, x) data = list(range(OBJECT_COUNT)) - self.run_concurrently( + threading_helper.run_concurrently( worker_func=insert, args=(data,), nthreads=NTHREADS ) if False: @@ -42,28 +42,6 @@ def is_sorted_ascending(lst): """ 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): From b74a7903e745522de41e9242b7f824e5b7d60b35 Mon Sep 17 00:00:00 2001 From: Neil Schemenauer Date: Tue, 29 Jul 2025 19:15:29 -0700 Subject: [PATCH 5/5] Remove unused imports from test. --- Lib/test/test_free_threading/test_bisect.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Lib/test/test_free_threading/test_bisect.py b/Lib/test/test_free_threading/test_bisect.py index 99c9d46bd85cfc..bd7732da035257 100644 --- a/Lib/test/test_free_threading/test_bisect.py +++ b/Lib/test/test_free_threading/test_bisect.py @@ -1,6 +1,5 @@ import unittest from test.support import import_helper, threading_helper -from threading import Thread, Barrier import random py_bisect = import_helper.import_fresh_module('bisect', blocked=['_bisect']) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy