blob: 9ca9724c4c91eff80866a654608e7e22de1914de [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
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02008import threading
Thomas Wouters0e3f5912006-08-11 14:57:12 +00009import time
Zachary Wareac28b792015-12-04 23:32:23 -060010import unittest
Benjamin Peterson0df35a92009-10-04 20:32:25 +000011
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000012from test.fork_wait import ForkWait
Zachary Ware38c707e2015-04-13 15:00:43 -050013from test.support import (reap_children, get_attribute,
Nick Coghlanb2ddf792010-12-02 04:11:46 +000014 import_module, verbose)
15
Guido van Rossumf7221c32000-02-25 19:25:05 +000016
R. David Murraya21e4ca2009-03-31 23:16:50 +000017# Skip test if fork does not exist.
18get_attribute(os, 'fork')
19
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000020class ForkTest(ForkWait):
21 def wait_impl(self, cpid):
Victor Stinner1e48eb32014-03-18 00:39:04 +010022 deadline = time.monotonic() + 10.0
23 while time.monotonic() <= deadline:
Thomas Wouters0e3f5912006-08-11 14:57:12 +000024 # waitpid() shouldn't hang, but some of the buildbots seem to hang
25 # in the forking tests. This is an attempt to fix the problem.
26 spid, status = os.waitpid(cpid, os.WNOHANG)
27 if spid == cpid:
28 break
Victor Stinner1e48eb32014-03-18 00:39:04 +010029 time.sleep(0.1)
Thomas Wouters0e3f5912006-08-11 14:57:12 +000030
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000031 self.assertEqual(spid, cpid)
32 self.assertEqual(status, 0, "cause = %d, exit = %d" % (status&0xff, status>>8))
Guido van Rossumf7221c32000-02-25 19:25:05 +000033
Nick Coghlanb2ddf792010-12-02 04:11:46 +000034 def test_threaded_import_lock_fork(self):
35 """Check fork() in main thread works while a subthread is doing an import"""
Benjamin Peterson0df35a92009-10-04 20:32:25 +000036 import_started = threading.Event()
37 fake_module_name = "fake test module"
38 partial_module = "partial"
39 complete_module = "complete"
40 def importer():
41 imp.acquire_lock()
42 sys.modules[fake_module_name] = partial_module
43 import_started.set()
44 time.sleep(0.01) # Give the other thread time to try and acquire.
45 sys.modules[fake_module_name] = complete_module
46 imp.release_lock()
47 t = threading.Thread(target=importer)
48 t.start()
49 import_started.wait()
50 pid = os.fork()
51 try:
Nick Coghlanb2ddf792010-12-02 04:11:46 +000052 # PyOS_BeforeFork should have waited for the import to complete
53 # before forking, so the child can recreate the import lock
54 # correctly, but also won't see a partially initialised module
Benjamin Peterson0df35a92009-10-04 20:32:25 +000055 if not pid:
56 m = __import__(fake_module_name)
57 if m == complete_module:
58 os._exit(0)
59 else:
Nick Coghlanb2ddf792010-12-02 04:11:46 +000060 if verbose > 1:
61 print("Child encountered partial module")
Benjamin Peterson0df35a92009-10-04 20:32:25 +000062 os._exit(1)
63 else:
64 t.join()
65 # Exitcode 1 means the child got a partial module (bad.) No
66 # exitcode (but a hang, which manifests as 'got pid 0')
67 # means the child deadlocked (also bad.)
68 self.wait_impl(pid)
69 finally:
70 try:
71 os.kill(pid, signal.SIGKILL)
72 except OSError:
73 pass
74
Nick Coghlanb2ddf792010-12-02 04:11:46 +000075
76 def test_nested_import_lock_fork(self):
77 """Check fork() in main thread works while the main thread is doing an import"""
78 # Issue 9573: this used to trigger RuntimeError in the child process
79 def fork_with_import_lock(level):
80 release = 0
81 in_child = False
82 try:
83 try:
84 for i in range(level):
85 imp.acquire_lock()
86 release += 1
87 pid = os.fork()
88 in_child = not pid
89 finally:
90 for i in range(release):
91 imp.release_lock()
92 except RuntimeError:
93 if in_child:
94 if verbose > 1:
95 print("RuntimeError in child")
96 os._exit(1)
97 raise
98 if in_child:
99 os._exit(0)
100 self.wait_impl(pid)
101
102 # Check this works with various levels of nested
103 # import in the main thread
104 for level in range(5):
105 fork_with_import_lock(level)
106
107
Zachary Ware38c707e2015-04-13 15:00:43 -0500108def tearDownModule():
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000109 reap_children()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000110
111if __name__ == "__main__":
Zachary Ware38c707e2015-04-13 15:00:43 -0500112 unittest.main()