gh-129107: make bytearray iterator thread safe (#130096)

Co-authored-by: Kumar Aditya <kumaraditya@python.org>
This commit is contained in:
Tomasz Pytel 2025-02-19 05:12:45 -05:00 committed by GitHub
parent 388e1ca9f0
commit 1b6bef8086
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 81 additions and 26 deletions

View file

@ -2455,9 +2455,6 @@ class FreeThreadingTest(unittest.TestCase):
with threading_helper.start_threads(threads):
pass
for thread in threads:
threading_helper.join_thread(thread)
# hard errors
check([clear] + [reduce] * 10)
@ -2519,6 +2516,44 @@ class FreeThreadingTest(unittest.TestCase):
check([clear] + [upper] * 10, bytearray(b'a' * 0x400000))
check([clear] + [zfill] * 10, bytearray(b'1' * 0x200000))
@unittest.skipUnless(support.Py_GIL_DISABLED, 'this test can only possibly fail with GIL disabled')
@threading_helper.reap_threads
@threading_helper.requires_working_threading()
def test_free_threading_bytearrayiter(self):
# Non-deterministic but good chance to fail if bytearrayiter is not free-threading safe.
# We are fishing for a "Assertion failed: object has negative ref count" and tsan races.
def iter_next(b, it):
b.wait()
list(it)
def iter_reduce(b, it):
b.wait()
it.__reduce__()
def iter_setstate(b, it):
b.wait()
it.__setstate__(0)
def check(funcs, it):
barrier = threading.Barrier(len(funcs))
threads = []
for func in funcs:
thread = threading.Thread(target=func, args=(barrier, it))
threads.append(thread)
with threading_helper.start_threads(threads):
pass
for _ in range(10):
ba = bytearray(b'0' * 0x4000) # this is a load-bearing variable, do not remove
check([iter_next] * 10, iter(ba))
check([iter_next] + [iter_reduce] * 10, iter(ba)) # for tsan
check([iter_next] + [iter_setstate] * 10, iter(ba)) # for tsan
if __name__ == "__main__":
unittest.main()