summaryrefslogtreecommitdiff
path: root/Python/ceval.c
diff options
context:
space:
mode:
authorJason R. Coombs <jaraco@jaraco.com>2020-12-27 12:46:59 -0500
committerJason R. Coombs <jaraco@jaraco.com>2020-12-27 12:46:59 -0500
commita78f0158a28734f965218b834ea8c0b166b7353f (patch)
treedca70268e2a41d49658e7eed783c6fc243d119cd /Python/ceval.c
parentec8e6895a3ce9cd69b6ceb75a15fcc74d4a522dc (diff)
parentbf64d9064ab641b1ef9a0c4bda097ebf1204faf4 (diff)
downloadcpython-git-revert-23107-revert-13893-fix-issue-37193.tar.gz
Merge branch 'master' into revert-23107-revert-13893-fix-issue-37193revert-23107-revert-13893-fix-issue-37193
Diffstat (limited to 'Python/ceval.c')
-rw-r--r--Python/ceval.c147
1 files changed, 81 insertions, 66 deletions
diff --git a/Python/ceval.c b/Python/ceval.c
index 13b209fc70..f0f39539c9 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -66,8 +66,8 @@ static void call_exc_trace(Py_tracefunc, PyObject *,
PyThreadState *, PyFrameObject *);
static int maybe_call_line_trace(Py_tracefunc, PyObject *,
PyThreadState *, PyFrameObject *,
- int *, int *, int *);
-static void maybe_dtrace_line(PyFrameObject *, int *, int *, int *);
+ PyCodeAddressRange *, int *);
+static void maybe_dtrace_line(PyFrameObject *, PyCodeAddressRange *, int *);
static void dtrace_function_entry(PyFrameObject *);
static void dtrace_function_return(PyFrameObject *);
@@ -203,13 +203,18 @@ UNSIGNAL_PENDING_CALLS(PyInterpreterState *interp)
static inline void
-SIGNAL_PENDING_SIGNALS(PyInterpreterState *interp)
+SIGNAL_PENDING_SIGNALS(PyInterpreterState *interp, int force)
{
struct _ceval_runtime_state *ceval = &interp->runtime->ceval;
struct _ceval_state *ceval2 = &interp->ceval;
_Py_atomic_store_relaxed(&ceval->signals_pending, 1);
- /* eval_breaker is not set to 1 if thread_can_handle_signals() is false */
- COMPUTE_EVAL_BREAKER(interp, ceval, ceval2);
+ if (force) {
+ _Py_atomic_store_relaxed(&ceval2->eval_breaker, 1);
+ }
+ else {
+ /* eval_breaker is not set to 1 if thread_can_handle_signals() is false */
+ COMPUTE_EVAL_BREAKER(interp, ceval, ceval2);
+ }
}
@@ -559,10 +564,22 @@ PyEval_RestoreThread(PyThreadState *tstate)
void
_PyEval_SignalReceived(PyInterpreterState *interp)
{
+#ifdef MS_WINDOWS
+ // bpo-42296: On Windows, _PyEval_SignalReceived() is called from a signal
+ // handler which can run in a thread different than the Python thread, in
+ // which case _Py_ThreadCanHandleSignals() is wrong. Ignore
+ // _Py_ThreadCanHandleSignals() and always set eval_breaker to 1.
+ //
+ // The next eval_frame_handle_pending() call will call
+ // _Py_ThreadCanHandleSignals() to recompute eval_breaker.
+ int force = 1;
+#else
+ int force = 0;
+#endif
/* bpo-30703: Function called when the C signal handler of Python gets a
signal. We cannot queue a callback using _PyEval_AddPendingCall() since
that function is not async-signal-safe. */
- SIGNAL_PENDING_SIGNALS(interp);
+ SIGNAL_PENDING_SIGNALS(interp, force);
}
/* Push one item onto the queue while holding the lock. */
@@ -662,7 +679,7 @@ handle_signals(PyThreadState *tstate)
UNSIGNAL_PENDING_SIGNALS(tstate->interp);
if (_PyErr_CheckSignalsTstate(tstate) < 0) {
/* On failure, re-schedule a call to handle_signals(). */
- SIGNAL_PENDING_SIGNALS(tstate->interp);
+ SIGNAL_PENDING_SIGNALS(tstate->interp, 0);
return -1;
}
return 0;
@@ -840,20 +857,22 @@ _Py_CheckRecursiveCall(PyThreadState *tstate, const char *where)
return -1;
}
#endif
- if (tstate->overflowed) {
+ if (tstate->recursion_headroom) {
if (tstate->recursion_depth > recursion_limit + 50) {
/* Overflowing while handling an overflow. Give up. */
Py_FatalError("Cannot recover from stack overflow.");
}
- return 0;
}
- if (tstate->recursion_depth > recursion_limit) {
- --tstate->recursion_depth;
- tstate->overflowed = 1;
- _PyErr_Format(tstate, PyExc_RecursionError,
- "maximum recursion depth exceeded%s",
- where);
- return -1;
+ else {
+ if (tstate->recursion_depth > recursion_limit) {
+ tstate->recursion_headroom++;
+ _PyErr_Format(tstate, PyExc_RecursionError,
+ "maximum recursion depth exceeded%s",
+ where);
+ tstate->recursion_headroom--;
+ --tstate->recursion_depth;
+ return -1;
+ }
}
return 0;
}
@@ -948,6 +967,17 @@ eval_frame_handle_pending(PyThreadState *tstate)
return -1;
}
+#ifdef MS_WINDOWS
+ // bpo-42296: On Windows, _PyEval_SignalReceived() can be called in a
+ // different thread than the Python thread, in which case
+ // _Py_ThreadCanHandleSignals() is wrong. Recompute eval_breaker in the
+ // current Python thread with the correct _Py_ThreadCanHandleSignals()
+ // value. It prevents to interrupt the eval loop at every instruction if
+ // the current Python thread cannot handle signals (if
+ // _Py_ThreadCanHandleSignals() is false).
+ COMPUTE_EVAL_BREAKER(tstate->interp, ceval, ceval2);
+#endif
+
return 0;
}
@@ -976,7 +1006,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
is true when the line being executed has changed. The
initial values are such as to make this false the first
time it is tested. */
- int instr_ub = -1, instr_lb = 0, instr_prev = -1;
const _Py_CODEUNIT *first_instr;
PyObject *names;
@@ -1390,6 +1419,10 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
dtrace_function_entry(f);
co = f->f_code;
+ PyCodeAddressRange bounds;
+ _PyCode_InitAddressRange(co, &bounds);
+ int instr_prev = -1;
+
names = co->co_names;
consts = co->co_consts;
fastlocals = f->f_localsplus;
@@ -1514,7 +1547,7 @@ main_loop:
f->f_lasti = INSTR_OFFSET();
if (PyDTrace_LINE_ENABLED())
- maybe_dtrace_line(f, &instr_lb, &instr_ub, &instr_prev);
+ maybe_dtrace_line(f, &bounds, &instr_prev);
/* line-by-line tracing support */
@@ -1528,7 +1561,7 @@ main_loop:
err = maybe_call_line_trace(tstate->c_tracefunc,
tstate->c_traceobj,
tstate, f,
- &instr_lb, &instr_ub, &instr_prev);
+ &bounds, &instr_prev);
/* Reload possibly changed frame fields */
JUMPTO(f->f_lasti);
stack_pointer = f->f_valuestack+f->f_stackdepth;
@@ -2397,6 +2430,10 @@ main_loop:
}
case TARGET(RERAISE): {
+ assert(f->f_iblock > 0);
+ if (oparg) {
+ f->f_lasti = f->f_blockstack[f->f_iblock-1].b_handler;
+ }
PyObject *exc = POP();
PyObject *val = POP();
PyObject *tb = POP();
@@ -3179,7 +3216,6 @@ main_loop:
if (co_opcache != NULL && /* co_opcache can be NULL after a DEOPT() call. */
type->tp_getattro == PyObject_GenericGetAttr)
{
- PyObject *descr;
Py_ssize_t ret;
if (type->tp_dictoffset > 0) {
@@ -3190,12 +3226,7 @@ main_loop:
goto error;
}
}
-
- descr = _PyType_Lookup(type, name);
- if (descr == NULL ||
- Py_TYPE(descr)->tp_descr_get == NULL ||
- !PyDescr_IsData(descr))
- {
+ if (_PyType_Lookup(type, name) == NULL) {
dictptr = (PyObject **) ((char *)owner + type->tp_dictoffset);
dict = *dictptr;
@@ -3855,7 +3886,7 @@ main_loop:
func ->func_closure = POP();
}
if (oparg & 0x04) {
- assert(PyDict_CheckExact(TOP()));
+ assert(PyTuple_CheckExact(TOP()));
func->func_annotations = POP();
}
if (oparg & 0x02) {
@@ -4012,7 +4043,7 @@ exception_unwind:
int handler = b->b_handler;
_PyErr_StackItem *exc_info = tstate->exc_info;
/* Beware, this invalidates all b->b_* fields */
- PyFrame_BlockSetup(f, EXCEPT_HANDLER, -1, STACK_LEVEL());
+ PyFrame_BlockSetup(f, EXCEPT_HANDLER, f->f_lasti, STACK_LEVEL());
PUSH(exc_info->exc_traceback);
PUSH(exc_info->exc_value);
if (exc_info->exc_type != NULL) {
@@ -4045,14 +4076,7 @@ exception_unwind:
PUSH(exc);
JUMPTO(handler);
if (_Py_TracingPossible(ceval2)) {
- int needs_new_execution_window = (f->f_lasti < instr_lb || f->f_lasti >= instr_ub);
- int needs_line_update = (f->f_lasti == instr_lb || f->f_lasti < instr_prev);
- /* Make sure that we trace line after exception if we are in a new execution
- * window or we don't need a line update and we are not in the first instruction
- * of the line. */
- if (needs_new_execution_window || (!needs_line_update && instr_lb > 0)) {
- instr_prev = INT_MAX;
- }
+ instr_prev = INT_MAX;
}
/* Resume normal execution */
f->f_state = FRAME_EXECUTING;
@@ -4966,7 +4990,7 @@ _PyEval_CallTracing(PyObject *func, PyObject *args)
static int
maybe_call_line_trace(Py_tracefunc func, PyObject *obj,
PyThreadState *tstate, PyFrameObject *frame,
- int *instr_lb, int *instr_ub, int *instr_prev)
+ PyCodeAddressRange *bounds, int *instr_prev)
{
int result = 0;
int line = frame->f_lineno;
@@ -4974,21 +4998,17 @@ maybe_call_line_trace(Py_tracefunc func, PyObject *obj,
/* If the last instruction executed isn't in the current
instruction window, reset the window.
*/
- if (frame->f_lasti < *instr_lb || frame->f_lasti >= *instr_ub) {
- PyAddrPair bounds;
- line = _PyCode_CheckLineNumber(frame->f_code, frame->f_lasti,
- &bounds);
- *instr_lb = bounds.ap_lower;
- *instr_ub = bounds.ap_upper;
- }
+ line = _PyCode_CheckLineNumber(frame->f_lasti, bounds);
/* If the last instruction falls at the start of a line or if it
represents a jump backwards, update the frame's line number and
then call the trace function if we're tracing source lines.
*/
- if ((frame->f_lasti == *instr_lb || frame->f_lasti < *instr_prev)) {
- frame->f_lineno = line;
- if (frame->f_trace_lines) {
- result = call_trace(func, obj, tstate, frame, PyTrace_LINE, Py_None);
+ if ((line != frame->f_lineno || frame->f_lasti < *instr_prev)) {
+ if (line != -1) {
+ frame->f_lineno = line;
+ if (frame->f_trace_lines) {
+ result = call_trace(func, obj, tstate, frame, PyTrace_LINE, Py_None);
+ }
}
}
/* Always emit an opcode event if we're tracing all opcodes. */
@@ -5913,33 +5933,28 @@ dtrace_function_return(PyFrameObject *f)
/* DTrace equivalent of maybe_call_line_trace. */
static void
maybe_dtrace_line(PyFrameObject *frame,
- int *instr_lb, int *instr_ub, int *instr_prev)
+ PyCodeAddressRange *bounds, int *instr_prev)
{
- int line = frame->f_lineno;
const char *co_filename, *co_name;
/* If the last instruction executed isn't in the current
instruction window, reset the window.
*/
- if (frame->f_lasti < *instr_lb || frame->f_lasti >= *instr_ub) {
- PyAddrPair bounds;
- line = _PyCode_CheckLineNumber(frame->f_code, frame->f_lasti,
- &bounds);
- *instr_lb = bounds.ap_lower;
- *instr_ub = bounds.ap_upper;
- }
+ int line = _PyCode_CheckLineNumber(frame->f_lasti, bounds);
/* If the last instruction falls at the start of a line or if
it represents a jump backwards, update the frame's line
number and call the trace function. */
- if (frame->f_lasti == *instr_lb || frame->f_lasti < *instr_prev) {
- frame->f_lineno = line;
- co_filename = PyUnicode_AsUTF8(frame->f_code->co_filename);
- if (!co_filename)
- co_filename = "?";
- co_name = PyUnicode_AsUTF8(frame->f_code->co_name);
- if (!co_name)
- co_name = "?";
- PyDTrace_LINE(co_filename, co_name, line);
+ if (line != frame->f_lineno || frame->f_lasti < *instr_prev) {
+ if (line != -1) {
+ frame->f_lineno = line;
+ co_filename = PyUnicode_AsUTF8(frame->f_code->co_filename);
+ if (!co_filename)
+ co_filename = "?";
+ co_name = PyUnicode_AsUTF8(frame->f_code->co_name);
+ if (!co_name)
+ co_name = "?";
+ PyDTrace_LINE(co_filename, co_name, line);
+ }
}
*instr_prev = frame->f_lasti;
}