|
10 | 10 | NTHREADS = 10 |
11 | 11 |
|
12 | 12 |
|
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 | | - |
67 | 13 | @threading_helper.requires_working_threading() |
68 | 14 | 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 |
74 | 17 | from pathlib import Path |
75 | 18 | import tempfile |
76 | 19 |
|
77 | | - names = ('fixture.py',) + tuple(f'file_{n:02d}.txt' |
78 | | - for n in range(32)) |
79 | 20 | 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) |
98 | 31 |
|
99 | 32 | @unittest.skipUnless(sysconfig.get_config_var('_Py_HAVE_STRERROR_R'), |
100 | 33 | 'need _Py_HAVE_STRERROR_R macro') |
|
0 commit comments