@@ -106,6 +106,7 @@ def tearDown(self):
106106
107107
108108class ThreadTests (BaseTestCase ):
109+ maxDiff = 9999
109110
110111 @cpython_only
111112 def test_name (self ):
@@ -639,19 +640,25 @@ def test_main_thread_after_fork(self):
639640 import os, threading
640641 from test import support
641642
643+ ident = threading.get_ident()
642644 pid = os.fork()
643645 if pid == 0:
646+ print("current ident", threading.get_ident() == ident)
644647 main = threading.main_thread()
645- print(main.name)
646- print(main.ident == threading.current_thread(). ident)
647- print(main.ident == threading.get_ident() )
648+ print("main", main.name)
649+ print(" main ident", main .ident == ident)
650+ print("current is main", threading.current_thread() is main )
648651 else:
649652 support.wait_process(pid, exitcode=0)
650653 """
651654 _ , out , err = assert_python_ok ("-c" , code )
652655 data = out .decode ().replace ('\r ' , '' )
653656 self .assertEqual (err , b"" )
654- self .assertEqual (data , "MainThread\n True\n True\n " )
657+ self .assertEqual (data ,
658+ "current ident True\n "
659+ "main MainThread\n "
660+ "main ident True\n "
661+ "current is main True\n " )
655662
656663 @skip_unless_reliable_fork
657664 @unittest .skipUnless (hasattr (os , 'waitpid' ), "test needs os.waitpid()" )
@@ -661,26 +668,99 @@ def test_main_thread_after_fork_from_nonmain_thread(self):
661668 from test import support
662669
663670 def func():
671+ ident = threading.get_ident()
664672 pid = os.fork()
665673 if pid == 0:
674+ print("current ident", threading.get_ident() == ident)
666675 main = threading.main_thread()
667- print(main.name)
668- print(main.ident == threading.current_thread(). ident)
669- print(main.ident == threading.get_ident() )
676+ print(" main", main .name, type(main).__name__ )
677+ print(" main ident", main .ident == ident)
678+ print("current is main", threading.current_thread() is main )
670679 # stdout is fully buffered because not a tty,
671680 # we have to flush before exit.
672681 sys.stdout.flush()
673- else:
674- support.wait_process(pid, exitcode=0)
675682
676683 th = threading.Thread(target=func)
677684 th.start()
678685 th.join()
679686 """
680687 _ , out , err = assert_python_ok ("-c" , code )
681688 data = out .decode ().replace ('\r ' , '' )
682- self .assertEqual (err , b"" )
683- self .assertEqual (data , "Thread-1 (func)\n True\n True\n " )
689+ self .assertEqual (err .decode ('utf-8' ), "" )
690+ self .assertEqual (data ,
691+ "current ident True\n "
692+ "main Thread-1 (func) Thread\n "
693+ "main ident True\n "
694+ "current is main True\n "
695+ )
696+
697+ @unittest .skipIf (sys .platform in platforms_to_skip , "due to known OS bug" )
698+ @support .requires_fork ()
699+ @unittest .skipUnless (hasattr (os , 'waitpid' ), "test needs os.waitpid()" )
700+ def test_main_thread_after_fork_from_foreign_thread (self , create_dummy = False ):
701+ code = """if 1:
702+ import os, threading, sys, traceback, _thread
703+ from test import support
704+
705+ def func(lock):
706+ ident = threading.get_ident()
707+ if %s:
708+ # call current_thread() before fork to allocate DummyThread
709+ current = threading.current_thread()
710+ print("current", current.name, type(current).__name__)
711+ print("ident in _active", ident in threading._active)
712+ # flush before fork, so child won't flush it again
713+ sys.stdout.flush()
714+ pid = os.fork()
715+ if pid == 0:
716+ print("current ident", threading.get_ident() == ident)
717+ main = threading.main_thread()
718+ print("main", main.name, type(main).__name__)
719+ print("main ident", main.ident == ident)
720+ print("current is main", threading.current_thread() is main)
721+ print("_dangling", [t.name for t in list(threading._dangling)])
722+ # stdout is fully buffered because not a tty,
723+ # we have to flush before exit.
724+ sys.stdout.flush()
725+ try:
726+ threading._shutdown()
727+ os._exit(0)
728+ except:
729+ traceback.print_exc()
730+ sys.stderr.flush()
731+ os._exit(1)
732+ else:
733+ try:
734+ support.wait_process(pid, exitcode=0)
735+ except Exception:
736+ # avoid 'could not acquire lock for
737+ # <_io.BufferedWriter name='<stderr>'> at interpreter shutdown,'
738+ traceback.print_exc()
739+ sys.stderr.flush()
740+ finally:
741+ lock.release()
742+
743+ join_lock = _thread.allocate_lock()
744+ join_lock.acquire()
745+ th = _thread.start_new_thread(func, (join_lock,))
746+ join_lock.acquire()
747+ """ % create_dummy
748+ # "DeprecationWarning: This process is multi-threaded, use of fork()
749+ # may lead to deadlocks in the child"
750+ _ , out , err = assert_python_ok ("-W" , "ignore::DeprecationWarning" , "-c" , code )
751+ data = out .decode ().replace ('\r ' , '' )
752+ self .assertEqual (err .decode (), "" )
753+ self .assertEqual (data ,
754+ ("current Dummy-1 _DummyThread\n " if create_dummy else "" ) +
755+ f"ident in _active { create_dummy !s} \n " +
756+ "current ident True\n "
757+ "main MainThread _MainThread\n "
758+ "main ident True\n "
759+ "current is main True\n "
760+ "_dangling ['MainThread']\n " )
761+
762+ def test_main_thread_after_fork_from_dummy_thread (self , create_dummy = False ):
763+ self .test_main_thread_after_fork_from_foreign_thread (create_dummy = True )
684764
685765 def test_main_thread_during_shutdown (self ):
686766 # bpo-31516: current_thread() should still point to the main thread
0 commit comments