diff options
author | Victor Stinner <victor.stinner@gmail.com> | 2013-12-13 01:17:29 (GMT) |
---|---|---|
committer | Victor Stinner <victor.stinner@gmail.com> | 2013-12-13 01:17:29 (GMT) |
commit | 131051079351859a5451b873667b489e2a9e9f2a (patch) | |
tree | b57298e16e6aabf7ba5a48755cfb3e3f01e57ca5 /Lib/test/test_threading.py | |
parent | da12adac10761439a8a3828958e970db98d7ebc8 (diff) | |
download | cpython-131051079351859a5451b873667b489e2a9e9f2a.zip cpython-131051079351859a5451b873667b489e2a9e9f2a.tar.gz cpython-131051079351859a5451b873667b489e2a9e9f2a.tar.bz2 |
Issue #14432: Generator now clears the borrowed reference to the thread state
Fix a crash when a generator is created in a C thread that is destroyed while
the generator is still used. The issue was that a generator contains a frame,
and the frame kept a reference to the Python state of the destroyed C thread.
The crash occurs when a trace function is setup.
Diffstat (limited to 'Lib/test/test_threading.py')
-rw-r--r-- | Lib/test/test_threading.py | 43 |
1 files changed, 43 insertions, 0 deletions
diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py index 0ebeb39..fee48a3 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -17,6 +17,10 @@ import weakref import os from test.script_helper import assert_python_ok, assert_python_failure import subprocess +try: + import _testcapi +except ImportError: + _testcapi = None from test import lock_tests @@ -769,6 +773,45 @@ class ThreadJoinOnShutdown(BaseTestCase): for t in threads: t.join() + @unittest.skipIf(_testcapi is None, "need _testcapi module") + def test_frame_tstate_tracing(self): + # Issue #14432: Crash when a generator is created in a C thread that is + # destroyed while the generator is still used. The issue was that a + # generator contains a frame, and the frame kept a reference to the + # Python state of the destroyed C thread. The crash occurs when a trace + # function is setup. + + def noop_trace(frame, event, arg): + # no operation + return noop_trace + + def generator(): + while 1: + yield "genereator" + + def callback(): + if callback.gen is None: + callback.gen = generator() + return next(callback.gen) + callback.gen = None + + old_trace = sys.gettrace() + sys.settrace(noop_trace) + try: + # Install a trace function + threading.settrace(noop_trace) + + # Create a generator in a C thread which exits after the call + _testcapi.call_in_temporary_c_thread(callback) + + # Call the generator in a different Python thread, check that the + # generator didn't keep a reference to the destroyed thread state + for test in range(3): + # The trace function is still called here + callback() + finally: + sys.settrace(old_trace) + class ThreadingExceptionTests(BaseTestCase): # A RuntimeError should be raised if Thread.start() is called |