blob: 8bcbd465cc32138e95259a90cd893eed5b3f870f [file] [log] [blame]
Guido van Rossumf7221c32000-02-25 19:25:05 +00001"""This test checks for correct fork() behavior.
Guido van Rossumf7221c32000-02-25 19:25:05 +00002"""
3
Brett Cannone4f41de2013-06-16 13:13:40 -04004import _imp as imp
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00005import os
Benjamin Peterson0df35a92009-10-04 20:32:25 +00006import signal
7import sys
Thomas Wouters0e3f5912006-08-11 14:57:12 +00008import time
Benjamin Peterson0df35a92009-10-04 20:32:25 +00009
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000010from test.fork_wait import ForkWait
Nick Coghlanb2ddf792010-12-02 04:11:46 +000011from test.support import (run_unittest, reap_children, get_attribute,
12 import_module, verbose)
13
Victor Stinner45df8202010-04-28 22:31:17 +000014threading = import_module('threading')
Guido van Rossumf7221c32000-02-25 19:25:05 +000015
R. David Murraya21e4ca2009-03-31 23:16:50 +000016# Skip test if fork does not exist.
17get_attribute(os, 'fork')
18
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000019class ForkTest(ForkWait):
20 def wait_impl(self, cpid):
Victor Stinner1e48eb32014-03-18 00:39:04 +010021 deadline = time.monotonic() + 10.0
22 while time.monotonic() <= deadline:
Thomas Wouters0e3f5912006-08-11 14:57:12 +000023 # waitpid() shouldn't hang, but some of the buildbots seem to hang
24 # in the forking tests. This is an attempt to fix the problem.
25 spid, status = os.waitpid(cpid, os.WNOHANG)
26 if spid == cpid:
27 break
Victor Stinner1e48eb32014-03-18 00:39:04 +010028 time.sleep(0.1)
Thomas Wouters0e3f5912006-08-11 14:57:12 +000029
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000030 self.assertEqual(spid, cpid)
31 self.assertEqual(status, 0, "cause = %d, exit = %d" % (status&0xff, status>>8))
Guido van Rossumf7221c32000-02-25 19:25:05 +000032
Nick Coghlanb2ddf792010-12-02 04:11:46 +000033 def test_threaded_import_lock_fork(self):
34 """Check fork() in main thread works while a subthread is doing an import"""
Benjamin Peterson0df35a92009-10-04 20:32:25 +000035 import_started = threading.Event()
36 fake_module_name = "fake test module"
37 partial_module = "partial"
38 complete_module = "complete"
39 def importer():
40 imp.acquire_lock()
41 sys.modules[fake_module_name] = partial_module
42 import_started.set()
43 time.sleep(0.01) # Give the other thread time to try and acquire.
44 sys.modules[fake_module_name] = complete_module
45 imp.release_lock()
46 t = threading.Thread(target=importer)
47 t.start()
48 import_started.wait()
49 pid = os.fork()
50 try:
Nick Coghlanb2ddf792010-12-02 04:11:46 +000051 # PyOS_BeforeFork should have waited for the import to complete
52 # before forking, so the child can recreate the import lock
53 # correctly, but also won't see a partially initialised module
Benjamin Peterson0df35a92009-10-04 20:32:25 +000054 if not pid:
55 m = __import__(fake_module_name)
56 if m == complete_module:
57 os._exit(0)
58 else:
Nick Coghlanb2ddf792010-12-02 04:11:46 +000059 if verbose > 1:
60 print("Child encountered partial module")
Benjamin Peterson0df35a92009-10-04 20:32:25 +000061 os._exit(1)
62 else:
63 t.join()
64 # Exitcode 1 means the child got a partial module (bad.) No
65 # exitcode (but a hang, which manifests as 'got pid 0')
66 # means the child deadlocked (also bad.)
67 self.wait_impl(pid)
68 finally:
69 try:
70 os.kill(pid, signal.SIGKILL)
71 except OSError:
72 pass
73
Nick Coghlanb2ddf792010-12-02 04:11:46 +000074
75 def test_nested_import_lock_fork(self):
76 """Check fork() in main thread works while the main thread is doing an import"""
77 # Issue 9573: this used to trigger RuntimeError in the child process
78 def fork_with_import_lock(level):
79 release = 0
80 in_child = False
81 try:
82 try:
83 for i in range(level):
84 imp.acquire_lock()
85 release += 1
86 pid = os.fork()
87 in_child = not pid
88 finally:
89 for i in range(release):
90 imp.release_lock()
91 except RuntimeError:
92 if in_child:
93 if verbose > 1:
94 print("RuntimeError in child")
95 os._exit(1)
96 raise
97 if in_child:
98 os._exit(0)
99 self.wait_impl(pid)
100
101 # Check this works with various levels of nested
102 # import in the main thread
103 for level in range(5):
104 fork_with_import_lock(level)
105
106
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000107def test_main():
108 run_unittest(ForkTest)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000109 reap_children()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000110
111if __name__ == "__main__":
112 test_main()