|
14 | 14 | from test import support |
15 | 15 | from test.support import import_helper |
16 | 16 | from test.support import os_helper |
| 17 | +from test.support import threading_helper |
17 | 18 | from test.support import warnings_helper |
18 | 19 | from test.support import force_not_colorized |
19 | 20 | from test.support.script_helper import assert_python_ok, assert_python_failure |
@@ -1621,6 +1622,100 @@ def test_release_lock_no_lock(self): |
1621 | 1622 | ): |
1622 | 1623 | c_warnings._release_lock() |
1623 | 1624 |
|
| 1625 | + @support.cpython_only |
| 1626 | + @unittest.skipUnless(support.has_fork_support, 'requires working os.fork') |
| 1627 | + @threading_helper.requires_working_threading() |
| 1628 | + def test_fork_other_thread_holds_lock(self): |
| 1629 | + code = textwrap.dedent(''' |
| 1630 | + import _warnings |
| 1631 | + import os |
| 1632 | + import threading |
| 1633 | + import warnings |
| 1634 | + from test import support |
| 1635 | +
|
| 1636 | + parent_pid = os.getpid() |
| 1637 | + read_fd, write_fd = os.pipe() |
| 1638 | + local = threading.local() |
| 1639 | + ready = threading.Event() |
| 1640 | + release = threading.Event() |
| 1641 | + warnings.simplefilter('ignore', DeprecationWarning) |
| 1642 | + warnings.simplefilter('ignore', ResourceWarning) |
| 1643 | +
|
| 1644 | + class WarnOnDelete: |
| 1645 | + def __del__(self): |
| 1646 | + if os.getpid() != parent_pid: |
| 1647 | + # Even an ignored warning must acquire the lock. |
| 1648 | + warnings.warn('child cleanup', ResourceWarning) |
| 1649 | + os.write(write_fd, b'finalized') |
| 1650 | +
|
| 1651 | + def worker(): |
| 1652 | + local.obj = WarnOnDelete() |
| 1653 | + _warnings._acquire_lock() |
| 1654 | + _warnings._acquire_lock() |
| 1655 | + try: |
| 1656 | + ready.set() |
| 1657 | + release.wait() |
| 1658 | + finally: |
| 1659 | + _warnings._release_lock() |
| 1660 | + _warnings._release_lock() |
| 1661 | +
|
| 1662 | + thread = threading.Thread(target=worker) |
| 1663 | + thread.start() |
| 1664 | + # Release the worker before fork's parent-side warning attempts |
| 1665 | + # to acquire the warnings lock. |
| 1666 | + os.register_at_fork(after_in_parent=release.set) |
| 1667 | + try: |
| 1668 | + assert ready.wait(support.LONG_TIMEOUT) |
| 1669 | + pid = os.fork() |
| 1670 | + if pid == 0: |
| 1671 | + # The finalizer must run before os.fork() returns. |
| 1672 | + os._exit(0) |
| 1673 | + support.wait_process(pid, exitcode=0) |
| 1674 | + finally: |
| 1675 | + release.set() |
| 1676 | + thread.join() |
| 1677 | + os.close(write_fd) |
| 1678 | + assert os.read(read_fd, 100) == b'finalized' |
| 1679 | + os.close(read_fd) |
| 1680 | + ''') |
| 1681 | + assert_python_ok('-c', code) |
| 1682 | + |
| 1683 | + @support.cpython_only |
| 1684 | + @unittest.skipUnless(support.has_fork_support, 'requires working os.fork') |
| 1685 | + def test_fork_current_thread_holds_lock(self): |
| 1686 | + code = textwrap.dedent(''' |
| 1687 | + import _warnings |
| 1688 | + import os |
| 1689 | + import warnings |
| 1690 | + from test import support |
| 1691 | +
|
| 1692 | + warnings.simplefilter('ignore') |
| 1693 | + for _ in range(3): |
| 1694 | + _warnings._acquire_lock() |
| 1695 | + pid = os.fork() |
| 1696 | + try: |
| 1697 | + # A warning must not change the inherited recursion depth. |
| 1698 | + warnings.warn('after fork') |
| 1699 | + for _ in range(3): |
| 1700 | + _warnings._release_lock() |
| 1701 | + try: |
| 1702 | + _warnings._release_lock() |
| 1703 | + except RuntimeError: |
| 1704 | + pass |
| 1705 | + else: |
| 1706 | + raise AssertionError('unexpected recursion depth') |
| 1707 | + _warnings._acquire_lock() |
| 1708 | + _warnings._release_lock() |
| 1709 | + except BaseException: |
| 1710 | + if pid == 0: |
| 1711 | + os._exit(1) |
| 1712 | + raise |
| 1713 | + if pid == 0: |
| 1714 | + os._exit(0) |
| 1715 | + support.wait_process(pid, exitcode=0) |
| 1716 | + ''') |
| 1717 | + assert_python_ok('-c', code) |
| 1718 | + |
1624 | 1719 |
|
1625 | 1720 | class _DeprecatedTest(BaseTest, unittest.TestCase): |
1626 | 1721 |
|
|
0 commit comments