Skip to content

Commit

Permalink
Use static inline function Py_EnterRecursiveCall() (#91988)
Browse files Browse the repository at this point in the history
Currently, calling Py_EnterRecursiveCall() and
Py_LeaveRecursiveCall() may use a function call or a static inline
function call, depending if the internal pycore_ceval.h header file
is included or not. Use a different name for the static inline
function to ensure that the static inline function is always used in
Python internals for best performance. Similar approach than
PyThreadState_GET() (function call) and _PyThreadState_GET() (static
inline function).

* Rename _Py_EnterRecursiveCall() to _Py_EnterRecursiveCallTstate()
* Rename _Py_LeaveRecursiveCall() to _Py_LeaveRecursiveCallTstate()
* pycore_ceval.h: Rename Py_EnterRecursiveCall() to
  _Py_EnterRecursiveCall() and Py_LeaveRecursiveCall() and
  _Py_LeaveRecursiveCall()
  • Loading branch information
vstinner committed May 4, 2022
1 parent 1424336 commit d716a0d
Show file tree
Hide file tree
Showing 15 changed files with 524 additions and 521 deletions.
20 changes: 8 additions & 12 deletions Include/internal/pycore_ceval.h
Expand Up @@ -91,7 +91,7 @@ extern void _PyEval_DeactivateOpCache(void);

#ifdef USE_STACKCHECK
/* With USE_STACKCHECK macro defined, trigger stack checks in
_Py_CheckRecursiveCall() on every 64th call to Py_EnterRecursiveCall. */
_Py_CheckRecursiveCall() on every 64th call to _Py_EnterRecursiveCall. */
static inline int _Py_MakeRecCheck(PyThreadState *tstate) {
return (tstate->recursion_remaining-- <= 0
|| (tstate->recursion_remaining & 63) == 0);
Expand All @@ -106,29 +106,25 @@ PyAPI_FUNC(int) _Py_CheckRecursiveCall(
PyThreadState *tstate,
const char *where);

static inline int _Py_EnterRecursiveCall(PyThreadState *tstate,
const char *where) {
static inline int _Py_EnterRecursiveCallTstate(PyThreadState *tstate,
const char *where) {
return (_Py_MakeRecCheck(tstate) && _Py_CheckRecursiveCall(tstate, where));
}

static inline int _Py_EnterRecursiveCall_inline(const char *where) {
static inline int _Py_EnterRecursiveCall(const char *where) {
PyThreadState *tstate = _PyThreadState_GET();
return _Py_EnterRecursiveCall(tstate, where);
return _Py_EnterRecursiveCallTstate(tstate, where);
}

#define Py_EnterRecursiveCall(where) _Py_EnterRecursiveCall_inline(where)

static inline void _Py_LeaveRecursiveCall(PyThreadState *tstate) {
static inline void _Py_LeaveRecursiveCallTstate(PyThreadState *tstate) {
tstate->recursion_remaining++;
}

static inline void _Py_LeaveRecursiveCall_inline(void) {
static inline void _Py_LeaveRecursiveCall(void) {
PyThreadState *tstate = _PyThreadState_GET();
_Py_LeaveRecursiveCall(tstate);
_Py_LeaveRecursiveCallTstate(tstate);
}

#define Py_LeaveRecursiveCall() _Py_LeaveRecursiveCall_inline()

extern struct _PyInterpreterFrame* _PyEval_GetFrame(void);

extern PyObject* _Py_MakeCoro(PyFunctionObject *func);
Expand Down
5 changes: 3 additions & 2 deletions Modules/_ctypes/_ctypes.c
Expand Up @@ -112,6 +112,7 @@ bytes(cdata)
#endif

#include "pycore_call.h" // _PyObject_CallNoArgs()
#include "pycore_ceval.h" // _Py_EnterRecursiveCall()
#include "structmember.h" // PyMemberDef

#include <ffi.h>
Expand Down Expand Up @@ -2270,12 +2271,12 @@ PyCSimpleType_from_param(PyObject *type, PyObject *value)
return NULL;
}
if (as_parameter) {
if (Py_EnterRecursiveCall("while processing _as_parameter_")) {
if (_Py_EnterRecursiveCall("while processing _as_parameter_")) {
Py_DECREF(as_parameter);
return NULL;
}
value = PyCSimpleType_from_param(type, as_parameter);
Py_LeaveRecursiveCall();
_Py_LeaveRecursiveCall();
Py_DECREF(as_parameter);
return value;
}
Expand Down
25 changes: 13 additions & 12 deletions Modules/_json.c
Expand Up @@ -10,6 +10,7 @@
#define NEEDS_PY_IDENTIFIER

#include "Python.h"
#include "pycore_ceval.h" // _Py_EnterRecursiveCall()
#include "structmember.h" // PyMemberDef
#include "pycore_accu.h"

Expand Down Expand Up @@ -1059,19 +1060,19 @@ scan_once_unicode(PyScannerObject *s, PyObject *pystr, Py_ssize_t idx, Py_ssize_
return scanstring_unicode(pystr, idx + 1, s->strict, next_idx_ptr);
case '{':
/* object */
if (Py_EnterRecursiveCall(" while decoding a JSON object "
"from a unicode string"))
if (_Py_EnterRecursiveCall(" while decoding a JSON object "
"from a unicode string"))
return NULL;
res = _parse_object_unicode(s, pystr, idx + 1, next_idx_ptr);
Py_LeaveRecursiveCall();
_Py_LeaveRecursiveCall();
return res;
case '[':
/* array */
if (Py_EnterRecursiveCall(" while decoding a JSON array "
"from a unicode string"))
if (_Py_EnterRecursiveCall(" while decoding a JSON array "
"from a unicode string"))
return NULL;
res = _parse_array_unicode(s, pystr, idx + 1, next_idx_ptr);
Py_LeaveRecursiveCall();
_Py_LeaveRecursiveCall();
return res;
case 'n':
/* null */
Expand Down Expand Up @@ -1430,17 +1431,17 @@ encoder_listencode_obj(PyEncoderObject *s, _PyAccu *acc,
return _steal_accumulate(acc, encoded);
}
else if (PyList_Check(obj) || PyTuple_Check(obj)) {
if (Py_EnterRecursiveCall(" while encoding a JSON object"))
if (_Py_EnterRecursiveCall(" while encoding a JSON object"))
return -1;
rv = encoder_listencode_list(s, acc, obj, indent_level);
Py_LeaveRecursiveCall();
_Py_LeaveRecursiveCall();
return rv;
}
else if (PyDict_Check(obj)) {
if (Py_EnterRecursiveCall(" while encoding a JSON object"))
if (_Py_EnterRecursiveCall(" while encoding a JSON object"))
return -1;
rv = encoder_listencode_dict(s, acc, obj, indent_level);
Py_LeaveRecursiveCall();
_Py_LeaveRecursiveCall();
return rv;
}
else {
Expand Down Expand Up @@ -1468,13 +1469,13 @@ encoder_listencode_obj(PyEncoderObject *s, _PyAccu *acc,
return -1;
}

if (Py_EnterRecursiveCall(" while encoding a JSON object")) {
if (_Py_EnterRecursiveCall(" while encoding a JSON object")) {
Py_DECREF(newobj);
Py_XDECREF(ident);
return -1;
}
rv = encoder_listencode_obj(s, acc, newobj, indent_level);
Py_LeaveRecursiveCall();
_Py_LeaveRecursiveCall();

Py_DECREF(newobj);
if (rv) {
Expand Down
23 changes: 12 additions & 11 deletions Modules/_pickle.c
Expand Up @@ -9,6 +9,7 @@
#endif

#include "Python.h"
#include "pycore_ceval.h" // _Py_EnterRecursiveCall()
#include "pycore_moduleobject.h" // _PyModule_GetState()
#include "pycore_runtime.h" // _Py_ID()
#include "pycore_pystate.h" // _PyThreadState_GET()
Expand Down Expand Up @@ -3068,21 +3069,21 @@ save_list(PicklerObject *self, PyObject *obj)
if (len != 0) {
/* Materialize the list elements. */
if (PyList_CheckExact(obj) && self->proto > 0) {
if (Py_EnterRecursiveCall(" while pickling an object"))
if (_Py_EnterRecursiveCall(" while pickling an object"))
goto error;
status = batch_list_exact(self, obj);
Py_LeaveRecursiveCall();
_Py_LeaveRecursiveCall();
} else {
PyObject *iter = PyObject_GetIter(obj);
if (iter == NULL)
goto error;

if (Py_EnterRecursiveCall(" while pickling an object")) {
if (_Py_EnterRecursiveCall(" while pickling an object")) {
Py_DECREF(iter);
goto error;
}
status = batch_list(self, iter);
Py_LeaveRecursiveCall();
_Py_LeaveRecursiveCall();
Py_DECREF(iter);
}
}
Expand Down Expand Up @@ -3327,10 +3328,10 @@ save_dict(PicklerObject *self, PyObject *obj)
if (PyDict_CheckExact(obj) && self->proto > 0) {
/* We can take certain shortcuts if we know this is a dict and
not a dict subclass. */
if (Py_EnterRecursiveCall(" while pickling an object"))
if (_Py_EnterRecursiveCall(" while pickling an object"))
goto error;
status = batch_dict_exact(self, obj);
Py_LeaveRecursiveCall();
_Py_LeaveRecursiveCall();
} else {
items = PyObject_CallMethodNoArgs(obj, &_Py_ID(items));
if (items == NULL)
Expand All @@ -3339,12 +3340,12 @@ save_dict(PicklerObject *self, PyObject *obj)
Py_DECREF(items);
if (iter == NULL)
goto error;
if (Py_EnterRecursiveCall(" while pickling an object")) {
if (_Py_EnterRecursiveCall(" while pickling an object")) {
Py_DECREF(iter);
goto error;
}
status = batch_dict(self, iter);
Py_LeaveRecursiveCall();
_Py_LeaveRecursiveCall();
Py_DECREF(iter);
}
}
Expand Down Expand Up @@ -4300,9 +4301,9 @@ save(PicklerObject *self, PyObject *obj, int pers_save)
return save_unicode(self, obj);
}

/* We're only calling Py_EnterRecursiveCall here so that atomic
/* We're only calling _Py_EnterRecursiveCall here so that atomic
types above are pickled faster. */
if (Py_EnterRecursiveCall(" while pickling an object")) {
if (_Py_EnterRecursiveCall(" while pickling an object")) {
return -1;
}

Expand Down Expand Up @@ -4460,7 +4461,7 @@ save(PicklerObject *self, PyObject *obj, int pers_save)
}
done:

Py_LeaveRecursiveCall();
_Py_LeaveRecursiveCall();
Py_XDECREF(reduce_func);
Py_XDECREF(reduce_value);

Expand Down
22 changes: 11 additions & 11 deletions Objects/abstract.c
Expand Up @@ -3,7 +3,7 @@
#include "Python.h"
#include "pycore_abstract.h" // _PyIndex_Check()
#include "pycore_call.h" // _PyObject_CallNoArgs()
#include "pycore_ceval.h" // _Py_EnterRecursiveCall()
#include "pycore_ceval.h" // _Py_EnterRecursiveCallTstate()
#include "pycore_object.h" // _Py_CheckSlotResult()
#include "pycore_pyerrors.h" // _PyErr_Occurred()
#include "pycore_pystate.h" // _PyThreadState_GET()
Expand Down Expand Up @@ -2546,7 +2546,7 @@ abstract_issubclass(PyObject *derived, PyObject *cls)
break;
}
assert(n >= 2);
if (Py_EnterRecursiveCall(" in __issubclass__")) {
if (_Py_EnterRecursiveCall(" in __issubclass__")) {
Py_DECREF(bases);
return -1;
}
Expand All @@ -2556,7 +2556,7 @@ abstract_issubclass(PyObject *derived, PyObject *cls)
break;
}
}
Py_LeaveRecursiveCall();
_Py_LeaveRecursiveCall();
Py_DECREF(bases);
return r;
}
Expand Down Expand Up @@ -2633,7 +2633,7 @@ object_recursive_isinstance(PyThreadState *tstate, PyObject *inst, PyObject *cls
if (PyTuple_Check(cls)) {
/* Not a general sequence -- that opens up the road to
recursion and stack overflow. */
if (_Py_EnterRecursiveCall(tstate, " in __instancecheck__")) {
if (_Py_EnterRecursiveCallTstate(tstate, " in __instancecheck__")) {
return -1;
}
Py_ssize_t n = PyTuple_GET_SIZE(cls);
Expand All @@ -2646,19 +2646,19 @@ object_recursive_isinstance(PyThreadState *tstate, PyObject *inst, PyObject *cls
break;
}
}
_Py_LeaveRecursiveCall(tstate);
_Py_LeaveRecursiveCallTstate(tstate);
return r;
}

PyObject *checker = _PyObject_LookupSpecial(cls, &_Py_ID(__instancecheck__));
if (checker != NULL) {
if (_Py_EnterRecursiveCall(tstate, " in __instancecheck__")) {
if (_Py_EnterRecursiveCallTstate(tstate, " in __instancecheck__")) {
Py_DECREF(checker);
return -1;
}

PyObject *res = PyObject_CallOneArg(checker, inst);
_Py_LeaveRecursiveCall(tstate);
_Py_LeaveRecursiveCallTstate(tstate);
Py_DECREF(checker);

if (res == NULL) {
Expand Down Expand Up @@ -2725,7 +2725,7 @@ object_issubclass(PyThreadState *tstate, PyObject *derived, PyObject *cls)

if (PyTuple_Check(cls)) {

if (_Py_EnterRecursiveCall(tstate, " in __subclasscheck__")) {
if (_Py_EnterRecursiveCallTstate(tstate, " in __subclasscheck__")) {
return -1;
}
Py_ssize_t n = PyTuple_GET_SIZE(cls);
Expand All @@ -2737,19 +2737,19 @@ object_issubclass(PyThreadState *tstate, PyObject *derived, PyObject *cls)
/* either found it, or got an error */
break;
}
_Py_LeaveRecursiveCall(tstate);
_Py_LeaveRecursiveCallTstate(tstate);
return r;
}

checker = _PyObject_LookupSpecial(cls, &_Py_ID(__subclasscheck__));
if (checker != NULL) {
int ok = -1;
if (_Py_EnterRecursiveCall(tstate, " in __subclasscheck__")) {
if (_Py_EnterRecursiveCallTstate(tstate, " in __subclasscheck__")) {
Py_DECREF(checker);
return ok;
}
PyObject *res = PyObject_CallOneArg(checker, derived);
_Py_LeaveRecursiveCall(tstate);
_Py_LeaveRecursiveCallTstate(tstate);
Py_DECREF(checker);
if (res != NULL) {
ok = PyObject_IsTrue(res);
Expand Down
2 changes: 1 addition & 1 deletion Objects/bytesobject.c
Expand Up @@ -3489,7 +3489,7 @@ _PyBytesWriter_Alloc(_PyBytesWriter *writer, Py_ssize_t size)
Don't modify the _PyBytesWriter structure (use a shorter small buffer)
in debug mode to also be able to detect stack overflow when running
tests in debug mode. The _PyBytesWriter is large (more than 512 bytes),
if Py_EnterRecursiveCall() is not used in deep C callback, we may hit a
if _Py_EnterRecursiveCall() is not used in deep C callback, we may hit a
stack overflow. */
writer->allocated = Py_MIN(writer->allocated, 10);
/* _PyBytesWriter_CheckConsistency() requires the last byte to be 0,
Expand Down
8 changes: 4 additions & 4 deletions Objects/call.c
Expand Up @@ -209,11 +209,11 @@ _PyObject_MakeTpCall(PyThreadState *tstate, PyObject *callable,
}

PyObject *result = NULL;
if (_Py_EnterRecursiveCall(tstate, " while calling a Python object") == 0)
if (_Py_EnterRecursiveCallTstate(tstate, " while calling a Python object") == 0)
{
result = _PyCFunctionWithKeywords_TrampolineCall(
(PyCFunctionWithKeywords)call, callable, argstuple, kwdict);
_Py_LeaveRecursiveCall(tstate);
_Py_LeaveRecursiveCallTstate(tstate);
}

Py_DECREF(argstuple);
Expand Down Expand Up @@ -336,13 +336,13 @@ _PyObject_Call(PyThreadState *tstate, PyObject *callable,
return NULL;
}

if (_Py_EnterRecursiveCall(tstate, " while calling a Python object")) {
if (_Py_EnterRecursiveCallTstate(tstate, " while calling a Python object")) {
return NULL;
}

result = (*call)(callable, args, kwargs);

_Py_LeaveRecursiveCall(tstate);
_Py_LeaveRecursiveCallTstate(tstate);

return _Py_CheckFunctionResult(tstate, callable, result, NULL);
}
Expand Down

0 comments on commit d716a0d

Please sign in to comment.