@@ -700,6 +700,49 @@ def my_release_save():
700700 output = "end of worker thread\n end of main thread\n "
701701 self .assertScriptHasOutput (script , output )
702702
703+ @unittest .skipIf (sys .platform in platforms_to_skip , "due to known OS bug" )
704+ def test_6_daemon_threads (self ):
705+ # Check that a daemon thread cannot crash the interpreter on shutdown
706+ # by manipulating internal structures that are being disposed of in
707+ # the main thread.
708+ script = """if True:
709+ import os
710+ import random
711+ import sys
712+ import time
713+ import threading
714+
715+ thread_has_run = set()
716+
717+ def random_io():
718+ '''Loop for a while sleeping random tiny amounts and doing some I/O.'''
719+ while True:
720+ in_f = open(os.__file__, 'rb')
721+ stuff = in_f.read(200)
722+ null_f = open(os.devnull, 'wb')
723+ null_f.write(stuff)
724+ time.sleep(random.random() / 1995)
725+ null_f.close()
726+ in_f.close()
727+ thread_has_run.add(threading.current_thread())
728+
729+ def main():
730+ count = 0
731+ for _ in range(40):
732+ new_thread = threading.Thread(target=random_io)
733+ new_thread.daemon = True
734+ new_thread.start()
735+ count += 1
736+ while len(thread_has_run) < count:
737+ time.sleep(0.001)
738+ # Trigger process shutdown
739+ sys.exit(0)
740+
741+ main()
742+ """
743+ rc , out , err = assert_python_ok ('-c' , script )
744+ self .assertFalse (err )
745+
703746 @unittest .skipUnless (hasattr (os , 'fork' ), "needs os.fork()" )
704747 @unittest .skipIf (sys .platform in platforms_to_skip , "due to known OS bug" )
705748 def test_reinit_tls_after_fork (self ):
0 commit comments