@@ -106,6 +106,7 @@ def tearDown(self):
106
106
107
107
108
108
class ThreadTests (BaseTestCase ):
109
+ maxDiff = 9999
109
110
110
111
@cpython_only
111
112
def test_name (self ):
@@ -639,19 +640,25 @@ def test_main_thread_after_fork(self):
639
640
import os, threading
640
641
from test import support
641
642
643
+ ident = threading.get_ident()
642
644
pid = os.fork()
643
645
if pid == 0:
646
+ print("current ident", threading.get_ident() == ident)
644
647
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 )
648
651
else:
649
652
support.wait_process(pid, exitcode=0)
650
653
"""
651
654
_ , out , err = assert_python_ok ("-c" , code )
652
655
data = out .decode ().replace ('\r ' , '' )
653
656
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 " )
655
662
656
663
@skip_unless_reliable_fork
657
664
@unittest .skipUnless (hasattr (os , 'waitpid' ), "test needs os.waitpid()" )
@@ -661,26 +668,99 @@ def test_main_thread_after_fork_from_nonmain_thread(self):
661
668
from test import support
662
669
663
670
def func():
671
+ ident = threading.get_ident()
664
672
pid = os.fork()
665
673
if pid == 0:
674
+ print("current ident", threading.get_ident() == ident)
666
675
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 )
670
679
# stdout is fully buffered because not a tty,
671
680
# we have to flush before exit.
672
681
sys.stdout.flush()
673
- else:
674
- support.wait_process(pid, exitcode=0)
675
682
676
683
th = threading.Thread(target=func)
677
684
th.start()
678
685
th.join()
679
686
"""
680
687
_ , out , err = assert_python_ok ("-c" , code )
681
688
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 )
684
764
685
765
def test_main_thread_during_shutdown (self ):
686
766
# bpo-31516: current_thread() should still point to the main thread
0 commit comments