Skip to content

Commit aac344d

Browse files
committed
gh-161769: Focus errno regression on ordinary threads and GC
Replace the directory/SI lifecycle helper with one public listdir test using the existing run_concurrently helper. Four ordinary threads each collect GC and check a real directory. No FileFinder, JSON, subinterpreters, ThreadPool, custom stop/barrier or deadline logic is needed. The exact smaller test reproduced BlockingIOError errno11 before and passed after the unchanged native errno fix on both main and3.15 native Release FT musl cores. The existing strerror test remains unchanged and the full test_os module passes on both corrected runtimes.
1 parent 920a86d commit aac344d

1 file changed

Lines changed: 12 additions & 79 deletions

File tree

‎Lib/test/test_free_threading/test_os.py‎

Lines changed: 12 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -10,91 +10,24 @@
1010
NTHREADS = 10
1111

1212

13-
def _directory_scan_during_gc(directory, names):
14-
import gc
15-
import importlib.machinery
16-
import json
17-
import os
18-
import threading
19-
import time
20-
from test import support
21-
22-
expected = set(names)
23-
barrier = threading.Barrier(5)
24-
stop = threading.Event()
25-
errors = []
26-
collections = []
27-
28-
def collect():
29-
barrier.wait(timeout=support.LONG_TIMEOUT)
30-
while not stop.is_set() and len(collections) < 512:
31-
collections.append(gc.collect())
32-
time.sleep(0.0001)
33-
34-
def scan():
35-
barrier.wait(timeout=support.LONG_TIMEOUT)
36-
for _ in range(256):
37-
try:
38-
assert set(os.listdir(directory)) == expected
39-
with os.scandir(directory) as entries:
40-
assert {entry.name for entry in entries} == expected
41-
finder = importlib.machinery.FileFinder(
42-
directory,
43-
(importlib.machinery.SourceFileLoader,
44-
importlib.machinery.SOURCE_SUFFIXES),
45-
)
46-
assert finder.find_spec('fixture') is not None
47-
except BaseException as error:
48-
errors.append(repr(error))
49-
50-
collector = threading.Thread(target=collect)
51-
workers = [threading.Thread(target=scan) for _ in range(4)]
52-
collector.start()
53-
for worker in workers:
54-
worker.start()
55-
try:
56-
for worker in workers:
57-
worker.join(timeout=support.LONG_TIMEOUT)
58-
finally:
59-
stop.set()
60-
collector.join(timeout=support.LONG_TIMEOUT)
61-
assert not collector.is_alive()
62-
assert all(not worker.is_alive() for worker in workers)
63-
assert collections
64-
return json.dumps(errors)
65-
66-
6713
@threading_helper.requires_working_threading()
6814
class TestOs(unittest.TestCase):
69-
def test_directory_io_during_subinterpreter_gc(self):
70-
from concurrent import interpreters
71-
from test import support
72-
from concurrent.futures import ThreadPoolExecutor
73-
import json
15+
def test_listdir_during_gc(self):
16+
import gc
7417
from pathlib import Path
7518
import tempfile
7619

77-
names = ('fixture.py',) + tuple(f'file_{n:02d}.txt'
78-
for n in range(32))
7920
with tempfile.TemporaryDirectory() as directory:
80-
for name in names:
81-
Path(directory, name).write_text('VALUE = 73\n')
82-
for _ in range(16):
83-
children = [interpreters.create() for _ in range(4)]
84-
try:
85-
with ThreadPoolExecutor(max_workers=4) as pool:
86-
futures = [pool.submit(child.call,
87-
_directory_scan_during_gc,
88-
directory, names)
89-
for child in children]
90-
results = [future.result(timeout=support.LONG_TIMEOUT)
91-
for future in futures]
92-
finally:
93-
for child in children:
94-
child.close()
95-
for result in results:
96-
errors = json.loads(result)
97-
self.assertFalse(errors, errors[:3])
21+
expected = {f"entry_{n}" for n in range(32)}
22+
for name in expected:
23+
Path(directory, name).touch()
24+
25+
def worker():
26+
for _ in range(256):
27+
gc.collect()
28+
self.assertEqual(set(os.listdir(directory)), expected)
29+
30+
run_concurrently(worker, nthreads=4)
9831

9932
@unittest.skipUnless(sysconfig.get_config_var('_Py_HAVE_STRERROR_R'),
10033
'need _Py_HAVE_STRERROR_R macro')

0 commit comments

Comments
 (0)