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