Issue21291: Popen.wait() needs to be threadsafe for returncode.
(self)
| 1563 | raise exc |
| 1564 | |
| 1565 | def test_threadsafe_wait(self): |
| 1566 | """Issue21291: Popen.wait() needs to be threadsafe for returncode.""" |
| 1567 | proc = subprocess.Popen([sys.executable, '-c', |
| 1568 | 'import time; time.sleep(12)']) |
| 1569 | self.assertEqual(proc.returncode, None) |
| 1570 | results = [] |
| 1571 | |
| 1572 | def kill_proc_timer_thread(): |
| 1573 | results.append(('thread-start-poll-result', proc.poll())) |
| 1574 | # terminate it from the thread and wait for the result. |
| 1575 | proc.kill() |
| 1576 | proc.wait() |
| 1577 | results.append(('thread-after-kill-and-wait', proc.returncode)) |
| 1578 | # this wait should be a no-op given the above. |
| 1579 | proc.wait() |
| 1580 | results.append(('thread-after-second-wait', proc.returncode)) |
| 1581 | |
| 1582 | # This is a timing sensitive test, the failure mode is |
| 1583 | # triggered when both the main thread and this thread are in |
| 1584 | # the wait() call at once. The delay here is to allow the |
| 1585 | # main thread to most likely be blocked in its wait() call. |
| 1586 | t = threading.Timer(0.2, kill_proc_timer_thread) |
| 1587 | t.start() |
| 1588 | |
| 1589 | if mswindows: |
| 1590 | expected_errorcode = 1 |
| 1591 | else: |
| 1592 | # Should be -9 because of the proc.kill() from the thread. |
| 1593 | expected_errorcode = -9 |
| 1594 | |
| 1595 | # Wait for the process to finish; the thread should kill it |
| 1596 | # long before it finishes on its own. Supplying a timeout |
| 1597 | # triggers a different code path for better coverage. |
| 1598 | proc.wait(timeout=support.SHORT_TIMEOUT) |
| 1599 | self.assertEqual(proc.returncode, expected_errorcode, |
| 1600 | msg="unexpected result in wait from main thread") |
| 1601 | |
| 1602 | # This should be a no-op with no change in returncode. |
| 1603 | proc.wait() |
| 1604 | self.assertEqual(proc.returncode, expected_errorcode, |
| 1605 | msg="unexpected result in second main wait.") |
| 1606 | |
| 1607 | t.join() |
| 1608 | # Ensure that all of the thread results are as expected. |
| 1609 | # When a race condition occurs in wait(), the returncode could |
| 1610 | # be set by the wrong thread that doesn't actually have it |
| 1611 | # leading to an incorrect value. |
| 1612 | self.assertEqual([('thread-start-poll-result', None), |
| 1613 | ('thread-after-kill-and-wait', expected_errorcode), |
| 1614 | ('thread-after-second-wait', expected_errorcode)], |
| 1615 | results) |
| 1616 | |
| 1617 | def test_issue8780(self): |
| 1618 | # Ensure that stdout is inherited from the parent |
nothing calls this directly
no test coverage detected