Skip to content

Commit bbbdcf0

Browse files
youknowdotcodex
andauthored
Update Lib set, type, and resource concurrency tests to Python 3.15 (#9435)
Sync unchanged canonical tests with existing threading and platform gates. Assisted-by: OpenAI Codex: model version unavailable Co-authored-by: Codex <codex@openai.com>
1 parent 9d21fef commit bbbdcf0

3 files changed

Lines changed: 468 additions & 0 deletions

File tree

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import unittest
2+
from test.support import import_helper, threading_helper
3+
4+
resource = import_helper.import_module("resource")
5+
6+
7+
NTHREADS = 10
8+
LOOP_PER_THREAD = 1000
9+
10+
11+
@threading_helper.requires_working_threading()
12+
class ResourceTest(unittest.TestCase):
13+
@unittest.skipUnless(hasattr(resource, "getrusage"), "needs getrusage")
14+
@unittest.skipUnless(
15+
hasattr(resource, "RUSAGE_THREAD"), "needs RUSAGE_THREAD"
16+
)
17+
def test_getrusage(self):
18+
ru_utime_lst = []
19+
20+
def dummy_work(ru_utime_lst):
21+
for _ in range(LOOP_PER_THREAD):
22+
pass
23+
24+
usage_process = resource.getrusage(resource.RUSAGE_SELF)
25+
usage_thread = resource.getrusage(resource.RUSAGE_THREAD)
26+
# Process user time should be greater than thread user time
27+
self.assertGreater(usage_process.ru_utime, usage_thread.ru_utime)
28+
ru_utime_lst.append(usage_thread.ru_utime)
29+
30+
threading_helper.run_concurrently(
31+
worker_func=dummy_work, args=(ru_utime_lst,), nthreads=NTHREADS
32+
)
33+
34+
usage_process = resource.getrusage(resource.RUSAGE_SELF)
35+
self.assertEqual(len(ru_utime_lst), NTHREADS)
36+
# Process user time should be greater than sum of all thread user times
37+
self.assertGreater(usage_process.ru_utime, sum(ru_utime_lst))
38+
39+
40+
if __name__ == "__main__":
41+
unittest.main()
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
import unittest
2+
from threading import Thread, Barrier, Event
3+
from test.support import threading_helper
4+
5+
6+
class TestSetRepr(unittest.TestCase):
7+
def test_repr_clear(self):
8+
"""Test repr() of a set while another thread is calling clear()"""
9+
NUM_ITERS = 10
10+
NUM_REPR_THREADS = 10
11+
barrier = Barrier(NUM_REPR_THREADS + 1, timeout=2)
12+
s = {1, 2, 3, 4, 5, 6, 7, 8}
13+
14+
def clear_set():
15+
barrier.wait()
16+
s.clear()
17+
18+
def repr_set():
19+
barrier.wait()
20+
set_reprs.append(repr(s))
21+
22+
for _ in range(NUM_ITERS):
23+
set_reprs = []
24+
threads = [Thread(target=clear_set)]
25+
for _ in range(NUM_REPR_THREADS):
26+
threads.append(Thread(target=repr_set))
27+
for t in threads:
28+
t.start()
29+
for t in threads:
30+
t.join()
31+
32+
for set_repr in set_reprs:
33+
self.assertIn(set_repr, ("set()", "{1, 2, 3, 4, 5, 6, 7, 8}"))
34+
35+
36+
class RaceTestBase:
37+
def test_contains_mutate(self):
38+
"""Test set contains operation combined with mutation."""
39+
barrier = Barrier(2, timeout=2)
40+
s = set()
41+
done = Event()
42+
43+
NUM_LOOPS = 1000
44+
45+
def read_set():
46+
barrier.wait()
47+
while not done.is_set():
48+
for i in range(self.SET_SIZE):
49+
item = i >> 1
50+
result = item in s
51+
52+
def mutate_set():
53+
barrier.wait()
54+
for i in range(NUM_LOOPS):
55+
s.clear()
56+
for j in range(self.SET_SIZE):
57+
s.add(j)
58+
for j in range(self.SET_SIZE):
59+
s.discard(j)
60+
# executes the set_swap_bodies() function
61+
s.__iand__(set(k for k in range(10, 20)))
62+
done.set()
63+
64+
threads = [Thread(target=read_set), Thread(target=mutate_set)]
65+
for t in threads:
66+
t.start()
67+
for t in threads:
68+
t.join()
69+
70+
def test_contains_frozenset(self):
71+
barrier = Barrier(3, timeout=2)
72+
done = Event()
73+
74+
NUM_LOOPS = 1_000
75+
76+
# This mutates the key used for contains test, not the container
77+
# itself. This works because frozenset allows the key to be a set().
78+
s = set()
79+
80+
def mutate_set():
81+
barrier.wait()
82+
while not done.is_set():
83+
s.add(0)
84+
s.add(1)
85+
s.clear()
86+
87+
def read_set():
88+
barrier.wait()
89+
container = frozenset([frozenset([0])])
90+
self.assertTrue(set([0]) in container)
91+
for _ in range(NUM_LOOPS):
92+
# Will return True when {0} is the key and False otherwise
93+
result = s in container
94+
done.set()
95+
96+
threads = [
97+
Thread(target=read_set),
98+
Thread(target=read_set),
99+
Thread(target=mutate_set),
100+
]
101+
for t in threads:
102+
t.start()
103+
for t in threads:
104+
t.join()
105+
106+
def test_contains_hash_mutate(self):
107+
"""Test set contains operation with mutating hash method."""
108+
barrier = Barrier(2, timeout=2)
109+
110+
NUM_LOOPS = 1_000
111+
SET_SIZE = self.SET_SIZE
112+
113+
s = set(range(SET_SIZE))
114+
115+
class Key:
116+
def __init__(self):
117+
self.count = 0
118+
self.value = 0
119+
120+
def __hash__(self):
121+
self.count += 1
122+
# This intends to trigger the SET_LOOKKEY_CHANGED case
123+
# of set_lookkey_threadsafe() since calling clear()
124+
# will cause the 'table' pointer to change.
125+
if self.count % 2 == 0:
126+
s.clear()
127+
else:
128+
s.update(range(SET_SIZE))
129+
return hash(self.value)
130+
131+
def __eq__(self, other):
132+
return self.value == other
133+
134+
key = Key()
135+
self.assertTrue(key in s)
136+
self.assertFalse(key in s)
137+
self.assertTrue(key in s)
138+
self.assertFalse(key in s)
139+
140+
def read_set():
141+
barrier.wait()
142+
for i in range(NUM_LOOPS):
143+
result = key in s
144+
145+
threads = [Thread(target=read_set), Thread(target=read_set)]
146+
for t in threads:
147+
t.start()
148+
for t in threads:
149+
t.join()
150+
151+
152+
@threading_helper.requires_working_threading()
153+
class SmallSetTest(RaceTestBase, unittest.TestCase):
154+
SET_SIZE = 6 # smaller than PySet_MINSIZE
155+
156+
157+
@threading_helper.requires_working_threading()
158+
class LargeSetTest(RaceTestBase, unittest.TestCase):
159+
SET_SIZE = 20 # larger than PySet_MINSIZE
160+
161+
162+
if __name__ == "__main__":
163+
unittest.main()

0 commit comments

Comments
 (0)