|
1 | | -canary-ok |
| 1 | +: |
| 2 | + x = C() |
| 3 | + ready.release() |
| 4 | + time.sleep(100) |
| 5 | + |
| 6 | + _thread.start_new_thread(waitingThread, ()) |
| 7 | + ready.acquire() # Be sure the other thread is waiting. |
| 8 | + sys.exit(42) |
| 9 | + """) |
| 10 | + self.assertEqual(rc, 42) |
| 11 | + |
| 12 | + def test_finalize_with_trace(self): |
| 13 | + # Issue1733757 |
| 14 | + # Avoid a deadlock when sys.settrace steps into threading._shutdown |
| 15 | + if support.check_sanitizer(thread=True): |
| 16 | + # the thread running `time.sleep(2)` below will still be alive |
| 17 | + # at process exit |
| 18 | + self.skipTest("TSAN would report thread leak") |
| 19 | + |
| 20 | + assert_python_ok("-c", """if 1: |
| 21 | + import sys, threading |
| 22 | + |
| 23 | + # A deadlock-killer, to prevent the |
| 24 | + # testsuite to hang forever |
| 25 | + def killer(): |
| 26 | + import os, time |
| 27 | + time.sleep(2) |
| 28 | + print('program blocked; aborting') |
| 29 | + os._exit(2) |
| 30 | + t = threading.Thread(target=killer) |
| 31 | + t.daemon = True |
| 32 | + t.start() |
| 33 | + |
| 34 | + # This is the trace function |
| 35 | + def func(frame, event, arg): |
| 36 | + threading.current_thread() |
| 37 | + return func |
| 38 | + |
| 39 | + sys.settrace(func) |
| 40 | + """) |
| 41 | + |
| 42 | + def test_join_nondaemon_on_shutdown(self): |
| 43 | + # Issue 1722344 |
| 44 | + # Raising SystemExit skipped threading._shutdown |
| 45 | + rc, out, err = assert_python_ok("-c", """if 1: |
| 46 | + import threading |
| 47 | + from time import sleep |
| 48 | + |
| 49 | + def child(): |
| 50 | + sleep(1) |
| 51 | + # As a non-daemon thread we SHOULD wake up and nothing |
| 52 | + # should be torn down yet |
| 53 | + print("Woke up, sleep function is:", sleep) |
| 54 | + |
| 55 | + threading.Thread(target=child).start() |
| 56 | + raise SystemExit |
| 57 | + """) |
| 58 | + self.assertEqual(out.strip(), |
| 59 | + b"Woke up, sleep function is: <built-in function sleep>") |
| 60 | + self.assertEqual(err, b"") |
| 61 | + |
| 62 | + def test_enumerate_after_join(self): |
| 63 | + # Try hard to trigger #1703448: a thread is still returned in |
| 64 | + # threading.enumerate() after it has been join()ed. |
| 65 | + enum = threading.enumerate |
| 66 | + old_interval = sys.getswitchinterval() |
| 67 | + try: |
| 68 | + for i in range(1, 100): |
| 69 | + support.setswitchinterval(i * 0.0002) |
| 70 | + t = threading.Thread(target=lambda: None) |
| 71 | + t.start() |
| 72 | + t.join() |
| 73 | + l = enum() |
| 74 | + self.assertNotIn(t, l, |
| 75 | + "#1703448 triggered after %d trials: %s" % (i, l)) |
| 76 | + finally: |
| 77 | + sys.setswitchinterval(old_interval) |
| 78 | + |
| 79 | + @support.bigmemtest(size=20, memuse=72*2**20, dry_run=False) |
| 80 | + def test_join_from_multiple_threads(self, size): |
| 81 | + # Thread.join() should be thread-safe |
| 82 | + errors = [] |
| 83 | + |
| 84 | + def worker(): |
| 85 | + time.sleep(0.005) |
| 86 | + |
| 87 | + def joiner(thread): |
| 88 | + try: |
| 89 | + thread.join() |
| 90 | + except Exception as e: |
| 91 | + errors.append(e) |
| 92 | + |
| 93 | + for N in range(2, 20): |
| 94 | + threads = [threading.Thread(target=worker)] |
| 95 | + for i in range(N): |
| 96 | + threads.append(threading.Thread(target=joiner, |
| 97 | + args=(threads[0],))) |
| 98 | + for t in threads: |
| 99 | + t.start() |
| 100 | + time.sleep(0.01) |
| 101 | + for t in threads: |
| 102 | + t.join() |
| 103 | + if errors: |
| 104 | + raise errors[0] |
| 105 | + |
| 106 | + def test_join_with_timeout(self): |
| 107 | + lock = _thread.allocate_lock() |
| 108 | + lock.acquire() |
| 109 | + |
| 110 | + def worker(): |
| 111 | + lock.acquire() |
| 112 | + |
| 113 | + thread = threading.Thread(target=worker) |
| 114 | + thread.start() |
| 115 | + thread.join(timeout=0.01) |
| 116 | + assert thread.is_alive() |
| 117 | + lock.release() |
| 118 | + thread.join() |
| 119 | + assert not thread.is_alive() |
| 120 | + |
| 121 | + def test_no_refcycle_through_target(self): |
| 122 | + class RunSelfFunction(object): |
| 123 | + def __init__(self, should_raise): |
| 124 | + # The links in this refcycle from Thread back to self |
| 125 | + # should be cleaned up when the thread completes. |
| 126 | + self.should_raise = should_raise |
| 127 | + self.thread = threading.Thread(target=self._run, |
| 128 | + args=(self,), |
| 129 | + kwargs={'yet_another':self}) |
| 130 | + self.thread.start() |
| 131 | + |
| 132 | + def _run(self, other_ref, yet_another): |
| 133 | + if self.should_raise: |
| 134 | + raise SystemExit |
| 135 | + |
| 136 | + restore_default_excepthook(self) |
| 137 | + |
| 138 | + cyclic_object = RunSelfFunction(should_raise=False) |
| 139 | + weak_cyclic_object = weakref.ref(cyclic_object) |
| 140 | + cyclic_object.thread.join() |
| 141 | + del cyclic_object |
| 142 | + self.assertIsNone(weak_cyclic_object(), |
| 143 | + msg=('%d references still around' % |
| 144 | + sys.getrefcount(weak_cyclic_object()))) |
| 145 | + |
0 commit comments