@@ -115,6 +115,7 @@ def tearDown(self):
115115
116116
117117class ThreadTests (BaseTestCase ):
118+ maxDiff = 9999
118119
119120 @cpython_only
120121 def test_name (self ):
@@ -627,19 +628,25 @@ def test_main_thread_after_fork(self):
627628 import os, threading
628629 from test import support
629630
631+ ident = threading.get_ident()
630632 pid = os.fork()
631633 if pid == 0:
634+ print("current ident", threading.get_ident() == ident)
632635 main = threading.main_thread()
633- print(main.name)
634- print(main.ident == threading.current_thread(). ident)
635- print(main.ident == threading.get_ident() )
636+ print("main", main.name)
637+ print(" main ident", main .ident == ident)
638+ print("current is main", threading.current_thread() is main )
636639 else:
637640 support.wait_process(pid, exitcode=0)
638641 """
639642 _ , out , err = assert_python_ok ("-c" , code )
640643 data = out .decode ().replace ('\r ' , '' )
641644 self .assertEqual (err , b"" )
642- self .assertEqual (data , "MainThread\n True\n True\n " )
645+ self .assertEqual (data ,
646+ "current ident True\n "
647+ "main MainThread\n "
648+ "main ident True\n "
649+ "current is main True\n " )
643650
644651 @skip_unless_reliable_fork
645652 @unittest .skipUnless (hasattr (os , 'waitpid' ), "test needs os.waitpid()" )
@@ -649,15 +656,17 @@ def test_main_thread_after_fork_from_nonmain_thread(self):
649656 from test import support
650657
651658 def func():
659+ ident = threading.get_ident()
652660 with warnings.catch_warnings(record=True) as ws:
653661 warnings.filterwarnings(
654662 "always", category=DeprecationWarning)
655663 pid = os.fork()
656664 if pid == 0:
665+ print("current ident", threading.get_ident() == ident)
657666 main = threading.main_thread()
658- print(main.name)
659- print(main.ident == threading.current_thread(). ident)
660- print(main.ident == threading.get_ident() )
667+ print(" main", main .name, type(main).__name__ )
668+ print(" main ident", main .ident == ident)
669+ print("current is main", threading.current_thread() is main )
661670 # stdout is fully buffered because not a tty,
662671 # we have to flush before exit.
663672 sys.stdout.flush()
@@ -673,7 +682,80 @@ def func():
673682 _ , out , err = assert_python_ok ("-c" , code )
674683 data = out .decode ().replace ('\r ' , '' )
675684 self .assertEqual (err .decode ('utf-8' ), "" )
676- self .assertEqual (data , "Thread-1 (func)\n True\n True\n " )
685+ self .assertEqual (data ,
686+ "current ident True\n "
687+ "main Thread-1 (func) Thread\n "
688+ "main ident True\n "
689+ "current is main True\n "
690+ )
691+
692+ @unittest .skipIf (sys .platform in platforms_to_skip , "due to known OS bug" )
693+ @support .requires_fork ()
694+ @unittest .skipUnless (hasattr (os , 'waitpid' ), "test needs os.waitpid()" )
695+ def test_main_thread_after_fork_from_foreign_thread (self , create_dummy = False ):
696+ code = """if 1:
697+ import os, threading, sys, traceback, _thread
698+ from test import support
699+
700+ def func(lock):
701+ ident = threading.get_ident()
702+ if %s:
703+ # call current_thread() before fork to allocate DummyThread
704+ current = threading.current_thread()
705+ print("current", current.name, type(current).__name__)
706+ print("ident in _active", ident in threading._active)
707+ # flush before fork, so child won't flush it again
708+ sys.stdout.flush()
709+ pid = os.fork()
710+ if pid == 0:
711+ print("current ident", threading.get_ident() == ident)
712+ main = threading.main_thread()
713+ print("main", main.name, type(main).__name__)
714+ print("main ident", main.ident == ident)
715+ print("current is main", threading.current_thread() is main)
716+ print("_dangling", [t.name for t in list(threading._dangling)])
717+ # stdout is fully buffered because not a tty,
718+ # we have to flush before exit.
719+ sys.stdout.flush()
720+ try:
721+ threading._shutdown()
722+ os._exit(0)
723+ except:
724+ traceback.print_exc()
725+ sys.stderr.flush()
726+ os._exit(1)
727+ else:
728+ try:
729+ support.wait_process(pid, exitcode=0)
730+ except Exception:
731+ # avoid 'could not acquire lock for
732+ # <_io.BufferedWriter name='<stderr>'> at interpreter shutdown,'
733+ traceback.print_exc()
734+ sys.stderr.flush()
735+ finally:
736+ lock.release()
737+
738+ join_lock = _thread.allocate_lock()
739+ join_lock.acquire()
740+ th = _thread.start_new_thread(func, (join_lock,))
741+ join_lock.acquire()
742+ """ % create_dummy
743+ # "DeprecationWarning: This process is multi-threaded, use of fork()
744+ # may lead to deadlocks in the child"
745+ _ , out , err = assert_python_ok ("-W" , "ignore::DeprecationWarning" , "-c" , code )
746+ data = out .decode ().replace ('\r ' , '' )
747+ self .assertEqual (err .decode (), "" )
748+ self .assertEqual (data ,
749+ ("current Dummy-1 _DummyThread\n " if create_dummy else "" ) +
750+ f"ident in _active { create_dummy !s} \n " +
751+ "current ident True\n "
752+ "main MainThread _MainThread\n "
753+ "main ident True\n "
754+ "current is main True\n "
755+ "_dangling ['MainThread']\n " )
756+
757+ def test_main_thread_after_fork_from_dummy_thread (self , create_dummy = False ):
758+ self .test_main_thread_after_fork_from_foreign_thread (create_dummy = True )
677759
678760 def test_main_thread_during_shutdown (self ):
679761 # bpo-31516: current_thread() should still point to the main thread
0 commit comments