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)
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