8000 gh-132551: make `io.BytesIO` thread safe (#132616) · python/cpython@5dd3a3a · GitHub
[go: up one dir, main page]

Skip to content

Commit 5dd3a3a

Browse files
gh-132551: make io.BytesIO thread safe (#132616)
Co-authored-by: Kumar Aditya <kumaraditya@python.org>
1 parent bd7c585 commit 5dd3a3a

File tree

4 files changed

+355
-99
lines changed

4 files changed

+355
-99
lines changed
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import threading
2+
from unittest import TestCase
3+
from test.support import threading_helper
4+
from random import randint
5+
from io import BytesIO
6+
from sys import getsizeof
7+
8+
9+
class TestBytesIO(TestCase):
10+
# Test pretty much everything that can break under free-threading.
11+
# Non-deterministic, but at least one of these things will fail if
12+
# BytesIO object is not free-thread safe.
13+
14+
def check(self, funcs, *args):
15+
barrier = threading.Barrier(len(funcs))
16+
threads = []
17+
18+
for func in funcs:
19+
thread = threading.Thread(target=func, args=(barrier, *args))
20+
21+
threads.append(thread)
22+
23+
with threading_helper.start_threads(threads):
24+
pass
25+
26+
@threading_helper.requires_working_threading()
27+
@threading_helper.reap_threads
28+
def test_free_threading(self):
29+
"""Test for segfaults and aborts."""
30+
31+
def write(barrier, b, *ignore):
32+
barrier.wait()
33+
try: b.write(b'0' * randint(100, 1000))
34+
except ValueError: pass # ignore write fail to closed file
35+
36+
def writelines(barrier, b, *ignore):
37+
barrier.wait()
38+
b.write(b'0\n' * randint(100, 1000))
39+
40+
def truncate(barrier, b, *ignore):
41+
barrier.wait()
42+
try: b.truncate(0)
43+
except: BufferError # ignore exported buffer
44+
45+
def read(barrier, b, *ignore):
46+
barrier.wait()
47+
b.read()
48+
49+
def read1(barrier, b, *ignore):
50+
barrier.wait()
51+
b.read1()
52+
53+
def readline(barrier, b, *ignore):
54+
barrier.wait()
55+
b.readline()
56+
57+
def readlines(barrier, b, *ignore):
58+
barrier.wait()
59+
b.readlines()
60+
61+
def readinto(barrier, b, into, *ignore):
62+
barrier.wait()
63+
b.readinto(into)
64+
65+
def close(barrier, b, *ignore):
66+
barrier.wait()
67+
b.close()
68+
69+
def getvalue(barrier, b, *ignore):
70+
barrier.wait()
71+
b.getvalue()
72+
73+
def getbuffer(barrier, b, *ignore):
74+
barrier.wait()
75+
b.getbuffer()
76+
77+
def iter(barrier, b, *ignore):
78+
barrier.wait()
79+
list(b)
80+
81+
def getstate(barrier, b, *ignore):
82+
barrier.wait()
83+
b.__getstate__()
84+
85+
def setstate(barrier, b, st, *ignore):
86+
barrier.wait()
87+
b.__setstate__(st)
88+
89+
def sizeof(barrier, b, *ignore):
90+
barrier.wait()
91+
getsizeof(b)
92+
93+
self.check([write] * 10, BytesIO())
94+
self.check([writelines] * 10, BytesIO())
95+
self.check([write] * 10 + [truncate] * 10, BytesIO())
96+
self.check([truncate] + [read] * 10, BytesIO(b'0\n'*204800))
97+
self.check([truncate] + [read1] * 10, BytesIO(b'0\n'*204800))
98+
self.check([truncate] + [readline] * 10, BytesIO(b'0\n'*20480))
99+
self.check([truncate] + [readlines] * 10, BytesIO(b'0\n'*20480))
100+
self.check([truncate] + [readinto] * 10, BytesIO(b'0\n'*204800), bytearray(b'0\n'*204800))
101+
self.check([close] + [write] * 10, BytesIO())
102+
self.check([truncate] + [getvalue] * 10, BytesIO(b'0\n'*204800))
103+
self.check([truncate] + [getbuffer] * 10, BytesIO(b'0\n'*204800))
104+
self.check([truncate] + [iter] * 10, BytesIO(b'0\n'*20480))
105+
self.check([truncate] + [getstate] * 10, BytesIO(b'0\n'*204800))
106+
self.check([truncate] + [setstate] * 10, BytesIO(b'0\n'*204800), (b'123', 0, None))
107+
self.check([truncate] + [sizeof] * 10, BytesIO(b'0\n'*204800))
108+
109+
# no tests for seek or tell because they don't break anything
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Make :class:`io.BytesIO` safe in :term:`free-threaded <free threading>` build.

0 commit comments

Comments
 (0)
0