mirror of
https://github.com/python/cpython.git
synced 2025-12-08 06:10:17 +00:00
[3.14] gh-139653: Add PyUnstable_ThreadState_SetStackProtection() (GH-139668) (#141661)
Co-authored-by: Rok Mandeljc <rok.mandeljc@gmail.com> Co-authored-by: Mark Shannon <mark@hotpy.org> Co-authored-by: Victor Stinner <vstinner@python.org>
This commit is contained in:
parent
11e3fc9636
commit
32a38a2523
11 changed files with 9871 additions and 9561 deletions
|
|
@ -984,6 +984,9 @@ because the :ref:`call protocol <call>` takes care of recursion handling.
|
||||||
be concatenated to the :exc:`RecursionError` message caused by the recursion
|
be concatenated to the :exc:`RecursionError` message caused by the recursion
|
||||||
depth limit.
|
depth limit.
|
||||||
|
|
||||||
|
.. seealso::
|
||||||
|
The :c:func:`PyUnstable_ThreadState_SetStackProtection` function.
|
||||||
|
|
||||||
.. versionchanged:: 3.9
|
.. versionchanged:: 3.9
|
||||||
This function is now also available in the :ref:`limited API <limited-c-api>`.
|
This function is now also available in the :ref:`limited API <limited-c-api>`.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1531,6 +1531,63 @@ All of the following functions must be called after :c:func:`Py_Initialize`.
|
||||||
.. versionadded:: 3.11
|
.. versionadded:: 3.11
|
||||||
|
|
||||||
|
|
||||||
|
.. c:function:: int PyUnstable_ThreadState_SetStackProtection(PyThreadState *tstate, void *stack_start_addr, size_t stack_size)
|
||||||
|
|
||||||
|
Set the stack protection start address and stack protection size
|
||||||
|
of a Python thread state.
|
||||||
|
|
||||||
|
On success, return ``0``.
|
||||||
|
On failure, set an exception and return ``-1``.
|
||||||
|
|
||||||
|
CPython implements :ref:`recursion control <recursion>` for C code by raising
|
||||||
|
:py:exc:`RecursionError` when it notices that the machine execution stack is close
|
||||||
|
to overflow. See for example the :c:func:`Py_EnterRecursiveCall` function.
|
||||||
|
For this, it needs to know the location of the current thread's stack, which it
|
||||||
|
normally gets from the operating system.
|
||||||
|
When the stack is changed, for example using context switching techniques like the
|
||||||
|
Boost library's ``boost::context``, you must call
|
||||||
|
:c:func:`~PyUnstable_ThreadState_SetStackProtection` to inform CPython of the change.
|
||||||
|
|
||||||
|
Call :c:func:`~PyUnstable_ThreadState_SetStackProtection` either before
|
||||||
|
or after changing the stack.
|
||||||
|
Do not call any other Python C API between the call and the stack
|
||||||
|
change.
|
||||||
|
|
||||||
|
See :c:func:`PyUnstable_ThreadState_ResetStackProtection` for undoing this operation.
|
||||||
|
|
||||||
|
.. versionadded:: next
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
This function was added in a bugfix release, and
|
||||||
|
extensions that use it will be incompatible with Python 3.14.0.
|
||||||
|
Most packaging tools for Python are not able to handle this
|
||||||
|
incompatibility automatically, and will need explicit configuration.
|
||||||
|
When using PyPA standards (wheels and source distributions),
|
||||||
|
specify ``Requires-Python: != 3.14.0.*`` in
|
||||||
|
`core metadata <https://packaging.python.org/en/latest/specifications/core-metadata/#requires-python>`_.
|
||||||
|
|
||||||
|
|
||||||
|
.. c:function:: void PyUnstable_ThreadState_ResetStackProtection(PyThreadState *tstate)
|
||||||
|
|
||||||
|
Reset the stack protection start address and stack protection size
|
||||||
|
of a Python thread state to the operating system defaults.
|
||||||
|
|
||||||
|
See :c:func:`PyUnstable_ThreadState_SetStackProtection` for an explanation.
|
||||||
|
|
||||||
|
.. versionadded:: next
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
This function was added in a bugfix release, and
|
||||||
|
extensions that use it will be incompatible with Python 3.14.0.
|
||||||
|
Most packaging tools for Python are not able to handle this
|
||||||
|
incompatibility automatically, and will need explicit configuration.
|
||||||
|
When using PyPA standards (wheels and source distributions),
|
||||||
|
specify ``Requires-Python: != 3.14.0.*`` in
|
||||||
|
`core metadata <https://packaging.python.org/en/latest/specifications/core-metadata/#requires-python>`_.
|
||||||
|
|
||||||
|
|
||||||
.. c:function:: PyInterpreterState* PyInterpreterState_Get(void)
|
.. c:function:: PyInterpreterState* PyInterpreterState_Get(void)
|
||||||
|
|
||||||
Get the current interpreter.
|
Get the current interpreter.
|
||||||
|
|
|
||||||
19135
Doc/data/python3.14.abi
19135
Doc/data/python3.14.abi
File diff suppressed because it is too large
Load diff
|
|
@ -3435,3 +3435,13 @@ Changes in the C API
|
||||||
functions on Python 3.13 and older.
|
functions on Python 3.13 and older.
|
||||||
|
|
||||||
.. _pythoncapi-compat project: https://github.com/python/pythoncapi-compat/
|
.. _pythoncapi-compat project: https://github.com/python/pythoncapi-compat/
|
||||||
|
|
||||||
|
|
||||||
|
Notable changes in 3.14.1
|
||||||
|
=========================
|
||||||
|
|
||||||
|
* Add :c:func:`PyUnstable_ThreadState_SetStackProtection` and
|
||||||
|
:c:func:`PyUnstable_ThreadState_ResetStackProtection` functions to set
|
||||||
|
the stack protection base address and stack protection size of a Python
|
||||||
|
thread state.
|
||||||
|
(Contributed by Victor Stinner in :gh:`139653`.)
|
||||||
|
|
|
||||||
|
|
@ -243,6 +243,18 @@ PyAPI_FUNC(int) PyGILState_Check(void);
|
||||||
*/
|
*/
|
||||||
PyAPI_FUNC(PyObject*) _PyThread_CurrentFrames(void);
|
PyAPI_FUNC(PyObject*) _PyThread_CurrentFrames(void);
|
||||||
|
|
||||||
|
// Set the stack protection start address and stack protection size
|
||||||
|
// of a Python thread state
|
||||||
|
PyAPI_FUNC(int) PyUnstable_ThreadState_SetStackProtection(
|
||||||
|
PyThreadState *tstate,
|
||||||
|
void *stack_start_addr, // Stack start address
|
||||||
|
size_t stack_size); // Stack size (in bytes)
|
||||||
|
|
||||||
|
// Reset the stack protection start address and stack protection size
|
||||||
|
// of a Python thread state
|
||||||
|
PyAPI_FUNC(void) PyUnstable_ThreadState_ResetStackProtection(
|
||||||
|
PyThreadState *tstate);
|
||||||
|
|
||||||
/* Routines for advanced debuggers, requested by David Beazley.
|
/* Routines for advanced debuggers, requested by David Beazley.
|
||||||
Don't use unless you know what you are doing! */
|
Don't use unless you know what you are doing! */
|
||||||
PyAPI_FUNC(PyInterpreterState *) PyInterpreterState_Main(void);
|
PyAPI_FUNC(PyInterpreterState *) PyInterpreterState_Main(void);
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,12 @@ extern const char* _Py_SourceAsString(
|
||||||
# define _PyOS_STACK_MARGIN_SHIFT (_PyOS_LOG2_STACK_MARGIN + 2)
|
# define _PyOS_STACK_MARGIN_SHIFT (_PyOS_LOG2_STACK_MARGIN + 2)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef _Py_THREAD_SANITIZER
|
||||||
|
# define _PyOS_MIN_STACK_SIZE (_PyOS_STACK_MARGIN_BYTES * 6)
|
||||||
|
#else
|
||||||
|
# define _PyOS_MIN_STACK_SIZE (_PyOS_STACK_MARGIN_BYTES * 3)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,10 @@ typedef struct _PyThreadStateImpl {
|
||||||
Py_ssize_t reftotal; // this thread's total refcount operations
|
Py_ssize_t reftotal; // this thread's total refcount operations
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// PyUnstable_ThreadState_ResetStackProtection() values
|
||||||
|
uintptr_t c_stack_init_base;
|
||||||
|
uintptr_t c_stack_init_top;
|
||||||
|
|
||||||
} _PyThreadStateImpl;
|
} _PyThreadStateImpl;
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
Add :c:func:`PyUnstable_ThreadState_SetStackProtection` and
|
||||||
|
:c:func:`PyUnstable_ThreadState_ResetStackProtection` functions to set the
|
||||||
|
stack protection base address and stack protection size of a Python thread
|
||||||
|
state. Patch by Victor Stinner.
|
||||||
|
|
@ -2408,6 +2408,58 @@ set_vectorcall_nop(PyObject *self, PyObject *func)
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
check_threadstate_set_stack_protection(PyThreadState *tstate,
|
||||||
|
void *start, size_t size)
|
||||||
|
{
|
||||||
|
assert(PyUnstable_ThreadState_SetStackProtection(tstate, start, size) == 0);
|
||||||
|
assert(!PyErr_Occurred());
|
||||||
|
|
||||||
|
_PyThreadStateImpl *ts = (_PyThreadStateImpl *)tstate;
|
||||||
|
assert(ts->c_stack_top == (uintptr_t)start + size);
|
||||||
|
assert(ts->c_stack_hard_limit <= ts->c_stack_soft_limit);
|
||||||
|
assert(ts->c_stack_soft_limit < ts->c_stack_top);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
test_threadstate_set_stack_protection(PyObject *self, PyObject *Py_UNUSED(args))
|
||||||
|
{
|
||||||
|
PyThreadState *tstate = PyThreadState_GET();
|
||||||
|
_PyThreadStateImpl *ts = (_PyThreadStateImpl *)tstate;
|
||||||
|
assert(!PyErr_Occurred());
|
||||||
|
|
||||||
|
uintptr_t init_base = ts->c_stack_init_base;
|
||||||
|
size_t init_top = ts->c_stack_init_top;
|
||||||
|
|
||||||
|
// Test the minimum stack size
|
||||||
|
size_t size = _PyOS_MIN_STACK_SIZE;
|
||||||
|
void *start = (void*)(_Py_get_machine_stack_pointer() - size);
|
||||||
|
check_threadstate_set_stack_protection(tstate, start, size);
|
||||||
|
|
||||||
|
// Test a larger size
|
||||||
|
size = 7654321;
|
||||||
|
assert(size > _PyOS_MIN_STACK_SIZE);
|
||||||
|
start = (void*)(_Py_get_machine_stack_pointer() - size);
|
||||||
|
check_threadstate_set_stack_protection(tstate, start, size);
|
||||||
|
|
||||||
|
// Test invalid size (too small)
|
||||||
|
size = 5;
|
||||||
|
start = (void*)(_Py_get_machine_stack_pointer() - size);
|
||||||
|
assert(PyUnstable_ThreadState_SetStackProtection(tstate, start, size) == -1);
|
||||||
|
assert(PyErr_ExceptionMatches(PyExc_ValueError));
|
||||||
|
PyErr_Clear();
|
||||||
|
|
||||||
|
// Test PyUnstable_ThreadState_ResetStackProtection()
|
||||||
|
PyUnstable_ThreadState_ResetStackProtection(tstate);
|
||||||
|
assert(ts->c_stack_init_base == init_base);
|
||||||
|
assert(ts->c_stack_init_top == init_top);
|
||||||
|
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static PyMethodDef module_functions[] = {
|
static PyMethodDef module_functions[] = {
|
||||||
{"get_configs", get_configs, METH_NOARGS},
|
{"get_configs", get_configs, METH_NOARGS},
|
||||||
{"get_recursion_depth", get_recursion_depth, METH_NOARGS},
|
{"get_recursion_depth", get_recursion_depth, METH_NOARGS},
|
||||||
|
|
@ -2516,6 +2568,8 @@ static PyMethodDef module_functions[] = {
|
||||||
{"emscripten_set_up_async_input_device", emscripten_set_up_async_input_device, METH_NOARGS},
|
{"emscripten_set_up_async_input_device", emscripten_set_up_async_input_device, METH_NOARGS},
|
||||||
#endif
|
#endif
|
||||||
{"set_vectorcall_nop", set_vectorcall_nop, METH_O},
|
{"set_vectorcall_nop", set_vectorcall_nop, METH_O},
|
||||||
|
{"test_threadstate_set_stack_protection",
|
||||||
|
test_threadstate_set_stack_protection, METH_NOARGS},
|
||||||
{NULL, NULL} /* sentinel */
|
{NULL, NULL} /* sentinel */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
144
Python/ceval.c
144
Python/ceval.c
|
|
@ -436,24 +436,26 @@ int pthread_attr_destroy(pthread_attr_t *a)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static void
|
||||||
void
|
hardware_stack_limits(uintptr_t *base, uintptr_t *top)
|
||||||
_Py_InitializeRecursionLimits(PyThreadState *tstate)
|
|
||||||
{
|
{
|
||||||
_PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
ULONG_PTR low, high;
|
ULONG_PTR low, high;
|
||||||
GetCurrentThreadStackLimits(&low, &high);
|
GetCurrentThreadStackLimits(&low, &high);
|
||||||
_tstate->c_stack_top = (uintptr_t)high;
|
*top = (uintptr_t)high;
|
||||||
ULONG guarantee = 0;
|
ULONG guarantee = 0;
|
||||||
SetThreadStackGuarantee(&guarantee);
|
SetThreadStackGuarantee(&guarantee);
|
||||||
_tstate->c_stack_hard_limit = ((uintptr_t)low) + guarantee + _PyOS_STACK_MARGIN_BYTES;
|
*base = (uintptr_t)low + guarantee;
|
||||||
_tstate->c_stack_soft_limit = _tstate->c_stack_hard_limit + _PyOS_STACK_MARGIN_BYTES;
|
#elif defined(__APPLE__)
|
||||||
|
pthread_t this_thread = pthread_self();
|
||||||
|
void *stack_addr = pthread_get_stackaddr_np(this_thread); // top of the stack
|
||||||
|
size_t stack_size = pthread_get_stacksize_np(this_thread);
|
||||||
|
*top = (uintptr_t)stack_addr;
|
||||||
|
*base = ((uintptr_t)stack_addr) - stack_size;
|
||||||
#else
|
#else
|
||||||
uintptr_t here_addr = _Py_get_machine_stack_pointer();
|
/// XXX musl supports HAVE_PTHRED_GETATTR_NP, but the resulting stack size
|
||||||
/// XXX musl supports HAVE_PTHRED_GETATTR_NP, but the resulting stack size
|
/// (on alpine at least) is much smaller than expected and imposes undue limits
|
||||||
/// (on alpine at least) is much smaller than expected and imposes undue limits
|
/// compared to the old stack size estimation. (We assume musl is not glibc.)
|
||||||
/// compared to the old stack size estimation. (We assume musl is not glibc.)
|
|
||||||
# if defined(HAVE_PTHREAD_GETATTR_NP) && !defined(_AIX) && \
|
# if defined(HAVE_PTHREAD_GETATTR_NP) && !defined(_AIX) && \
|
||||||
!defined(__NetBSD__) && (defined(__GLIBC__) || !defined(__linux__))
|
!defined(__NetBSD__) && (defined(__GLIBC__) || !defined(__linux__))
|
||||||
size_t stack_size, guard_size;
|
size_t stack_size, guard_size;
|
||||||
|
|
@ -466,38 +468,106 @@ _Py_InitializeRecursionLimits(PyThreadState *tstate)
|
||||||
err |= pthread_attr_destroy(&attr);
|
err |= pthread_attr_destroy(&attr);
|
||||||
}
|
}
|
||||||
if (err == 0) {
|
if (err == 0) {
|
||||||
uintptr_t base = ((uintptr_t)stack_addr) + guard_size;
|
*base = ((uintptr_t)stack_addr) + guard_size;
|
||||||
uintptr_t top = base + stack_size;
|
*top = (uintptr_t)stack_addr + stack_size;
|
||||||
# ifdef _Py_THREAD_SANITIZER
|
|
||||||
// Thread sanitizer crashes if we use a bit more than half the stack.
|
|
||||||
# if _Py_STACK_GROWS_DOWN
|
|
||||||
base += stack_size / 2;
|
|
||||||
# else
|
|
||||||
top -= stack_size / 2;
|
|
||||||
# endif
|
|
||||||
# endif
|
|
||||||
# if _Py_STACK_GROWS_DOWN
|
|
||||||
_tstate->c_stack_top = top;
|
|
||||||
_tstate->c_stack_hard_limit = base + _PyOS_STACK_MARGIN_BYTES;
|
|
||||||
_tstate->c_stack_soft_limit = base + _PyOS_STACK_MARGIN_BYTES * 2;
|
|
||||||
assert(_tstate->c_stack_soft_limit < here_addr);
|
|
||||||
assert(here_addr < _tstate->c_stack_top);
|
|
||||||
# else
|
|
||||||
_tstate->c_stack_top = base;
|
|
||||||
_tstate->c_stack_hard_limit = top - _PyOS_STACK_MARGIN_BYTES;
|
|
||||||
_tstate->c_stack_soft_limit = top - _PyOS_STACK_MARGIN_BYTES * 2;
|
|
||||||
assert(here_addr > base);
|
|
||||||
assert(here_addr < _tstate->c_stack_soft_limit);
|
|
||||||
# endif
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
# endif
|
# endif
|
||||||
_tstate->c_stack_top = _Py_SIZE_ROUND_UP(here_addr, 4096);
|
uintptr_t here_addr = _Py_get_machine_stack_pointer();
|
||||||
_tstate->c_stack_soft_limit = _tstate->c_stack_top - Py_C_STACK_SIZE;
|
uintptr_t top_addr = _Py_SIZE_ROUND_UP(here_addr, 4096);
|
||||||
_tstate->c_stack_hard_limit = _tstate->c_stack_top - (Py_C_STACK_SIZE + _PyOS_STACK_MARGIN_BYTES);
|
*top = top_addr;
|
||||||
|
*base = top_addr - Py_C_STACK_SIZE;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
tstate_set_stack(PyThreadState *tstate,
|
||||||
|
uintptr_t base, uintptr_t top)
|
||||||
|
{
|
||||||
|
assert(base < top);
|
||||||
|
assert((top - base) >= _PyOS_MIN_STACK_SIZE);
|
||||||
|
|
||||||
|
#ifdef _Py_THREAD_SANITIZER
|
||||||
|
// Thread sanitizer crashes if we use more than half the stack.
|
||||||
|
uintptr_t stacksize = top - base;
|
||||||
|
# if _Py_STACK_GROWS_DOWN
|
||||||
|
base += stacksize / 2;
|
||||||
|
# else
|
||||||
|
top -= stacksize / 2;
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
_PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
|
||||||
|
#if _Py_STACK_GROWS_DOWN
|
||||||
|
_tstate->c_stack_top = top;
|
||||||
|
_tstate->c_stack_hard_limit = base + _PyOS_STACK_MARGIN_BYTES;
|
||||||
|
_tstate->c_stack_soft_limit = base + _PyOS_STACK_MARGIN_BYTES * 2;
|
||||||
|
# ifndef NDEBUG
|
||||||
|
// Sanity checks
|
||||||
|
_PyThreadStateImpl *ts = (_PyThreadStateImpl *)tstate;
|
||||||
|
assert(ts->c_stack_hard_limit <= ts->c_stack_soft_limit);
|
||||||
|
assert(ts->c_stack_soft_limit < ts->c_stack_top);
|
||||||
|
# endif
|
||||||
|
#else
|
||||||
|
_tstate->c_stack_top = base;
|
||||||
|
_tstate->c_stack_hard_limit = top - _PyOS_STACK_MARGIN_BYTES;
|
||||||
|
_tstate->c_stack_soft_limit = top - _PyOS_STACK_MARGIN_BYTES * 2;
|
||||||
|
# ifndef NDEBUG
|
||||||
|
// Sanity checks
|
||||||
|
_PyThreadStateImpl *ts = (_PyThreadStateImpl *)tstate;
|
||||||
|
assert(ts->c_stack_hard_limit >= ts->c_stack_soft_limit);
|
||||||
|
assert(ts->c_stack_soft_limit > ts->c_stack_top);
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
_Py_InitializeRecursionLimits(PyThreadState *tstate)
|
||||||
|
{
|
||||||
|
uintptr_t base, top;
|
||||||
|
hardware_stack_limits(&base, &top);
|
||||||
|
assert(top != 0);
|
||||||
|
|
||||||
|
tstate_set_stack(tstate, base, top);
|
||||||
|
_PyThreadStateImpl *ts = (_PyThreadStateImpl *)tstate;
|
||||||
|
ts->c_stack_init_base = base;
|
||||||
|
ts->c_stack_init_top = top;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
PyUnstable_ThreadState_SetStackProtection(PyThreadState *tstate,
|
||||||
|
void *stack_start_addr, size_t stack_size)
|
||||||
|
{
|
||||||
|
if (stack_size < _PyOS_MIN_STACK_SIZE) {
|
||||||
|
PyErr_Format(PyExc_ValueError,
|
||||||
|
"stack_size must be at least %zu bytes",
|
||||||
|
_PyOS_MIN_STACK_SIZE);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uintptr_t base = (uintptr_t)stack_start_addr;
|
||||||
|
uintptr_t top = base + stack_size;
|
||||||
|
tstate_set_stack(tstate, base, top);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
PyUnstable_ThreadState_ResetStackProtection(PyThreadState *tstate)
|
||||||
|
{
|
||||||
|
_PyThreadStateImpl *ts = (_PyThreadStateImpl *)tstate;
|
||||||
|
if (ts->c_stack_init_top != 0) {
|
||||||
|
tstate_set_stack(tstate,
|
||||||
|
ts->c_stack_init_base,
|
||||||
|
ts->c_stack_init_top);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_Py_InitializeRecursionLimits(tstate);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* The function _Py_EnterRecursiveCallTstate() only calls _Py_CheckRecursiveCall()
|
/* The function _Py_EnterRecursiveCallTstate() only calls _Py_CheckRecursiveCall()
|
||||||
if the recursion_depth reaches recursion_limit. */
|
if the recursion_depth reaches recursion_limit. */
|
||||||
int
|
int
|
||||||
|
|
|
||||||
|
|
@ -1583,6 +1583,9 @@ init_threadstate(_PyThreadStateImpl *_tstate,
|
||||||
_tstate->c_stack_top = 0;
|
_tstate->c_stack_top = 0;
|
||||||
_tstate->c_stack_hard_limit = 0;
|
_tstate->c_stack_hard_limit = 0;
|
||||||
|
|
||||||
|
_tstate->c_stack_init_base = 0;
|
||||||
|
_tstate->c_stack_init_top = 0;
|
||||||
|
|
||||||
_tstate->asyncio_running_loop = NULL;
|
_tstate->asyncio_running_loop = NULL;
|
||||||
_tstate->asyncio_running_task = NULL;
|
_tstate->asyncio_running_task = NULL;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue