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