Skip to content

Commit be434dc

Browse files
authored
bpo-38644: Pass tstate to Py_EnterRecursiveCall() (GH-16997)
* 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().
1 parent f4b1e3d commit be434dc

File tree

7 files changed

+200
-135
lines changed

7 files changed

+200
-135
lines changed

‎Include/cpython/ceval.h‎

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,31 @@ PyAPI_DATA(int) _Py_CheckRecursionLimit;
1111
#ifdef USE_STACKCHECK
1212
/* With USE_STACKCHECK macro defined, trigger stack checks in
1313
_Py_CheckRecursiveCall() on every 64th call to Py_EnterRecursiveCall. */
14-
# define _Py_MakeRecCheck(x) \
15-
(++(x) > _Py_CheckRecursionLimit || \
16-
++(PyThreadState_GET()->stackcheck_counter) > 64)
14+
static inline int _Py_MakeRecCheck(PyThreadState *tstate) {
15+
return (++tstate->recursion_depth > _Py_CheckRecursionLimit
16+
|| ++tstate->stackcheck_counter > 64);
17+
}
1718
#else
18-
# define _Py_MakeRecCheck(x) (++(x) > _Py_CheckRecursionLimit)
19+
static inline int _Py_MakeRecCheck(PyThreadState *tstate) {
20+
return (++tstate->recursion_depth > _Py_CheckRecursionLimit);
21+
}
1922
#endif
2023

21-
PyAPI_FUNC(int) _Py_CheckRecursiveCall(const char *where);
24+
PyAPI_FUNC(int) _Py_CheckRecursiveCall(
25+
PyThreadState *tstate,
26+
const char *where);
27+
28+
static inline int _Py_EnterRecursiveCall(PyThreadState *tstate,
29+
const char *where) {
30+
return (_Py_MakeRecCheck(tstate) && _Py_CheckRecursiveCall(tstate, where));
31+
}
2232

23-
#define _Py_EnterRecursiveCall_macro(where) \
24-
(_Py_MakeRecCheck(PyThreadState_GET()->recursion_depth) && \
25-
_Py_CheckRecursiveCall(where))
33+
static inline int _Py_EnterRecursiveCall_inline(const char *where) {
34+
PyThreadState *tstate = PyThreadState_GET();
35+
return _Py_EnterRecursiveCall(tstate, where);
36+
}
2637

27-
#define Py_EnterRecursiveCall(where) _Py_EnterRecursiveCall_macro(where)
38+
#define Py_EnterRecursiveCall(where) _Py_EnterRecursiveCall_inline(where)
2839

2940

3041
/* Compute the "lower-water mark" for a recursion limit. When
@@ -38,12 +49,18 @@ PyAPI_FUNC(int) _Py_CheckRecursiveCall(const char *where);
3849
#define _Py_MakeEndRecCheck(x) \
3950
(--(x) < _Py_RecursionLimitLowerWaterMark(_Py_CheckRecursionLimit))
4051

41-
#define _Py_LeaveRecursiveCall_macro() \
42-
do{ if(_Py_MakeEndRecCheck(PyThreadState_GET()->recursion_depth)) \
43-
PyThreadState_GET()->overflowed = 0; \
44-
} while(0)
52+
static inline void _Py_LeaveRecursiveCall(PyThreadState *tstate) {
53+
if (_Py_MakeEndRecCheck(tstate->recursion_depth)) {
54+
tstate->overflowed = 0;
55+
}
56+
}
57+
58+
static inline void _Py_LeaveRecursiveCall_inline(void) {
59+
PyThreadState *tstate = PyThreadState_GET();
60+
_Py_LeaveRecursiveCall(tstate);
61+
}
4562

46-
#define Py_LeaveRecursiveCall() _Py_LeaveRecursiveCall_macro()
63+
#define Py_LeaveRecursiveCall() _Py_LeaveRecursiveCall_inline()
4764

4865
#ifdef __cplusplus
4966
}

‎Objects/abstract.c‎

Lines changed: 49 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/* Abstract Object Interface (many thanks to Jim Fulton) */
22

