Skip to content

Commit 1a437fc

Browse files
authored
Merge pull request RustPython#6920 from fanninpm/3.14-test-fork1
Update `test_fork1` from v3.14.2
2 parents 111ced0 + 6f9320b commit 1a437fc

1 file changed

Lines changed: 103 additions & 0 deletions

File tree

Lib/test/test_fork1.py

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
"""This test checks for correct fork() behavior.
2+
"""
3+
4+
import _imp as imp
5+
import os
6+
import signal
7+
import sys
8+
import threading
9+
import time
10+
import unittest
11+
12+
from test.fork_wait import ForkWait
13+
from test import support
14+
15+
16+
# Skip test if fork does not exist.
17+
if not support.has_fork_support:
18+
raise unittest.SkipTest("test module requires working os.fork")
19+
20+
21+
class ForkTest(ForkWait):
22+
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: process 44587 exited with code 1, but exit code 42 is expected
23+
def test_threaded_import_lock_fork(self):
24+
"""Check fork() in main thread works while a subthread is doing an import"""
25+
import_started = threading.Event()
26+
fake_module_name = "fake test module"
27+
partial_module = "partial"
28+
complete_module = "complete"
29+
def importer():
30+
imp.acquire_lock()
31+
sys.modules[fake_module_name] = partial_module
32+
import_started.set()
33+
time.sleep(0.01) # Give the other thread time to try and acquire.
34+
sys.modules[fake_module_name] = complete_module
35+
imp.release_lock()
36+
t = threading.Thread(target=importer)
37+
t.start()
38+
import_started.wait()
39+
exitcode = 42
40+
pid = os.fork()
41+
try:
42+
# PyOS_BeforeFork should have waited for the import to complete
43+
# before forking, so the child can recreate the import lock
44+
# correctly, but also won't see a partially initialised module
45+
if not pid:
46+
m = __import__(fake_module_name)
47+
if m == complete_module:
48+
os._exit(exitcode)
49+
else:
50+
if support.verbose > 1:
51+
print("Child encountered partial module")
52+
os._exit(1)
53+
else:
54+
t.join()
55+
# Exitcode 1 means the child got a partial module (bad.) No
56+
# exitcode (but a hang, which manifests as 'got pid 0')
57+
# means the child deadlocked (also bad.)
58+
self.wait_impl(pid, exitcode=exitcode)
59+
finally:
60+
try:
61+
os.kill(pid, signal.SIGKILL)
62+
except OSError:
63+
pass
64+
65+
66+
def test_nested_import_lock_fork(self):
67+
"""Check fork() in main thread works while the main thread is doing an import"""
68+
exitcode = 42
69+
# Issue 9573: this used to trigger RuntimeError in the child process
70+
def fork_with_import_lock(level):
71+
release = 0
72+
in_child = False
73+
try:
74+
try:
75+
for i in range(level):
76+
imp.acquire_lock()
77+
release += 1
78+
pid = os.fork()
79+
in_child = not pid
80+
finally:
81+
for i in range(release):
82+
imp.release_lock()
83+
except RuntimeError:
84+
if in_child:
85+
if support.verbose > 1:
86+
print("RuntimeError in child")
87+
os._exit(1)
88+
raise
89+
if in_child:
90+
os._exit(exitcode)
91+
self.wait_impl(pid, exitcode=exitcode)
92+
93+
# Check this works with various levels of nested
94+
# import in the main thread
95+
for level in range(5):
96+
fork_with_import_lock(level)
97+
98+
99+
def tearDownModule():
100+
support.reap_children()
101+
102+
if __name__ == "__main__":
103+
unittest.main()

0 commit comments

Comments
 (0)