]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
bpo-38644: Add Py_EnterRecursiveCall() to the limited API (GH-17046)
authorVictor Stinner <vstinner@python.org>
Mon, 4 Nov 2019 18:48:34 +0000 (19:48 +0100)
committerGitHub <noreply@github.com>
Mon, 4 Nov 2019 18:48:34 +0000 (19:48 +0100)
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.

Doc/c-api/exceptions.rst
Doc/whatsnew/3.9.rst
Include/ceval.h
Include/cpython/ceval.h [new file with mode: 0644]
Makefile.pre.in
Misc/NEWS.d/next/C API/2019-11-04-17-59-46.bpo-38644.euO_RR.rst [new file with mode: 0644]
PCbuild/pythoncore.vcxproj
PCbuild/pythoncore.vcxproj.filters
Python/ceval.c

index c7ba74cc8d587424f5f14da84b1b4151f2673d6e..a042c6eee0a298fc630e72e0520306200847983e 100644 (file)
@@ -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
index 7d7c502459a9b1073bb99ba221835e9ac3b0dbaf..3cac9c5eedbc231bb12541d826ec9ee94553aedf 100644 (file)
@@ -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.
index 61db777cc4d5bfd7b5969276d361316810657660..8da779ba97b73e4d162521972d486b71e3ccfe7e 100644 (file)
@@ -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
diff --git a/Include/cpython/ceval.h b/Include/cpython/ceval.h
new file mode 100644 (file)
index 0000000..61bbc4f
--- /dev/null
@@ -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
index 1c0958ec974b2511b69ff550d9ec25d93fb1ba3b..3c607d08ae745d5fcc1a206fb54e98cb325d7c56 100644 (file)
@@ -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 \
diff --git a/Misc/NEWS.d/next/C API/2019-11-04-17-59-46.bpo-38644.euO_RR.rst b/Misc/NEWS.d/next/C API/2019-11-04-17-59-46.bpo-38644.euO_RR.rst
new file mode 100644 (file)
index 0000000..b94f505
--- /dev/null
@@ -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.
index 1c055b6a33430475f5b005a4072468c67a125abe..b72474060a3627d5b9ae4dab647e39d24d477d55 100644 (file)
     <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" />
index dbff89fbff62ba8761c239d9509b997434ce667f..80908135550b64a8b260b11804ab1757ee936681 100644 (file)
@@ -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>
index a7d2ea80069a06d144644fc882cd23511ce98f67..881a7dd629bccc33fcd8c7135901c5e07370854d 100644 (file)
@@ -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();
+}