mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	[3.9] bpo-43710: Rollback the 3.9 bpo-42500 fix, it broke the ABI in 3.9.3 (#25179)
This reverts commit 8b795ab554.
It changed the PyThreadState structure size, breaking the ABI in 3.9.3.
			
			
This commit is contained in:
		
							parent
							
								
									de0b2b1330
								
							
						
					
					
						commit
						c7b0feca25
					
				
					 9 changed files with 79 additions and 74 deletions
				
			
		|  | @ -58,7 +58,8 @@ struct _ts { | ||||||
|     /* Borrowed reference to the current frame (it can be NULL) */ |     /* Borrowed reference to the current frame (it can be NULL) */ | ||||||
|     PyFrameObject *frame; |     PyFrameObject *frame; | ||||||
|     int recursion_depth; |     int recursion_depth; | ||||||
|     int recursion_headroom; /* Allow 50 more calls to handle any errors. */ |     char overflowed; /* The stack has overflowed. Allow 50 more calls
 | ||||||
|  |                         to handle the runtime error. */ | ||||||
|     char recursion_critical; /* The current calls must not cause
 |     char recursion_critical; /* The current calls must not cause
 | ||||||
|                                 a stack overflow. */ |                                 a stack overflow. */ | ||||||
|     int stackcheck_counter; |     int stackcheck_counter; | ||||||
|  |  | ||||||
|  | @ -90,8 +90,24 @@ static inline int _Py_EnterRecursiveCall_inline(const char *where) { | ||||||
| 
 | 
 | ||||||
| #define Py_EnterRecursiveCall(where) _Py_EnterRecursiveCall_inline(where) | #define Py_EnterRecursiveCall(where) _Py_EnterRecursiveCall_inline(where) | ||||||
| 
 | 
 | ||||||
|  | /* Compute the "lower-water mark" for a recursion limit. When
 | ||||||
|  |  * Py_LeaveRecursiveCall() is called with a recursion depth below this mark, | ||||||
|  |  * the overflowed flag is reset to 0. */ | ||||||
|  | static inline int _Py_RecursionLimitLowerWaterMark(int limit) { | ||||||
|  |     if (limit > 200) { | ||||||
|  |         return (limit - 50); | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |         return (3 * (limit >> 2)); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static inline void _Py_LeaveRecursiveCall(PyThreadState *tstate)  { | static inline void _Py_LeaveRecursiveCall(PyThreadState *tstate)  { | ||||||
|     tstate->recursion_depth--; |     tstate->recursion_depth--; | ||||||
|  |     int limit = tstate->interp->ceval.recursion_limit; | ||||||
|  |     if (tstate->recursion_depth < _Py_RecursionLimitLowerWaterMark(limit)) { | ||||||
|  |         tstate->overflowed = 0; | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static inline void _Py_LeaveRecursiveCall_inline(void)  { | static inline void _Py_LeaveRecursiveCall_inline(void)  { | ||||||
|  |  | ||||||
|  | @ -1043,7 +1043,7 @@ def gen(): | ||||||
|                 # tstate->recursion_depth is equal to (recursion_limit - 1) |                 # tstate->recursion_depth is equal to (recursion_limit - 1) | ||||||
|                 # and is equal to recursion_limit when _gen_throw() calls |                 # and is equal to recursion_limit when _gen_throw() calls | ||||||
|                 # PyErr_NormalizeException(). |                 # PyErr_NormalizeException(). | ||||||
|                 recurse(setrecursionlimit(depth + 2) - depth) |                 recurse(setrecursionlimit(depth + 2) - depth - 1) | ||||||
|             finally: |             finally: | ||||||
|                 sys.setrecursionlimit(recursionlimit) |                 sys.setrecursionlimit(recursionlimit) | ||||||
|                 print('Done.') |                 print('Done.') | ||||||
|  | @ -1073,54 +1073,6 @@ def test_recursion_normalizing_infinite_exception(self): | ||||||
|                       b'while normalizing an exception', err) |                       b'while normalizing an exception', err) | ||||||
|         self.assertIn(b'Done.', out) |         self.assertIn(b'Done.', out) | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     def test_recursion_in_except_handler(self): |  | ||||||
| 
 |  | ||||||
|         def set_relative_recursion_limit(n): |  | ||||||
|             depth = 1 |  | ||||||
|             while True: |  | ||||||
|                 try: |  | ||||||
|                     sys.setrecursionlimit(depth) |  | ||||||
|                 except RecursionError: |  | ||||||
|                     depth += 1 |  | ||||||
|                 else: |  | ||||||
|                     break |  | ||||||
|             sys.setrecursionlimit(depth+n) |  | ||||||
| 
 |  | ||||||
|         def recurse_in_except(): |  | ||||||
|             try: |  | ||||||
|                 1/0 |  | ||||||
|             except: |  | ||||||
|                 recurse_in_except() |  | ||||||
| 
 |  | ||||||
|         def recurse_after_except(): |  | ||||||
|             try: |  | ||||||
|                 1/0 |  | ||||||
|             except: |  | ||||||
|                 pass |  | ||||||
|             recurse_after_except() |  | ||||||
| 
 |  | ||||||
|         def recurse_in_body_and_except(): |  | ||||||
|             try: |  | ||||||
|                 recurse_in_body_and_except() |  | ||||||
|             except: |  | ||||||
|                 recurse_in_body_and_except() |  | ||||||
| 
 |  | ||||||
|         recursionlimit = sys.getrecursionlimit() |  | ||||||
|         try: |  | ||||||
|             set_relative_recursion_limit(10) |  | ||||||
|             for func in (recurse_in_except, recurse_after_except, recurse_in_body_and_except): |  | ||||||
|                 with self.subTest(func=func): |  | ||||||
|                     try: |  | ||||||
|                         func() |  | ||||||
|                     except RecursionError: |  | ||||||
|                         pass |  | ||||||
|                     else: |  | ||||||
|                         self.fail("Should have raised a RecursionError") |  | ||||||
|         finally: |  | ||||||
|             sys.setrecursionlimit(recursionlimit) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|     @cpython_only |     @cpython_only | ||||||
|     def test_recursion_normalizing_with_no_memory(self): |     def test_recursion_normalizing_with_no_memory(self): | ||||||
|         # Issue #30697. Test that in the abort that occurs when there is no |         # Issue #30697. Test that in the abort that occurs when there is no | ||||||
|  | @ -1157,7 +1109,7 @@ def raiseMemError(): | ||||||
|             except MemoryError as e: |             except MemoryError as e: | ||||||
|                 tb = e.__traceback__ |                 tb = e.__traceback__ | ||||||
|             else: |             else: | ||||||
|                 self.fail("Should have raised a MemoryError") |                 self.fail("Should have raises a MemoryError") | ||||||
|             return traceback.format_tb(tb) |             return traceback.format_tb(tb) | ||||||
| 
 | 
 | ||||||
|         tb1 = raiseMemError() |         tb1 = raiseMemError() | ||||||
|  |  | ||||||
|  | @ -219,7 +219,7 @@ def test_recursionlimit_recovery(self): | ||||||
|         def f(): |         def f(): | ||||||
|             f() |             f() | ||||||
|         try: |         try: | ||||||
|             for depth in (50, 75, 100, 250, 1000): |             for depth in (10, 25, 50, 75, 100, 250, 1000): | ||||||
|                 try: |                 try: | ||||||
|                     sys.setrecursionlimit(depth) |                     sys.setrecursionlimit(depth) | ||||||
|                 except RecursionError: |                 except RecursionError: | ||||||
|  | @ -229,17 +229,17 @@ def f(): | ||||||
| 
 | 
 | ||||||
|                 # Issue #5392: test stack overflow after hitting recursion |                 # Issue #5392: test stack overflow after hitting recursion | ||||||
|                 # limit twice |                 # limit twice | ||||||
|                 with self.assertRaises(RecursionError): |                 self.assertRaises(RecursionError, f) | ||||||
|                     f() |                 self.assertRaises(RecursionError, f) | ||||||
|                 with self.assertRaises(RecursionError): |  | ||||||
|                     f() |  | ||||||
|         finally: |         finally: | ||||||
|             sys.setrecursionlimit(oldlimit) |             sys.setrecursionlimit(oldlimit) | ||||||
| 
 | 
 | ||||||
|     @test.support.cpython_only |     @test.support.cpython_only | ||||||
|     def test_setrecursionlimit_recursion_depth(self): |     def test_setrecursionlimit_recursion_depth(self): | ||||||
|         # Issue #25274: Setting a low recursion limit must be blocked if the |         # Issue #25274: Setting a low recursion limit must be blocked if the | ||||||
|         # current recursion depth is already higher than limit. |         # current recursion depth is already higher than the "lower-water | ||||||
|  |         # mark". Otherwise, it may not be possible anymore to | ||||||
|  |         # reset the overflowed flag to 0. | ||||||
| 
 | 
 | ||||||
|         from _testinternalcapi import get_recursion_depth |         from _testinternalcapi import get_recursion_depth | ||||||
| 
 | 
 | ||||||
|  | @ -260,10 +260,42 @@ def set_recursion_limit_at_depth(depth, limit): | ||||||
|             sys.setrecursionlimit(1000) |             sys.setrecursionlimit(1000) | ||||||
| 
 | 
 | ||||||
|             for limit in (10, 25, 50, 75, 100, 150, 200): |             for limit in (10, 25, 50, 75, 100, 150, 200): | ||||||
|                 set_recursion_limit_at_depth(limit, limit) |                 # formula extracted from _Py_RecursionLimitLowerWaterMark() | ||||||
|  |                 if limit > 200: | ||||||
|  |                     depth = limit - 50 | ||||||
|  |                 else: | ||||||
|  |                     depth = limit * 3 // 4 | ||||||
|  |                 set_recursion_limit_at_depth(depth, limit) | ||||||
|         finally: |         finally: | ||||||
|             sys.setrecursionlimit(oldlimit) |             sys.setrecursionlimit(oldlimit) | ||||||
| 
 | 
 | ||||||
|  |     # The error message is specific to CPython | ||||||
|  |     @test.support.cpython_only | ||||||
|  |     def test_recursionlimit_fatalerror(self): | ||||||
|  |         # A fatal error occurs if a second recursion limit is hit when recovering | ||||||
|  |         # from a first one. | ||||||
|  |         code = textwrap.dedent(""" | ||||||
|  |             import sys | ||||||
|  | 
 | ||||||
|  |             def f(): | ||||||
|  |                 try: | ||||||
|  |                     f() | ||||||
|  |                 except RecursionError: | ||||||
|  |                     f() | ||||||
|  | 
 | ||||||
|  |             sys.setrecursionlimit(%d) | ||||||
|  |             f()""") | ||||||
|  |         with test.support.SuppressCrashReport(): | ||||||
|  |             for i in (50, 1000): | ||||||
|  |                 sub = subprocess.Popen([sys.executable, '-c', code % i], | ||||||
|  |                     stderr=subprocess.PIPE) | ||||||
|  |                 err = sub.communicate()[1] | ||||||
|  |                 self.assertTrue(sub.returncode, sub.returncode) | ||||||
|  |                 self.assertIn( | ||||||
|  |                     b"Fatal Python error: _Py_CheckRecursiveCall: " | ||||||
|  |                     b"Cannot recover from stack overflow", | ||||||
|  |                     err) | ||||||
|  | 
 | ||||||
|     def test_getwindowsversion(self): |     def test_getwindowsversion(self): | ||||||
|         # Raise SkipTest if sys doesn't have getwindowsversion attribute |         # Raise SkipTest if sys doesn't have getwindowsversion attribute | ||||||
|         test.support.get_attribute(sys, "getwindowsversion") |         test.support.get_attribute(sys, "getwindowsversion") | ||||||
|  |  | ||||||
|  | @ -0,0 +1,4 @@ | ||||||
|  | Reverted the fix for https://bugs.python.org/issue42500 as it changed the | ||||||
|  | PyThreadState struct size and broke the 3.9.x ABI in the 3.9.3 release | ||||||
|  | (visible on 32-bit platforms using binaries compiled using an earlier | ||||||
|  | version of Python 3.9.x headers). | ||||||
|  | @ -793,23 +793,24 @@ _Py_CheckRecursiveCall(PyThreadState *tstate, const char *where) | ||||||
|         _Py_CheckRecursionLimit = recursion_limit; |         _Py_CheckRecursionLimit = recursion_limit; | ||||||
|     } |     } | ||||||
| #endif | #endif | ||||||
|     if (tstate->recursion_headroom) { |     if (tstate->recursion_critical) | ||||||
|  |         /* Somebody asked that we don't check for recursion. */ | ||||||
|  |         return 0; | ||||||
|  |     if (tstate->overflowed) { | ||||||
|         if (tstate->recursion_depth > recursion_limit + 50) { |         if (tstate->recursion_depth > recursion_limit + 50) { | ||||||
|             /* Overflowing while handling an overflow. Give up. */ |             /* Overflowing while handling an overflow. Give up. */ | ||||||
|             Py_FatalError("Cannot recover from stack overflow."); |             Py_FatalError("Cannot recover from stack overflow."); | ||||||
|         } |         } | ||||||
|  |         return 0; | ||||||
|     } |     } | ||||||
|     else { |  | ||||||
|     if (tstate->recursion_depth > recursion_limit) { |     if (tstate->recursion_depth > recursion_limit) { | ||||||
|             tstate->recursion_headroom++; |         --tstate->recursion_depth; | ||||||
|  |         tstate->overflowed = 1; | ||||||
|         _PyErr_Format(tstate, PyExc_RecursionError, |         _PyErr_Format(tstate, PyExc_RecursionError, | ||||||
|                       "maximum recursion depth exceeded%s", |                       "maximum recursion depth exceeded%s", | ||||||
|                       where); |                       where); | ||||||
|             tstate->recursion_headroom--; |  | ||||||
|             --tstate->recursion_depth; |  | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|     } |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -290,14 +290,12 @@ _PyErr_NormalizeException(PyThreadState *tstate, PyObject **exc, | ||||||
|                           PyObject **val, PyObject **tb) |                           PyObject **val, PyObject **tb) | ||||||
| { | { | ||||||
|     int recursion_depth = 0; |     int recursion_depth = 0; | ||||||
|     tstate->recursion_headroom++; |  | ||||||
|     PyObject *type, *value, *initial_tb; |     PyObject *type, *value, *initial_tb; | ||||||
| 
 | 
 | ||||||
|   restart: |   restart: | ||||||
|     type = *exc; |     type = *exc; | ||||||
|     if (type == NULL) { |     if (type == NULL) { | ||||||
|         /* There was no exception, so nothing to do. */ |         /* There was no exception, so nothing to do. */ | ||||||
|         tstate->recursion_headroom--; |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -349,7 +347,6 @@ _PyErr_NormalizeException(PyThreadState *tstate, PyObject **exc, | ||||||
|     } |     } | ||||||
|     *exc = type; |     *exc = type; | ||||||
|     *val = value; |     *val = value; | ||||||
|     tstate->recursion_headroom--; |  | ||||||
|     return; |     return; | ||||||
| 
 | 
 | ||||||
|   error: |   error: | ||||||
|  |  | ||||||
|  | @ -576,7 +576,7 @@ new_threadstate(PyInterpreterState *interp, int init) | ||||||
| 
 | 
 | ||||||
|     tstate->frame = NULL; |     tstate->frame = NULL; | ||||||
|     tstate->recursion_depth = 0; |     tstate->recursion_depth = 0; | ||||||
|     tstate->recursion_headroom = 0; |     tstate->overflowed = 0; | ||||||
|     tstate->recursion_critical = 0; |     tstate->recursion_critical = 0; | ||||||
|     tstate->stackcheck_counter = 0; |     tstate->stackcheck_counter = 0; | ||||||
|     tstate->tracing = 0; |     tstate->tracing = 0; | ||||||
|  |  | ||||||
|  | @ -1160,6 +1160,7 @@ static PyObject * | ||||||
| sys_setrecursionlimit_impl(PyObject *module, int new_limit) | sys_setrecursionlimit_impl(PyObject *module, int new_limit) | ||||||
| /*[clinic end generated code: output=35e1c64754800ace input=b0f7a23393924af3]*/ | /*[clinic end generated code: output=35e1c64754800ace input=b0f7a23393924af3]*/ | ||||||
| { | { | ||||||
|  |     int mark; | ||||||
|     PyThreadState *tstate = _PyThreadState_GET(); |     PyThreadState *tstate = _PyThreadState_GET(); | ||||||
| 
 | 
 | ||||||
|     if (new_limit < 1) { |     if (new_limit < 1) { | ||||||
|  | @ -1177,7 +1178,8 @@ sys_setrecursionlimit_impl(PyObject *module, int new_limit) | ||||||
|        Reject too low new limit if the current recursion depth is higher than |        Reject too low new limit if the current recursion depth is higher than | ||||||
|        the new low-water mark. Otherwise it may not be possible anymore to |        the new low-water mark. Otherwise it may not be possible anymore to | ||||||
|        reset the overflowed flag to 0. */ |        reset the overflowed flag to 0. */ | ||||||
|     if (tstate->recursion_depth >= new_limit) { |     mark = _Py_RecursionLimitLowerWaterMark(new_limit); | ||||||
|  |     if (tstate->recursion_depth >= mark) { | ||||||
|         _PyErr_Format(tstate, PyExc_RecursionError, |         _PyErr_Format(tstate, PyExc_RecursionError, | ||||||
|                       "cannot set the recursion limit to %i at " |                       "cannot set the recursion limit to %i at " | ||||||
|                       "the recursion depth %i: the limit is too low", |                       "the recursion depth %i: the limit is too low", | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Gregory P. Smith
						Gregory P. Smith