88import time
99
1010from test .fork_wait import ForkWait
11- from test .support import run_unittest , reap_children , get_attribute , import_module
11+ from test .support import (run_unittest , reap_children , get_attribute ,
12+ import_module , verbose )
13+
1214threading = import_module ('threading' )
1315
1416# Skip test if fork does not exist.
1517get_attribute (os , 'fork' )
1618
17-
1819class ForkTest (ForkWait ):
1920 def wait_impl (self , cpid ):
2021 for i in range (10 ):
@@ -28,7 +29,8 @@ def wait_impl(self, cpid):
2829 self .assertEqual (spid , cpid )
2930 self .assertEqual (status , 0 , "cause = %d, exit = %d" % (status & 0xff , status >> 8 ))
3031
31- def test_import_lock_fork (self ):
32+ def test_threaded_import_lock_fork (self ):
33+ """Check fork() in main thread works while a subthread is doing an import"""
3234 import_started = threading .Event ()
3335 fake_module_name = "fake test module"
3436 partial_module = "partial"
@@ -45,11 +47,16 @@ def importer():
4547 import_started .wait ()
4648 pid = os .fork ()
4749 try :
50+ # PyOS_BeforeFork should have waited for the import to complete
51+ # before forking, so the child can recreate the import lock
52+ # correctly, but also won't see a partially initialised module
4853 if not pid :
4954 m = __import__ (fake_module_name )
5055 if m == complete_module :
5156 os ._exit (0 )
5257 else :
58+ if verbose > 1 :
59+ print ("Child encountered partial module" )
5360 os ._exit (1 )
5461 else :
5562 t .join ()
@@ -63,6 +70,39 @@ def importer():
6370 except OSError :
6471 pass
6572
73+
74+ def test_nested_import_lock_fork (self ):
75+ """Check fork() in main thread works while the main thread is doing an import"""
76+ # Issue 9573: this used to trigger RuntimeError in the child process
77+ def fork_with_import_lock (level ):
78+ release = 0
79+ in_child = False
80+ try :
81+ try :
82+ for i in range (level ):
83+ imp .acquire_lock ()
84+ release += 1
85+ pid = os .fork ()
86+ in_child = not pid
87+ finally :
88+ for i in range (release ):
89+ imp .release_lock ()
90+ except RuntimeError :
91+ if in_child :
92+ if verbose > 1 :
93+ print ("RuntimeError in child" )
94+ os ._exit (1 )
95+ raise
96+ if in_child :
97+ os ._exit (0 )
98+ self .wait_impl (pid )
99+
100+ # Check this works with various levels of nested
101+ # import in the main thread
102+ for level in range (5 ):
103+ fork_with_import_lock (level )
104+
105+
66106def test_main ():
67107 run_unittest (ForkTest )
68108 reap_children ()
0 commit comments