|
12 | 12 | import weakref |
13 | 13 | import os |
14 | 14 | import subprocess |
| 15 | +from test.script_helper import assert_python_ok |
15 | 16 |
|
16 | 17 | from test import lock_tests |
17 | 18 |
|
@@ -471,7 +472,6 @@ def test_1_join_on_shutdown(self): |
471 | 472 | """ |
472 | 473 | self._run_and_join(script) |
473 | 474 |
|
474 | | - |
475 | 475 | @unittest.skipUnless(hasattr(os, 'fork'), "needs os.fork()") |
476 | 476 | def test_2_join_in_forked_process(self): |
477 | 477 | # Like the test above, but from a forked interpreter |
@@ -663,6 +663,49 @@ def my_release_save(): |
663 | 663 | output = "end of worker thread\nend of main thread\n" |
664 | 664 | self.assertScriptHasOutput(script, output) |
665 | 665 |
|
| 666 | + def test_6_daemon_threads(self): |
| 667 | + # Check that a daemon thread cannot crash the interpreter on shutdown |
| 668 | + # by manipulating internal structures that are being disposed of in |
| 669 | + # the main thread. |
| 670 | + script = """if True: |
| 671 | + import os |
| 672 | + import random |
| 673 | + import sys |
| 674 | + import time |
| 675 | + import threading |
| 676 | +
|
| 677 | + thread_has_run = set() |
| 678 | +
|
| 679 | + def random_io(): |
| 680 | + '''Loop for a while sleeping random tiny amounts and doing some I/O.''' |
| 681 | + blank = b'x' * 200 |
| 682 | + while True: |
| 683 | + in_f = open(os.__file__, 'r') |
| 684 | + stuff = in_f.read(200) |
| 685 | + null_f = open(os.devnull, 'w') |
| 686 | + null_f.write(stuff) |
| 687 | + time.sleep(random.random() / 1995) |
| 688 | + null_f.close() |
| 689 | + in_f.close() |
| 690 | + thread_has_run.add(threading.current_thread()) |
| 691 | +
|
| 692 | + def main(): |
| 693 | + count = 0 |
| 694 | + for _ in range(40): |
| 695 | + new_thread = threading.Thread(target=random_io) |
| 696 | + new_thread.daemon = True |
| 697 | + new_thread.start() |
| 698 | + count += 1 |
| 699 | + while len(thread_has_run) < count: |
| 700 | + time.sleep(0.001) |
| 701 | + # Trigger process shutdown |
| 702 | + sys.exit(0) |
| 703 | +
|
| 704 | + main() |
| 705 | + """ |
| 706 | + rc, out, err = assert_python_ok('-c', script) |
| 707 | + self.assertFalse(err) |
| 708 | + |
666 | 709 |
|
667 | 710 | class ThreadingExceptionTests(BaseTestCase): |
668 | 711 | # A RuntimeError should be raised if Thread.start() is called |
|
0 commit comments