@@ -115,6 +115,7 @@ def tearDown(self):
115115
116116
117117class ThreadTests (BaseTestCase ):
118+ maxDiff = 9999
118119
119120 @cpython_only
120121 def test_name (self ):
@@ -676,19 +677,25 @@ def test_main_thread_after_fork(self):
676677 import os, threading
677678 from test import support
678679
680+ ident = threading.get_ident()
679681 pid = os.fork()
680682 if pid == 0:
683+ print("current ident", threading.get_ident() == ident)
681684 main = threading.main_thread()
682- print(main.name)
683- print(main.ident == threading.current_thread(). ident)
684- print(main.ident == threading.get_ident() )
685+ print("main", main.name)
686+ print(" main ident", main .ident == ident)
687+ print("current is main", threading.current_thread() is main )
685688 else:
686689 support.wait_process(pid, exitcode=0)
687690 """
688691 _ , out , err = assert_python_ok ("-c" , code )
689692 data = out .decode ().replace ('\r ' , '' )
690693 self .assertEqual (err , b"" )
691- self .assertEqual (data , "MainThread\n True\n True\n " )
694+ self .assertEqual (data ,
695+ "current ident True\n "
696+ "main MainThread\n "
697+ "main ident True\n "
698+ "current is main True\n " )
692699
693700 @skip_unless_reliable_fork
694701 @unittest .skipUnless (hasattr (os , 'waitpid' ), "test needs os.waitpid()" )
@@ -698,15 +705,17 @@ def test_main_thread_after_fork_from_nonmain_thread(self):
698705 from test import support
699706
700707 def func():
708+ ident = threading.get_ident()
701709 with warnings.catch_warnings(record=True) as ws:
702710 warnings.filterwarnings(
703711 "always", category=DeprecationWarning)
704712 pid = os.fork()
705713 if pid == 0:
714+ print("current ident", threading.get_ident() == ident)
706715 main = threading.main_thread()
707- print(main.name)
708- print(main.ident == threading.current_thread(). ident)
709- print(main.ident == threading.get_ident() )
716+ print(" main", main .name, type(main).__name__ )
717+ print(" main ident", main .ident == ident)
718+ print("current is main", threading.current_thread() is main )
710719 # stdout is fully buffered because not a tty,
711720 # we have to flush before exit.
712721 sys.stdout.flush()
@@ -722,7 +731,80 @@ def func():
722731 _ , out , err = assert_python_ok ("-c" , code )
723732 data = out .decode ().replace ('\r ' , '' )
724733 self .assertEqual (err .decode ('utf-8' ), "" )
725- self .assertEqual (data , "Thread-1 (func)\n True\n True\n " )
734+ self .assertEqual (data ,
735+ "current ident True\n "
736+ "main Thread-1 (func) Thread\n "
737+ "main ident True\n "
738+ "current is main True\n "
739+ )
740+
741+ @unittest .skipIf (sys .platform in platforms_to_skip , "due to known OS bug" )
742+ @support .requires_fork ()
743+ @unittest .skipUnless (hasattr (os , 'waitpid' ), "test needs os.waitpid()" )
744+ def test_main_thread_after_fork_from_foreign_thread (self , create_dummy = False ):
745+ code = """if 1:
746+ import os, threading, sys, traceback, _thread
747+ from test import support
748+
749+ def func(lock):
750+ ident = threading.get_ident()
751+ if %s:
752+ # call current_thread() before fork to allocate DummyThread
753+ current = threading.current_thread()
754+ print("current", current.name, type(current).__name__)
755+ print("ident in _active", ident in threading._active)
756+ # flush before fork, so child won't flush it again
757+ sys.stdout.flush()
758+ pid = os.fork()
759+ if pid == 0:
760+ print("current ident", threading.get_ident() == ident)
761+ main = threading.main_thread()
762+ print("main", main.name, type(main).__name__)
763+ print("main ident", main.ident == ident)
764+ print("current is main", threading.current_thread() is main)
765+ print("_dangling", [t.name for t in list(threading._dangling)])
766+ # stdout is fully buffered because not a tty,
767+ # we have to flush before exit.
768+ sys.stdout.flush()
769+ try:
770+ threading._shutdown()
771+ os._exit(0)
772+ except:
773+ traceback.print_exc()
774+ sys.stderr.flush()
775+ os._exit(1)
776+ else:
777+ try:
778+ support.wait_process(pid, exitcode=0)
779+ except Exception:
780+ # avoid 'could not acquire lock for
781+ # <_io.BufferedWriter name='<stderr>'> at interpreter shutdown,'
782+ traceback.print_exc()
783+ sys.stderr.flush()
784+ finally:
785+ lock.release()
786+
787+ join_lock = _thread.allocate_lock()
788+ join_lock.acquire()
789+ th = _thread.start_new_thread(func, (join_lock,))
790+ join_lock.acquire()
791+ """ % create_dummy
792+ # "DeprecationWarning: This process is multi-threaded, use of fork()
793+ # may lead to deadlocks in the child"
794+ _ , out , err = assert_python_ok ("-W" , "ignore::DeprecationWarning" , "-c" , code )
795+ data = out .decode ().replace ('\r ' , '' )
796+ self .assertEqual (err .decode (), "" )
797+ self .assertEqual (data ,
798+ ("current Dummy-1 _DummyThread\n " if create_dummy else "" ) +
799+ f"ident in _active { create_dummy !s} \n " +
800+ "current ident True\n "
801+ "main MainThread _MainThread\n "
802+ "main ident True\n "
803+ "current is main True\n "
804+ "_dangling ['MainThread']\n " )
805+
806+ def test_main_thread_after_fork_from_dummy_thread (self , create_dummy = False ):
807+ self .test_main_thread_after_fork_from_foreign_thread (create_dummy = True )
726808
727809 def test_main_thread_during_shutdown (self ):
728810 # bpo-31516: current_thread() should still point to the main thread
0 commit comments