xref: /third_party/python/Lib/test/test_fork1.py (revision 7db96d56)
17db96d56Sopenharmony_ci"""This test checks for correct fork() behavior.
27db96d56Sopenharmony_ci"""
37db96d56Sopenharmony_ci
47db96d56Sopenharmony_ciimport _imp as imp
57db96d56Sopenharmony_ciimport os
67db96d56Sopenharmony_ciimport signal
77db96d56Sopenharmony_ciimport sys
87db96d56Sopenharmony_ciimport threading
97db96d56Sopenharmony_ciimport time
107db96d56Sopenharmony_ciimport unittest
117db96d56Sopenharmony_ci
127db96d56Sopenharmony_cifrom test.fork_wait import ForkWait
137db96d56Sopenharmony_cifrom test import support
147db96d56Sopenharmony_ci
157db96d56Sopenharmony_ci
167db96d56Sopenharmony_ci# Skip test if fork does not exist.
177db96d56Sopenharmony_ciif not support.has_fork_support:
187db96d56Sopenharmony_ci    raise unittest.SkipTest("test module requires working os.fork")
197db96d56Sopenharmony_ci
207db96d56Sopenharmony_ci
217db96d56Sopenharmony_ciclass ForkTest(ForkWait):
227db96d56Sopenharmony_ci    def test_threaded_import_lock_fork(self):
237db96d56Sopenharmony_ci        """Check fork() in main thread works while a subthread is doing an import"""
247db96d56Sopenharmony_ci        import_started = threading.Event()
257db96d56Sopenharmony_ci        fake_module_name = "fake test module"
267db96d56Sopenharmony_ci        partial_module = "partial"
277db96d56Sopenharmony_ci        complete_module = "complete"
287db96d56Sopenharmony_ci        def importer():
297db96d56Sopenharmony_ci            imp.acquire_lock()
307db96d56Sopenharmony_ci            sys.modules[fake_module_name] = partial_module
317db96d56Sopenharmony_ci            import_started.set()
327db96d56Sopenharmony_ci            time.sleep(0.01) # Give the other thread time to try and acquire.
337db96d56Sopenharmony_ci            sys.modules[fake_module_name] = complete_module
347db96d56Sopenharmony_ci            imp.release_lock()
357db96d56Sopenharmony_ci        t = threading.Thread(target=importer)
367db96d56Sopenharmony_ci        t.start()
377db96d56Sopenharmony_ci        import_started.wait()
387db96d56Sopenharmony_ci        exitcode = 42
397db96d56Sopenharmony_ci        pid = os.fork()
407db96d56Sopenharmony_ci        try:
417db96d56Sopenharmony_ci            # PyOS_BeforeFork should have waited for the import to complete
427db96d56Sopenharmony_ci            # before forking, so the child can recreate the import lock
437db96d56Sopenharmony_ci            # correctly, but also won't see a partially initialised module
447db96d56Sopenharmony_ci            if not pid:
457db96d56Sopenharmony_ci                m = __import__(fake_module_name)
467db96d56Sopenharmony_ci                if m == complete_module:
477db96d56Sopenharmony_ci                    os._exit(exitcode)
487db96d56Sopenharmony_ci                else:
497db96d56Sopenharmony_ci                    if support.verbose > 1:
507db96d56Sopenharmony_ci                        print("Child encountered partial module")
517db96d56Sopenharmony_ci                    os._exit(1)
527db96d56Sopenharmony_ci            else:
537db96d56Sopenharmony_ci                t.join()
547db96d56Sopenharmony_ci                # Exitcode 1 means the child got a partial module (bad.) No
557db96d56Sopenharmony_ci                # exitcode (but a hang, which manifests as 'got pid 0')
567db96d56Sopenharmony_ci                # means the child deadlocked (also bad.)
577db96d56Sopenharmony_ci                self.wait_impl(pid, exitcode=exitcode)
587db96d56Sopenharmony_ci        finally:
597db96d56Sopenharmony_ci            try:
607db96d56Sopenharmony_ci                os.kill(pid, signal.SIGKILL)
617db96d56Sopenharmony_ci            except OSError:
627db96d56Sopenharmony_ci                pass
637db96d56Sopenharmony_ci
647db96d56Sopenharmony_ci
657db96d56Sopenharmony_ci    def test_nested_import_lock_fork(self):
667db96d56Sopenharmony_ci        """Check fork() in main thread works while the main thread is doing an import"""
677db96d56Sopenharmony_ci        exitcode = 42
687db96d56Sopenharmony_ci        # Issue 9573: this used to trigger RuntimeError in the child process
697db96d56Sopenharmony_ci        def fork_with_import_lock(level):
707db96d56Sopenharmony_ci            release = 0
717db96d56Sopenharmony_ci            in_child = False
727db96d56Sopenharmony_ci            try:
737db96d56Sopenharmony_ci                try:
747db96d56Sopenharmony_ci                    for i in range(level):
757db96d56Sopenharmony_ci                        imp.acquire_lock()
767db96d56Sopenharmony_ci                        release += 1
777db96d56Sopenharmony_ci                    pid = os.fork()
787db96d56Sopenharmony_ci                    in_child = not pid
797db96d56Sopenharmony_ci                finally:
807db96d56Sopenharmony_ci                    for i in range(release):
817db96d56Sopenharmony_ci                        imp.release_lock()
827db96d56Sopenharmony_ci            except RuntimeError:
837db96d56Sopenharmony_ci                if in_child:
847db96d56Sopenharmony_ci                    if support.verbose > 1:
857db96d56Sopenharmony_ci                        print("RuntimeError in child")
867db96d56Sopenharmony_ci                    os._exit(1)
877db96d56Sopenharmony_ci                raise
887db96d56Sopenharmony_ci            if in_child:
897db96d56Sopenharmony_ci                os._exit(exitcode)
907db96d56Sopenharmony_ci            self.wait_impl(pid, exitcode=exitcode)
917db96d56Sopenharmony_ci
927db96d56Sopenharmony_ci        # Check this works with various levels of nested
937db96d56Sopenharmony_ci        # import in the main thread
947db96d56Sopenharmony_ci        for level in range(5):
957db96d56Sopenharmony_ci            fork_with_import_lock(level)
967db96d56Sopenharmony_ci
977db96d56Sopenharmony_ci
987db96d56Sopenharmony_cidef tearDownModule():
997db96d56Sopenharmony_ci    support.reap_children()
1007db96d56Sopenharmony_ci
1017db96d56Sopenharmony_ciif __name__ == "__main__":
1027db96d56Sopenharmony_ci    unittest.main()
103