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.
|
case, a :exc:`RecursionError` is set and a nonzero value is returned.
|
||||||
Otherwise, zero is returned.
|
Otherwise, zero is returned.
|
||||||
|
|
||||||
*where* should be a string such as ``" in instance check"`` to be
|
*where* should be a UTF-8 encoded string such as ``" in instance check"`` to
|
||||||
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.
|
||||||
|
|
||||||
.. 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
|
Ends a :c:func:`Py_EnterRecursiveCall`. Must be called once for each
|
||||||
*successful* invocation of :c:func:`Py_EnterRecursiveCall`.
|
*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
|
Properly implementing :c:member:`~PyTypeObject.tp_repr` for container types requires
|
||||||
special recursion handling. In addition to protecting the stack,
|
special recursion handling. In addition to protecting the stack,
|
||||||
:c:member:`~PyTypeObject.tp_repr` also needs to track objects to prevent cycles. The
|
:c:member:`~PyTypeObject.tp_repr` also needs to track objects to prevent cycles. The
|
||||||
|
|
|
@ -197,6 +197,12 @@ Optimizations
|
||||||
Build and C API Changes
|
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
|
* 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
|
calls a callable Python object without any arguments. It is the most efficient
|
||||||
way to call a callable Python object without any argument.
|
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(void) Py_SetRecursionLimit(int);
|
||||||
PyAPI_FUNC(int) Py_GetRecursionLimit(void);
|
PyAPI_FUNC(int) Py_GetRecursionLimit(void);
|
||||||
|
|
||||||
#define Py_EnterRecursiveCall(where) \
|
PyAPI_FUNC(int) Py_EnterRecursiveCall(const char *where);
|
||||||
(_Py_MakeRecCheck(PyThreadState_GET()->recursion_depth) && \
|
PyAPI_FUNC(void) Py_LeaveRecursiveCall(void);
|
||||||
_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))
|
|
||||||
|
|
||||||
#define Py_ALLOW_RECURSION \
|
#define Py_ALLOW_RECURSION \
|
||||||
do { unsigned char _old = PyThreadState_GET()->recursion_critical;\
|
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_MASK 0x4
|
||||||
#define FVS_HAVE_SPEC 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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#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/Python-ast.h \
|
||||||
\
|
\
|
||||||
$(srcdir)/Include/cpython/abstract.h \
|
$(srcdir)/Include/cpython/abstract.h \
|
||||||
|
$(srcdir)/Include/cpython/ceval.h \
|
||||||
$(srcdir)/Include/cpython/dictobject.h \
|
$(srcdir)/Include/cpython/dictobject.h \
|
||||||
$(srcdir)/Include/cpython/fileobject.h \
|
$(srcdir)/Include/cpython/fileobject.h \
|
||||||
$(srcdir)/Include/cpython/import.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\complexobject.h" />
|
||||||
<ClInclude Include="..\Include\context.h" />
|
<ClInclude Include="..\Include\context.h" />
|
||||||
<ClInclude Include="..\Include\cpython\abstract.h" />
|
<ClInclude Include="..\Include\cpython\abstract.h" />
|
||||||
|
<ClInclude Include="..\Include\cpython\ceval.h" />
|
||||||
<ClInclude Include="..\Include\cpython\dictobject.h" />
|
<ClInclude Include="..\Include\cpython\dictobject.h" />
|
||||||
<ClInclude Include="..\Include\cpython\fileobject.h" />
|
<ClInclude Include="..\Include\cpython\fileobject.h" />
|
||||||
<ClInclude Include="..\Include\cpython\import.h" />
|
<ClInclude Include="..\Include\cpython\import.h" />
|
||||||
|
|
|
@ -84,6 +84,9 @@
|
||||||
<ClInclude Include="..\Include\cpython\abstract.h">
|
<ClInclude Include="..\Include\cpython\abstract.h">
|
||||||
<Filter>Include</Filter>
|
<Filter>Include</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\Include\cpython\ceval.h">
|
||||||
|
<Filter>Include</Filter>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="..\Include\cpython\dictobject.h">
|
<ClInclude Include="..\Include\cpython\dictobject.h">
|
||||||
<Filter>Include</Filter>
|
<Filter>Include</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
|
|
@ -5632,3 +5632,21 @@ maybe_dtrace_line(PyFrameObject *frame,
|
||||||
}
|
}
|
||||||
*instr_prev = frame->f_lasti;
|
*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