33
#include "Python.h"
4+
#include "pycore_pyerrors.h"
45
#include "pycore_pystate.h"
56
#include <ctype.h>
67
#include "structmember.h" /* we need the offsetof() macro from there */
@@ -2459,8 +2460,8 @@ recursive_isinstance(PyObject *inst, PyObject *cls)
24592460
return retval;
24602461
}
24612462

2462-
int
2463-
PyObject_IsInstance(PyObject *inst, PyObject *cls)
2463+
static int
2464+
object_isinstance(PyThreadState *tstate, PyObject *inst, PyObject *cls)
24642465
{
24652466
_Py_IDENTIFIER(__instancecheck__);
24662467
PyObject *checker;
@@ -2475,47 +2476,55 @@ PyObject_IsInstance(PyObject *inst, PyObject *cls)
24752476
}
24762477

24772478
if (PyTuple_Check(cls)) {
2478-
Py_ssize_t i;
2479-
Py_ssize_t n;
2480-
int r = 0;
2481-
2482-
if (Py_EnterRecursiveCall(" in __instancecheck__"))
2479+
if (_Py_EnterRecursiveCall(tstate, " in __instancecheck__")) {
24832480
return -1;
2484-
n = PyTuple_GET_SIZE(cls);
2485-
for (i = 0; i < n; ++i) {
2481+
}
2482+
Py_ssize_t n = PyTuple_GET_SIZE(cls);
2483+
int r = 0;
2484+
for (Py_ssize_t i = 0; i < n; ++i) {
24862485
PyObject *item = PyTuple_GET_ITEM(cls, i);
2487-
r = PyObject_IsInstance(inst, item);
2486+
r = object_isinstance(tstate, inst, item);
24882487
if (r != 0)
24892488
/* either found it, or got an error */
24902489
break;
24912490
}
2492-
Py_LeaveRecursiveCall();
2491+
_Py_LeaveRecursiveCall(tstate);
24932492
return r;
24942493
}
24952494

24962495
checker = _PyObject_LookupSpecial(cls, &PyId___instancecheck__);
24972496
if (checker != NULL) {
2498-
PyObject *res;
24992497
int ok = -1;
2500-
if (Py_EnterRecursiveCall(" in __instancecheck__")) {
2498+
if (_Py_EnterRecursiveCall(tstate, " in __instancecheck__")) {
25012499
Py_DECREF(checker);
25022500
return ok;
25032501
}
2504-
res = _PyObject_CallOneArg(checker, inst);
2505-
Py_LeaveRecursiveCall();
2502+
PyObject *res = _PyObject_CallOneArg(checker, inst);
2503+
_Py_LeaveRecursiveCall(tstate);
25062504
Py_DECREF(checker);
25072505
if (res != NULL) {
25082506
ok = PyObject_IsTrue(res);
25092507
Py_DECREF(res);
25102508
}
25112509
return ok;
25122510
}
2513-
else if (PyErr_Occurred())
2511+
else if (_PyErr_Occurred(tstate)) {
25142512
return -1;
2513+
}
2514+
25152515
/* Probably never reached anymore. */
25162516
return recursive_isinstance(inst, cls);
25172517
}
25182518

2519+
2520+
int
2521+
PyObject_IsInstance(PyObject *inst, PyObject *cls)
2522+
{
2523+
PyThreadState *tstate = _PyThreadState_GET();
2524+
return object_isinstance(tstate, inst, cls);
2525+
}
2526+
2527+
25192528
static int
25202529
recursive_issubclass(PyObject *derived, PyObject *cls)
25212530
{
@@ -2534,8 +2543,8 @@ recursive_issubclass(PyObject *derived, PyObject *cls)
25342543
return abstract_issubclass(derived, cls);
25352544
}
25362545

2537-
int
2538-
PyObject_IsSubclass(PyObject *derived, PyObject *cls)
2546+
static int
2547+
object_issubclass(PyThreadState *tstate, PyObject *derived, PyObject *cls)
25392548
{
25402549
_Py_IDENTIFIER(__subclasscheck__);
25412550
PyObject *checker;
@@ -2549,47 +2558,56 @@ PyObject_IsSubclass(PyObject *derived, PyObject *cls)
25492558
}
25502559

25512560
if (PyTuple_Check(cls)) {
2552-
Py_ssize_t i;
2553-
Py_ssize_t n;
2554-
int r = 0;
25552561

2556-
if (Py_EnterRecursiveCall(" in __subclasscheck__"))
2562+
if (_Py_EnterRecursiveCall(tstate, " in __subclasscheck__")) {
25572563
return -1;
2558-
n = PyTuple_GET_SIZE(cls);
2559-
for (i = 0; i < n; ++i) {
2564+
}
2565+
Py_ssize_t n = PyTuple_GET_SIZE(cls);
2566+
int r = 0;
2567+
for (Py_ssize_t i = 0; i < n; ++i) {
25602568
PyObject *item = PyTuple_GET_ITEM(cls, i);
2561-
r = PyObject_IsSubclass(derived, item);
2569+
r = object_issubclass(tstate, derived, item);
25622570
if (r != 0)
25632571
/* either found it, or got an error */
25642572
break;
25652573
}
2566-
Py_LeaveRecursiveCall();
2574+
_Py_LeaveRecursiveCall(tstate);
25672575
return r;
25682576
}
25692577

25702578
checker = _PyObject_LookupSpecial(cls, &PyId___subclasscheck__);
25712579
if (checker != NULL) {
2572-
PyObject *res;
25732580
int ok = -1;
2574-
if (Py_EnterRecursiveCall(" in __subclasscheck__")) {
2581+
if (_Py_EnterRecursiveCall(tstate, " in __subclasscheck__")) {
25752582
Py_DECREF(checker);
25762583
return ok;
25772584
}
2578-
res = _PyObject_CallOneArg(checker, derived);
2579-
Py_LeaveRecursiveCall();
2585+
PyObject *res = _PyObject_CallOneArg(checker, derived);
2586+
_Py_LeaveRecursiveCall(tstate);
25802587
Py_DECREF(checker);
25812588
if (res != NULL) {
25822589
ok = PyObject_IsTrue(res);
25832590
Py_DECREF(res);
25842591
}
25852592
return ok;
25862593
}
2587-
else if (PyErr_Occurred())
2594+
else if (_PyErr_Occurred(tstate)) {
25882595
return -1;
2596+
}
2597+
25892598
/* Probably never reached anymore. */
25902599
return recursive_issubclass(derived, cls);
25912600
}
25922601

2602+
2603+
int
2604+
PyObject_IsSubclass(PyObject *derived, PyObject *cls)
2605+
{
2606+
PyThreadState *tstate = _PyThreadState_GET();
2607+
return object_issubclass(tstate, derived, cls);
2608+
}
2609+
2610+
25932611
int
25942612
_PyObject_RealIsInstance(PyObject *inst, PyObject *cls)
25952613
{

‎Objects/call.c‎

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "Python.h"
22
#include "pycore_object.h"
3+
#include "pycore_pyerrors.h"
34
#include "pycore_pystate.h"
45
#include "pycore_tupleobject.h"
56
#include "frameobject.h"
@@ -126,12 +127,15 @@ _PyObject_FastCallDict(PyObject *callable, PyObject *const *args,
126127
PyObject *
127128
_PyObject_MakeTpCall(PyObject *callable, PyObject *const *args, Py_ssize_t nargs, PyObject *keywords)
128129
{
130+
PyThreadState *tstate = _PyThreadState_GET();
131+
129132
/* Slow path: build a temporary tuple for positional arguments and a
130133
* temporary dictionary for keyword arguments (if any) */
131134
ternaryfunc call = Py_TYPE(callable)->tp_call;
132135
if (call == NULL) {
133-
PyErr_Format(PyExc_TypeError, "'%.200s' object is not callable",
134-
Py_TYPE(callable)->tp_name);
136+
_PyErr_Format(tstate, PyExc_TypeError,
137+
"'%.200s' object is not callable",
138+
Py_TYPE(callable)->tp_name);
135139
return NULL;
136140
}
137141

@@ -162,10 +166,10 @@ _PyObject_MakeTpCall(PyObject *callable, PyObject *const *args, Py_ssize_t nargs
162166
}
163167

164168
PyObject *result = NULL;
165-
if (Py_EnterRecursiveCall(" while calling a Python object") == 0)
169+
if (_Py_EnterRecursiveCall(tstate, " while calling a Python object") == 0)
166170
{
167171
result = call(callable, argstuple, kwdict);
168-
Py_LeaveRecursiveCall();
172+
_Py_LeaveRecursiveCall(tstate);
169173
}
170174

171175
Py_DECREF(argstuple);
@@ -220,13 +224,14 @@ PyVectorcall_Call(PyObject *callable, PyObject *tuple, PyObject *kwargs)
220224
PyObject *
221225
PyObject_Call(PyObject *callable, PyObject *args, PyObject *kwargs)
222226
{
227+
PyThreadState *tstate = _PyThreadState_GET();
223228
ternaryfunc call;
224229
PyObject *result;
225230

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

@@ -236,17 +241,19 @@ PyObject_Call(PyObject *callable, PyObject *args, PyObject *kwargs)
236241
else {
237242
call = callable->ob_type->tp_call;
238243
if (call == NULL) {
239-
PyErr_Format(PyExc_TypeError, "'%.200s' object is not callable",
240-
callable->ob_type->tp_name);
244+
_PyErr_Format(tstate, PyExc_TypeError,
245+
"'%.200s' object is not callable",
246+
callable->ob_type->tp_name);
241247
return NULL;
242248
}
243249

244-
if (Py_EnterRecursiveCall(" while calling a Python object"))
250+
if (_Py_EnterRecursiveCall(tstate, " while calling a Python object")) {
245251
return NULL;
252+
}
246253

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

249-
Py_LeaveRecursiveCall();
256+
_Py_LeaveRecursiveCall(tstate);
250257

251258
return _Py_CheckFunctionResult(callable, result, NULL);
252259
}
@@ -266,30 +273,27 @@ static PyObject* _Py_HOT_FUNCTION
266273
function_code_fastcall(PyCodeObject *co, PyObject *const *args, Py_ssize_t nargs,
267274
PyObject *globals)
268275
{
269-
PyFrameObject *f;
276+
assert(globals != NULL);
277+
270278
PyThreadState *tstate = _PyThreadState_GET();
271-
PyObject **fastlocals;
272-
Py_ssize_t i;
273-
PyObject *result;
279+
assert(tstate != NULL);
274280

275-
assert(globals != NULL);
276281
/* XXX Perhaps we should create a specialized
277282
_PyFrame_New_NoTrack() that doesn't take locals, but does
278283
take builtins without sanity checking them.
279284
*/
280-
assert(tstate != NULL);
281-
f = _PyFrame_New_NoTrack(tstate, co, globals, NULL);
285+
PyFrameObject *f = _PyFrame_New_NoTrack(tstate, co, globals, NULL);
282286
if (f == NULL) {
283287
return NULL;
284288
}
285289

286-
fastlocals = f->f_localsplus;
290+
PyObject **fastlocals = f->f_localsplus;
287291

288-
for (i = 0; i < nargs; i++) {
292+
for (Py_ssize_t i = 0; i < nargs; i++) {
289293
Py_INCREF(*args);
290294
fastlocals[i] = *args++;
291295
}
292-
result = PyEval_EvalFrameEx(f,0);
296+
PyObject *result = PyEval_EvalFrameEx(f, 0);
293297

294298
if (Py_REFCNT(f) > 1) {
295299
Py_DECREF(f);

0 commit comments

Comments
 (0)