Skip to content

Commit

Permalink
bpo-38644: Pass tstate to Py_EnterRecursiveCall() (pythonGH-16997)
Browse files Browse the repository at this point in the history
* Add _Py_EnterRecursiveCall() and _Py_LeaveRecursiveCall() which
  require a tstate argument.
* Pass tstate to _Py_MakeRecCheck() and  _Py_CheckRecursiveCall().
* Convert Py_EnterRecursiveCall() and Py_LeaveRecursiveCall() macros
  to static inline functions.

_PyThreadState_GET() is the most efficient way to get the tstate, and
so using it with _Py_EnterRecursiveCall() and
_Py_LeaveRecursiveCall() should be a little bit more efficient than
using Py_EnterRecursiveCall() and Py_LeaveRecursiveCall() which use
the "slower" PyThreadState_GET().
  • Loading branch information
vstinner authored and shihai1991 committed Jan 31, 2020
1 parent dca7d91 commit 7e432f9
Show file tree
Hide file tree
Showing 7 changed files with 200 additions and 135 deletions.
45 changes: 31 additions & 14 deletions Include/cpython/ceval.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,31 @@ 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)
static inline int _Py_MakeRecCheck(PyThreadState *tstate) {
return (++tstate->recursion_depth > _Py_CheckRecursionLimit
|| ++tstate->stackcheck_counter > 64);
}
#else
# define _Py_MakeRecCheck(x) (++(x) > _Py_CheckRecursionLimit)
static inline int _Py_MakeRecCheck(PyThreadState *tstate) {
return (++tstate->recursion_depth > _Py_CheckRecursionLimit);
}
#endif

PyAPI_FUNC(int) _Py_CheckRecursiveCall(const char *where);
PyAPI_FUNC(int) _Py_CheckRecursiveCall(
PyThreadState *tstate,
const char *where);

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

#define _Py_EnterRecursiveCall_macro(where) \
(_Py_MakeRecCheck(PyThreadState_GET()->recursion_depth) && \
_Py_CheckRecursiveCall(where))
static inline int _Py_EnterRecursiveCall_inline(const char *where) {
PyThreadState *tstate = PyThreadState_GET();
return _Py_EnterRecursiveCall(tstate, where);
}

#define Py_EnterRecursiveCall(where) _Py_EnterRecursiveCall_macro(where)
#define Py_EnterRecursiveCall(where) _Py_EnterRecursiveCall_inline(where)


/* Compute the "lower-water mark" for a recursion limit. When
Expand All @@ -38,12 +49,18 @@ PyAPI_FUNC(int) _Py_CheckRecursiveCall(const char *where);
#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)
static inline void _Py_LeaveRecursiveCall(PyThreadState *tstate) {
if (_Py_MakeEndRecCheck(tstate->recursion_depth)) {
tstate->overflowed = 0;
}
}

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

#define Py_LeaveRecursiveCall() _Py_LeaveRecursiveCall_macro()
#define Py_LeaveRecursiveCall() _Py_LeaveRecursiveCall_inline()

#ifdef __cplusplus
}
Expand Down
80 changes: 49 additions & 31 deletions Objects/abstract.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* Abstract Object Interface (many thanks to Jim Fulton) */

#include "Python.h"
#include "pycore_pyerrors.h"
#include "pycore_pystate.h"
#include <ctype.h>
#include "structmember.h" /* we need the offsetof() macro from there */
Expand Down Expand Up @@ -2459,8 +2460,8 @@ recursive_isinstance(PyObject *inst, PyObject *cls)
return retval;
}

int
PyObject_IsInstance(PyObject *inst, PyObject *cls)
static int
object_isinstance(PyThreadState *tstate, PyObject *inst, PyObject *cls)
{
_Py_IDENTIFIER(__instancecheck__);
PyObject *checker;
Expand All @@ -2475,47 +2476,55 @@ PyObject_IsInstance(PyObject *inst, PyObject *cls)
}

