Skip to content

Commit

Permalink
bpo-38644: Add Py_EnterRecursiveCall() to the limited API (pythonGH-1…
Browse files Browse the repository at this point in the history
…7046)

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.
  • Loading branch information
vstinner authored Nov 4, 2019
1 parent 6552563 commit f4b1e3d
Show file tree
Hide file tree
Showing 9 changed files with 101 additions and 38 deletions.
12 changes: 9 additions & 3 deletions Doc/c-api/exceptions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 6 additions & 0 deletions Doc/whatsnew/3.9.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
43 changes: 8 additions & 35 deletions Include/ceval.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;\
Expand Down Expand Up @@ -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
Expand Down
50 changes: 50 additions & 0 deletions Include/cpython/ceval.h
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions Makefile.pre.in
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
1 change: 1 addition & 0 deletions PCbuild/pythoncore.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -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" />
Expand Down
3 changes: 3 additions & 0 deletions PCbuild/pythoncore.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
18 changes: 18 additions & 0 deletions Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

0 comments on commit f4b1e3d

Please sign in to comment.