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