if (PyTuple_Check(cls)) {
Py_ssize_t i;
Py_ssize_t n;
int r = 0;

if (Py_EnterRecursiveCall(" in __instancecheck__"))
if (_Py_EnterRecursiveCall(tstate, " in __instancecheck__")) {
return -1;
n = PyTuple_GET_SIZE(cls);
for (i = 0; i < n; ++i) {
}
Py_ssize_t n = PyTuple_GET_SIZE(cls);
int r = 0;
for (Py_ssize_t i = 0; i < n; ++i) {
PyObject *item = PyTuple_GET_ITEM(cls, i);
r = PyObject_IsInstance(inst, item);
r = object_isinstance(tstate, inst, item);
if (r != 0)
/* either found it, or got an error */
break;
}
Py_LeaveRecursiveCall();
_Py_LeaveRecursiveCall(tstate);
return r;
}

checker = _PyObject_LookupSpecial(cls, &PyId___instancecheck__);
if (checker != NULL) {
PyObject *res;
int ok = -1;
if (Py_EnterRecursiveCall(" in __instancecheck__")) {
if (_Py_EnterRecursiveCall(tstate, " in __instancecheck__")) {
Py_DECREF(checker);
return ok;
}
res = _PyObject_CallOneArg(checker, inst);
Py_LeaveRecursiveCall();
PyObject *res = _PyObject_CallOneArg(checker, inst);
_Py_LeaveRecursiveCall(tstate);
Py_DECREF(checker);
if (res != NULL) {
ok = PyObject_IsTrue(res);
Py_DECREF(res);
}
return ok;
}
else if (PyErr_Occurred())
else if (_PyErr_Occurred(tstate)) {
return -1;
}

/* Probably never reached anymore. */
return recursive_isinstance(inst, cls);
}


int
PyObject_IsInstance(PyObject *inst, PyObject *cls)
{
PyThreadState *tstate = _PyThreadState_GET();
return object_isinstance(tstate, inst, cls);
}


static int
recursive_issubclass(PyObject *derived, PyObject *cls)
{
Expand All @@ -2534,8 +2543,8 @@ recursive_issubclass(PyObject *derived, PyObject *cls)
return abstract_issubclass(derived, cls);
}

int
PyObject_IsSubclass(PyObject *derived, PyObject *cls)
static int
object_issubclass(PyThreadState *tstate, PyObject *derived, PyObject *cls)
{
_Py_IDENTIFIER(__subclasscheck__);
PyObject *checker;
Expand All @@ -2549,47 +2558,56 @@ PyObject_IsSubclass(PyObject *derived, PyObject *cls)
}

if (PyTuple_Check(cls)) {
Py_ssize_t i;
Py_ssize_t n;
int r = 0;

if (Py_EnterRecursiveCall(" in __subclasscheck__"))
if (_Py_EnterRecursiveCall(tstate, " in __subclasscheck__")) {
return -1;
n = PyTuple_GET_SIZE(cls);
for (i = 0; i < n; ++i) {
}
Py_ssize_t n = PyTuple_GET_SIZE(cls);
int r = 0;
for (Py_ssize_t i = 0; i < n; ++i) {
PyObject *item = PyTuple_GET_ITEM(cls, i);
r = PyObject_IsSubclass(derived, item);
r = object_issubclass(tstate, derived, item);
if (r != 0)
/* either found it, or got an error */
break;
}
Py_LeaveRecursiveCall();
_Py_LeaveRecursiveCall(tstate);
return r;
}

checker = _PyObject_LookupSpecial(cls, &PyId___subclasscheck__);
if (checker != NULL) {
PyObject *res;
int ok = -1;
if (Py_EnterRecursiveCall(" in __subclasscheck__")) {
if (_Py_EnterRecursiveCall(tstate, " in __subclasscheck__")) {
Py_DECREF(checker);
return ok;
}
res = _PyObject_CallOneArg(checker, derived);
Py_LeaveRecursiveCall();
PyObject *res = _PyObject_CallOneArg(checker, derived);
_Py_LeaveRecursiveCall(tstate);
Py_DECREF(checker);
if (res != NULL) {
ok = PyObject_IsTrue(res);
Py_DECREF(res);
}
return ok;
}
else if (PyErr_Occurred())
else if (_PyErr_Occurred(tstate)) {
return -1;
}

/* Probably never reached anymore. */
return recursive_issubclass(derived, cls);
}


int
PyObject_IsSubclass(PyObject *derived, PyObject *cls)
{
PyThreadState *tstate = _PyThreadState_GET();
return object_issubclass(tstate, derived, cls);
}


int
_PyObject_RealIsInstance(PyObject *inst, PyObject *cls)
{
Expand Down
42 changes: 23 additions & 19 deletions Objects/call.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "Python.h"
#include "pycore_object.h"
#include "pycore_pyerrors.h"
#include "pycore_pystate.h"
#include "pycore_tupleobject.h"
#include "frameobject.h"
Expand Down Expand Up @@ -126,12 +127,15 @@ _PyObject_FastCallDict(PyObject *callable, PyObject *const *args,
PyObject *
_PyObject_MakeTpCall(PyObject *callable, PyObject *const *args, Py_ssize_t nargs, PyObject *keywords)
{
PyThreadState *tstate = _PyThreadState_GET();

/* Slow path: build a temporary tuple for positional arguments and a
* temporary dictionary for keyword arguments (if any) */
ternaryfunc call = Py_TYPE(callable)->tp_call;
if (call == NULL) {
PyErr_Format(PyExc_TypeError, "'%.200s' object is not callable",
Py_TYPE(callable)->tp_name);
_PyErr_Format(tstate, PyExc_TypeError,
"'%.200s' object is not callable",
Py_TYPE(callable)->tp_name);
return NULL;
}

Expand Down Expand Up @@ -162,10 +166,10 @@ _PyObject_MakeTpCall(PyObject *callable, PyObject *const *args, Py_ssize_t nargs
}

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

Py_DECREF(argstuple);
Expand Down Expand Up @@ -220,13 +224,14 @@ PyVectorcall_Call(PyObject *callable, PyObject *tuple, PyObject *kwargs)
PyObject *
PyObject_Call(PyObject *callable, PyObject *args, PyObject *kwargs)
{
PyThreadState *tstate = _PyThreadState_GET();
ternaryfunc call;
PyObject *result;

/* PyObject_Call() must not be called with an exception set,
because it can clear it (directly or indirectly) and so the
caller loses its exception */
assert(!PyErr_Occurred());
assert(!_PyErr_Occurred(tstate));
assert(PyTuple_Check(args));
assert(kwargs == NULL || PyDict_Check(kwargs));

Expand All @@ -236,17 +241,19 @@ PyObject_Call(PyObject *callable, PyObject *args, PyObject *kwargs)
else {
call = callable->ob_type->tp_call;
if (call == NULL) {
PyErr_Format(PyExc_TypeError, "'%.200s' object is not callable",
callable->ob_type->tp_name);
_PyErr_Format(tstate, PyExc_TypeError,
"'%.200s' object is not callable",
callable->ob_type->tp_name);
return NULL;
}

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

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

Py_LeaveRecursiveCall();
_Py_LeaveRecursiveCall(tstate);

return _Py_CheckFunctionResult(callable, result, NULL);
}
Expand All @@ -266,30 +273,27 @@ static PyObject* _Py_HOT_FUNCTION
function_code_fastcall(PyCodeObject *co, PyObject *const *args, Py_ssize_t nargs,
PyObject *globals)
{
PyFrameObject *f;
assert(globals != NULL);

PyThreadState *tstate = _PyThreadState_GET();
PyObject **fastlocals;
Py_ssize_t i;
PyObject *result;
assert(tstate != NULL);

assert(globals != NULL);
/* XXX Perhaps we should create a specialized
_PyFrame_New_NoTrack() that doesn't take locals, but does
take builtins without sanity checking them.
*/
assert(tstate != NULL);
f = _PyFrame_New_NoTrack(tstate, co, globals, NULL);
PyFrameObject *f = _PyFrame_New_NoTrack(tstate, co, globals, NULL);
if (f == NULL) {
return NULL;
}

fastlocals = f->f_localsplus;
PyObject **fastlocals = f->f_localsplus;

for (i = 0; i < nargs; i++) {
for (Py_ssize_t i = 0; i < nargs; i++) {
Py_INCREF(*args);
fastlocals[i] = *args++;
}
result = PyEval_EvalFrameEx(f,0);
PyObject *result = PyEval_EvalFrameEx(f, 0);

if (Py_REFCNT(f) > 1) {
Py_DECREF(f);
Expand Down
Loading

0 comments on commit 7e432f9

Please sign in to comment.