bpo-38644: Add Py_EnterRecursiveCall() to the limited API (GH-17046)
Provide Py_EnterRecursiveCall() and Py_LeaveRecursiveCall() as regular functions for the limited API. Previously, there were defined as macros, but these macros didn't work with the limited API which cannot access PyThreadState.recursion_depth field. Remove _Py_CheckRecursionLimit from the stable ABI. Add Include/cpython/ceval.h header file.
This commit is contained in:
parent
6552563b3d
commit
f4b1e3d7c6
|
@ -715,15 +715,21 @@ recursion depth automatically).
|
|||
case, a :exc:`RecursionError` is set and a nonzero value is returned.
|
||||
Otherwise, zero is returned.
|
||||
|
||||
*where* should be a string such as ``" in instance check"`` to be
|
||||
concatenated to the :exc:`RecursionError` message caused by the recursion
|
||||
*where* should be a UTF-8 encoded string such as ``" in instance check"`` to
|
||||
be concatenated to the :exc:`RecursionError` message caused by the recursion
|
||||
depth limit.
|
||||
|
||||
.. c:function:: void Py_LeaveRecursiveCall()
|
||||
.. versionchanged:: 3.9
|
||||
This function is now also available in the limited API.
|
||||
|
||||
.. c:function:: void Py_LeaveRecursiveCall(void)
|
||||
|
||||
Ends a :c:func:`Py_EnterRecursiveCall`. Must be called once for each
|
||||
*successful* invocation of :c:func:`Py_EnterRecursiveCall`.
|
||||
|
||||
.. versionchanged:: 3.9
|
||||
This function is now also available in the limited API.
|
||||
|
||||
Properly implementing :c:member:`~PyTypeObject.tp_repr` for container types requires
|
||||
special recursion handling. In addition to protecting the stack,
|
||||
:c:member:`~PyTypeObject.tp_repr` also needs to track objects to prevent cycles. The
|
||||
|
|
|
@ -197,6 +197,12 @@ Optimizations
|
|||
Build and C API Changes
|
||||
=======================
|
||||
|
||||
* Provide :c:func:`Py_EnterRecursiveCall` and :c:func:`Py_LeaveRecursiveCall`
|
||||
as regular functions for the limited API. Previously, there were defined as
|
||||
macros, but these macros didn't work with the limited API which cannot access
|
||||
``PyThreadState.recursion_depth`` field. Remove ``_Py_CheckRecursionLimit``
|
||||
from the stable ABI.
|
||||
(Contributed by Victor Stinner in :issue:`38644`.)
|
||||
* Add a new public :c:func:`PyObject_CallNoArgs` function to the C API, which
|
||||
calls a callable Python object without any arguments. It is the most efficient
|
||||
way to call a callable Python object without any argument.
|
||||
|
|
|
@ -85,41 +85,8 @@ PyAPI_FUNC(int) Py_MakePendingCalls(void);
|
|||
PyAPI_FUNC(void) Py_SetRecursionLimit(int);
|
||||
PyAPI_FUNC(int) Py_GetRecursionLimit(void);
|
||||
|
||||
#define Py_EnterRecursiveCall(where) \
|
||||
(_Py_MakeRecCheck(PyThreadState_GET()->recursion_depth) && \
|
||||
_Py_CheckRecursiveCall(where))
|
||||
#define Py_LeaveRecursiveCall() \
|
||||
do{ if(_Py_MakeEndRecCheck(PyThreadState_GET()->recursion_depth)) \
|
||||
PyThreadState_GET()->overflowed = 0; \
|
||||
} while(0)
|
||||
PyAPI_FUNC(int) _Py_CheckRecursiveCall(const char *where);
|
||||
|
||||
/* Due to the macros in which it's used, _Py_CheckRecursionLimit is in
|
||||
the stable ABI. It should be removed therefrom when possible.
|
||||
*/
|
||||
PyAPI_DATA(int) _Py_CheckRecursionLimit;
|
||||
|
||||
#ifdef USE_STACKCHECK
|
||||
/* With USE_STACKCHECK, trigger stack checks in _Py_CheckRecursiveCall()
|
||||
on every 64th call to Py_EnterRecursiveCall.
|
||||
*/
|
||||
# define _Py_MakeRecCheck(x) \
|
||||
(++(x) > _Py_CheckRecursionLimit || \
|
||||
++(PyThreadState_GET()->stackcheck_counter) > 64)
|
||||
#else
|
||||
# define _Py_MakeRecCheck(x) (++(x) > _Py_CheckRecursionLimit)
|
||||
#endif
|
||||
|
||||
/* 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. */
|
||||
#define _Py_RecursionLimitLowerWaterMark(limit) \
|
||||
(((limit) > 200) \
|
||||
? ((limit) - 50) \
|
||||
: (3 * ((limit) >> 2)))
|
||||
|
||||
#define _Py_MakeEndRecCheck(x) \
|
||||
(--(x) < _Py_RecursionLimitLowerWaterMark(_Py_CheckRecursionLimit))
|
||||
PyAPI_FUNC(int) Py_EnterRecursiveCall(const char *where);
|
||||
PyAPI_FUNC(void) Py_LeaveRecursiveCall(void);
|
||||
|
||||
#define Py_ALLOW_RECURSION \
|
||||
do { unsigned char _old = PyThreadState_GET()->recursion_critical;\
|
||||
|
@ -224,6 +191,12 @@ PyAPI_FUNC(int) _PyEval_SliceIndexNotNone(PyObject *, Py_ssize_t *);
|
|||
#define FVS_MASK 0x4
|
||||
#define FVS_HAVE_SPEC 0x4
|
||||
|
||||
#ifndef Py_LIMITED_API
|
||||
# define Py_CPYTHON_CEVAL_H
|
||||
# include "cpython/ceval.h"
|
||||
# undef Py_CPYTHON_CEVAL_H
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
#ifndef Py_CPYTHON_CEVAL_H
|
||||
# error "this header file must not be included directly"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
PyAPI_DATA(int) _Py_CheckRecursionLimit;
|
||||
|
||||
#ifdef USE_STACKCHECK
|
||||
/* With USE_STACKCHECK macro defined, trigger stack checks in
|
||||
_Py_CheckRecursiveCall() on every 64th call to Py_EnterRecursiveCall. */
|
||||
# define _Py_MakeRecCheck(x) \
|
||||
(++(x) > _Py_CheckRecursionLimit || \
|
||||
++(PyThreadState_GET()->stackcheck_counter) > 64)
|
||||
#else
|
||||
# define _Py_MakeRecCheck(x) (++(x) > _Py_CheckRecursionLimit)
|
||||
#endif
|
||||
|
||||
PyAPI_FUNC(int) _Py_CheckRecursiveCall(const char *where);
|
||||
|
||||
#define _Py_EnterRecursiveCall_macro(where) \
|
||||
(_Py_MakeRecCheck(PyThreadState_GET()->recursion_depth) && \
|
||||
_Py_CheckRecursiveCall(where))
|
||||
|
||||
#define Py_EnterRecursiveCall(where) _Py_EnterRecursiveCall_macro(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. */
|
||||
#define _Py_RecursionLimitLowerWaterMark(limit) \
|
||||
(((limit) > 200) \
|
||||
? ((limit) - 50) \
|
||||
: (3 * ((limit) >> 2)))
|
||||
|
||||
#define _Py_MakeEndRecCheck(x) \
|
||||
(--(x) < _Py_RecursionLimitLowerWaterMark(_Py_CheckRecursionLimit))
|
||||
|
||||
#define _Py_LeaveRecursiveCall_macro() \
|
||||
do{ if(_Py_MakeEndRecCheck(PyThreadState_GET()->recursion_depth)) \
|
||||
PyThreadState_GET()->overflowed = 0; \
|
||||
} while(0)
|
||||
|
||||
#define Py_LeaveRecursiveCall() _Py_LeaveRecursiveCall_macro()
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -1057,6 +1057,7 @@ PYTHON_HEADERS= \
|
|||
$(srcdir)/Include/Python-ast.h \
|
||||
\
|
||||
$(srcdir)/Include/cpython/abstract.h \
|
||||
$(srcdir)/Include/cpython/ceval.h \
|
||||
$(srcdir)/Include/cpython/dictobject.h \
|
||||
$(srcdir)/Include/cpython/fileobject.h \
|
||||
$(srcdir)/Include/cpython/import.h \
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
Provide :c:func:`Py_EnterRecursiveCall` and :c:func:`Py_LeaveRecursiveCall`
|
||||
as regular functions for the limited API. Previously, there were defined as
|
||||
macros, but these macros didn't work with the limited API which cannot access
|
||||
``PyThreadState.recursion_depth`` field. Remove ``_Py_CheckRecursionLimit``
|
||||
from the stable ABI.
|
|
@ -127,6 +127,7 @@
|
|||
<ClInclude Include="..\Include\complexobject.h" />
|
||||
<ClInclude Include="..\Include\context.h" />
|
||||
<ClInclude Include="..\Include\cpython\abstract.h" />
|
||||
<ClInclude Include="..\Include\cpython\ceval.h" />
|
||||
<ClInclude Include="..\Include\cpython\dictobject.h" />
|
||||
<ClInclude Include="..\Include\cpython\fileobject.h" />
|
||||
<ClInclude Include="..\Include\cpython\import.h" />
|
||||
|
|
|
@ -84,6 +84,9 @@
|
|||
<ClInclude Include="..\Include\cpython\abstract.h">
|
||||
<Filter>Include</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Include\cpython\ceval.h">
|
||||
<Filter>Include</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Include\cpython\dictobject.h">
|
||||
<Filter>Include</Filter>
|
||||
</ClInclude>
|
||||
|
|
|
@ -5632,3 +5632,21 @@ maybe_dtrace_line(PyFrameObject *frame,
|
|||
}
|
||||
*instr_prev = frame->f_lasti;
|
||||
}
|
||||
|
||||
|
||||
/* Implement Py_EnterRecursiveCall() and Py_LeaveRecursiveCall() as functions
|
||||
for the limited API. */
|
||||
|
||||
#undef Py_EnterRecursiveCall
|
||||
|
||||
int Py_EnterRecursiveCall(const char *where)
|
||||
{
|
||||
return _Py_EnterRecursiveCall_macro(where);
|
||||
}
|
||||
|
||||
#undef Py_LeaveRecursiveCall
|
||||
|
||||
void Py_LeaveRecursiveCall(void)
|
||||
{
|
||||
_Py_LeaveRecursiveCall_macro();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue