mirror of
https://github.com/python/cpython.git
synced 2025-08-04 00:48:58 +00:00
[3.11] GH-99729: Unlink frames before clearing them (#100047)
This commit is contained in:
parent
3fae04b10e
commit
2182a71eed
4 changed files with 56 additions and 10 deletions
|
@ -2,11 +2,13 @@ import gc
|
|||
import re
|
||||
import sys
|
||||
import textwrap
|
||||
import threading
|
||||
import types
|
||||
import unittest
|
||||
import weakref
|
||||
|
||||
from test import support
|
||||
from test.support import threading_helper
|
||||
from test.support.script_helper import assert_python_ok
|
||||
|
||||
|
||||
|
@ -325,6 +327,46 @@ class TestIncompleteFrameAreInvisible(unittest.TestCase):
|
|||
if old_enabled:
|
||||
gc.enable()
|
||||
|
||||
@support.cpython_only
|
||||
@threading_helper.requires_working_threading()
|
||||
def test_sneaky_frame_object_teardown(self):
|
||||
|
||||
class SneakyDel:
|
||||
def __del__(self):
|
||||
"""
|
||||
Stash a reference to the entire stack for walking later.
|
||||
|
||||
It may look crazy, but you'd be surprised how common this is
|
||||
when using a test runner (like pytest). The typical recipe is:
|
||||
ResourceWarning + -Werror + a custom sys.unraisablehook.
|
||||
"""
|
||||
nonlocal sneaky_frame_object
|
||||
sneaky_frame_object = sys._getframe()
|
||||
|
||||
class SneakyThread(threading.Thread):
|
||||
"""
|
||||
A separate thread isn't needed to make this code crash, but it does
|
||||
make crashes more consistent, since it means sneaky_frame_object is
|
||||
backed by freed memory after the thread completes!
|
||||
"""
|
||||
|
||||
def run(self):
|
||||
"""Run SneakyDel.__del__ as this frame is popped."""
|
||||
ref = SneakyDel()
|
||||
|
||||
sneaky_frame_object = None
|
||||
t = SneakyThread()
|
||||
t.start()
|
||||
t.join()
|
||||
# sneaky_frame_object can be anything, really, but it's crucial that
|
||||
# SneakyThread.run's frame isn't anywhere on the stack while it's being
|
||||
# torn down:
|
||||
self.assertIsNotNone(sneaky_frame_object)
|
||||
while sneaky_frame_object is not None:
|
||||
self.assertIsNot(
|
||||
sneaky_frame_object.f_code, SneakyThread.run.__code__
|
||||
)
|
||||
sneaky_frame_object = sneaky_frame_object.f_back
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue