import unittest
from collections import deque
from contextlib import _GeneratorContextManager, contextmanager, nullcontext
+from _testinternalcapi import SelfInterruptingContextManager
def do_with(obj):
expected)
+class InterruptDuringEnter(unittest.TestCase):
+
+ def test_exit_called_after_interrupt(self):
+ cm = SelfInterruptingContextManager()
+ self.assertFalse(cm.within())
+ try:
+ with cm:
+ self.assertTrue(cm.within())
+ except KeyboardInterrupt:
+ self.assertFalse(cm.within())
+ return
+ except:
+ self.fail("Wrong exception raised")
+ self.fail("No exception raised")
+
+
if __name__ == '__main__':
unittest.main()
--- /dev/null
+Ignore interrupts immediately after calling the ``__enter__`` method of a
+context menager in a ``with`` statement. This ensures that the ``__exit__``
+method is always called in a ``with`` statement.
Py_RETURN_NONE;
}
+/* Self interrupting context manager */
+
+typedef struct {
+ PyObject_HEAD
+ int within;
+} SelfInterruptingContextManagerObject;
+
+static PyObject *
+new_self_interrupting(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ SelfInterruptingContextManagerObject *self =
+ (SelfInterruptingContextManagerObject *)type->tp_alloc(type, 0);
+ if (self != NULL) {
+ self->within = 0;
+ }
+ return (PyObject *)self;
+}
+
+static PyObject *
+self_interrupting_enter(PyObject *op, PyObject *Py_UNUSED(dummy))
+{
+ ((SelfInterruptingContextManagerObject *)op)->within = 1;
+ PyThreadState *tstate = PyThreadState_Get();
+ PyObject *ki = Py_NewRef(PyExc_KeyboardInterrupt);
+ PyObject *old_exc = _Py_atomic_exchange_ptr(&tstate->async_exc, ki);
+ _Py_set_eval_breaker_bit(tstate, _PY_ASYNC_EXCEPTION_BIT);
+ Py_XDECREF(old_exc);
+
+ return Py_NewRef(op);
+}
+
+static PyObject *
+self_interrupting_within(PyObject *op, PyObject *Py_UNUSED(dummy))
+{
+ return PyBool_FromLong(((SelfInterruptingContextManagerObject *)op)->within);
+}
+
+static PyObject *
+self_interrupting_exit(PyObject *op, PyObject *Py_UNUSED(args)) {
+ ((SelfInterruptingContextManagerObject *)op)->within = 0;
+ Py_RETURN_NONE;
+}
+
+static PyMethodDef self_interrupting_methods[] = {
+ {"__enter__", self_interrupting_enter, METH_NOARGS, NULL},
+ {"within", self_interrupting_within, METH_NOARGS, NULL},
+ {"__exit__", self_interrupting_exit, METH_VARARGS, NULL},
+ {NULL, NULL} /* sentinel */
+};
+
+static PyTypeObject SelfInterruptingContextManager_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "_testcapi.SelfInterruptingContextManager",
+ sizeof(SelfInterruptingContextManagerObject),
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE,
+ .tp_new = new_self_interrupting,
+ .tp_methods = self_interrupting_methods,
+};
+
+
static PyMethodDef module_functions[] = {
{"get_configs", get_configs, METH_NOARGS},
{"get_eval_frame_stats", get_eval_frame_stats, METH_NOARGS, NULL},
}
#endif
+ if (PyType_Ready(&SelfInterruptingContextManager_Type) < 0) {
+ return 1;
+ }
+ PyModule_AddObject(module, "SelfInterruptingContextManager", (PyObject *)&SelfInterruptingContextManager_Type);
+
return 0;
}
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
_PyFrame_SetStackPointer(frame, stack_pointer);
_PyFrame_StackPointerValidate(frame);
- int err = check_periodics(tstate);
+ int err = check_periodics_at_end(tstate, frame);
_PyFrame_StackPointerInvalidate(frame);
if (err != 0) {
JUMP_TO_LABEL(error);
{
assert(stack_pointer == _PyFrame_GetStackPointer(frame));
_PyFrame_StackPointerValidate(frame);
- int err = check_periodics(tstate);
+ int err = check_periodics_at_end(tstate, frame);
_PyFrame_StackPointerInvalidate(frame);
if (err != 0) {
JUMP_TO_LABEL(error);
{
assert(stack_pointer == _PyFrame_GetStackPointer(frame));
_PyFrame_StackPointerValidate(frame);
- int err = check_periodics(tstate);
+ int err = check_periodics_at_end(tstate, frame);
_PyFrame_StackPointerInvalidate(frame);
if (err != 0) {
JUMP_TO_LABEL(error);
{
assert(stack_pointer == _PyFrame_GetStackPointer(frame));
_PyFrame_StackPointerValidate(frame);
- int err = check_periodics(tstate);
+ int err = check_periodics_at_end(tstate, frame);
_PyFrame_StackPointerInvalidate(frame);
if (err != 0) {
JUMP_TO_LABEL(error);
{
assert(stack_pointer == _PyFrame_GetStackPointer(frame));
_PyFrame_StackPointerValidate(frame);
- int err = check_periodics(tstate);
+ int err = check_periodics_at_end(tstate, frame);
_PyFrame_StackPointerInvalidate(frame);
if (err != 0) {
JUMP_TO_LABEL(error);
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
_PyFrame_SetStackPointer(frame, stack_pointer);
_PyFrame_StackPointerValidate(frame);
- int err = check_periodics(tstate);
+ int err = check_periodics_at_end(tstate, frame);
_PyFrame_StackPointerInvalidate(frame);
if (err != 0) {
JUMP_TO_LABEL(error);
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
_PyFrame_SetStackPointer(frame, stack_pointer);
_PyFrame_StackPointerValidate(frame);
- int err = check_periodics(tstate);
+ int err = check_periodics_at_end(tstate, frame);
_PyFrame_StackPointerInvalidate(frame);
if (err != 0) {
JUMP_TO_LABEL(error);
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
_PyFrame_SetStackPointer(frame, stack_pointer);
_PyFrame_StackPointerValidate(frame);
- int err = check_periodics(tstate);
+ int err = check_periodics_at_end(tstate, frame);
_PyFrame_StackPointerInvalidate(frame);
if (err != 0) {
JUMP_TO_LABEL(error);
{
assert(stack_pointer == _PyFrame_GetStackPointer(frame));
_PyFrame_StackPointerValidate(frame);
- int err = check_periodics(tstate);
+ int err = check_periodics_at_end(tstate, frame);
_PyFrame_StackPointerInvalidate(frame);
if (err != 0) {
JUMP_TO_LABEL(error);
{
assert(stack_pointer == _PyFrame_GetStackPointer(frame));
_PyFrame_StackPointerValidate(frame);
- int err = check_periodics(tstate);
+ int err = check_periodics_at_end(tstate, frame);
_PyFrame_StackPointerInvalidate(frame);
if (err != 0) {
JUMP_TO_LABEL(error);
{
assert(stack_pointer == _PyFrame_GetStackPointer(frame));
_PyFrame_StackPointerValidate(frame);
- int err = check_periodics(tstate);
+ int err = check_periodics_at_end(tstate, frame);
_PyFrame_StackPointerInvalidate(frame);
if (err != 0) {
JUMP_TO_LABEL(error);
{
assert(stack_pointer == _PyFrame_GetStackPointer(frame));
_PyFrame_StackPointerValidate(frame);
- int err = check_periodics(tstate);
+ int err = check_periodics_at_end(tstate, frame);
_PyFrame_StackPointerInvalidate(frame);
if (err != 0) {
JUMP_TO_LABEL(error);
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
_PyFrame_SetStackPointer(frame, stack_pointer);
_PyFrame_StackPointerValidate(frame);
- int err = check_periodics(tstate);
+ int err = check_periodics_at_end(tstate, frame);
_PyFrame_StackPointerInvalidate(frame);
if (err != 0) {
JUMP_TO_LABEL(error);
{
assert(stack_pointer == _PyFrame_GetStackPointer(frame));
_PyFrame_StackPointerValidate(frame);
- int err = check_periodics(tstate);
+ int err = check_periodics_at_end(tstate, frame);
_PyFrame_StackPointerInvalidate(frame);
if (err != 0) {
JUMP_TO_LABEL(error);
{
assert(stack_pointer == _PyFrame_GetStackPointer(frame));
_PyFrame_StackPointerValidate(frame);
- int err = check_periodics(tstate);
+ int err = check_periodics_at_end(tstate, frame);
_PyFrame_StackPointerInvalidate(frame);
if (err != 0) {
JUMP_TO_LABEL(error);
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
_PyFrame_SetStackPointer(frame, stack_pointer);
_PyFrame_StackPointerValidate(frame);
- int err = check_periodics(tstate);
+ int err = check_periodics_at_end(tstate, frame);
_PyFrame_StackPointerInvalidate(frame);
if (err != 0) {
JUMP_TO_LABEL(error);
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
_PyFrame_SetStackPointer(frame, stack_pointer);
_PyFrame_StackPointerValidate(frame);
- int err = check_periodics(tstate);
+ int err = check_periodics_at_end(tstate, frame);
_PyFrame_StackPointerInvalidate(frame);
if (err != 0) {
JUMP_TO_LABEL(error);
}
replaced op(_CHECK_PERIODIC_AT_END, (--)) {
- int err = check_periodics(tstate);
+ int err = check_periodics_at_end(tstate, frame);
ERROR_IF(err != 0);
}
return 0;
}
+static inline int
+check_periodics_at_end(PyThreadState *tstate, _PyInterpreterFrame *frame) {
+ _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY();
+ QSBR_QUIESCENT_STATE(tstate);
+ if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) {
+ // Do not handle pending interrupts if the previous instruction was LOAD_SPECIAL
+ // This may also not handle interrupts if a cache looks like LOAD_SPECIAL,
+ // but this is benign as we won't skip periodic checks indefinitely.
+ if (frame->instr_ptr[-1].op.code == LOAD_SPECIAL) {
+ return 0;
+ }
+ return _Py_HandlePending(tstate);
+ }
+ return 0;
+}
+
// Mark the generator as executing. Returns true if the state was changed,
// false if it was already executing or finished.
static inline bool
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
_PyFrame_SetStackPointer(frame, stack_pointer);
_PyFrame_StackPointerValidate(frame);
- int err = check_periodics(tstate);
+ int err = check_periodics_at_end(tstate, frame);
_PyFrame_StackPointerInvalidate(frame);
if (err != 0) {
JUMP_TO_LABEL(error);
{
assert(stack_pointer == _PyFrame_GetStackPointer(frame));
_PyFrame_StackPointerValidate(frame);
- int err = check_periodics(tstate);
+ int err = check_periodics_at_end(tstate, frame);
_PyFrame_StackPointerInvalidate(frame);
if (err != 0) {
JUMP_TO_LABEL(error);
{
assert(stack_pointer == _PyFrame_GetStackPointer(frame));
_PyFrame_StackPointerValidate(frame);
- int err = check_periodics(tstate);
+ int err = check_periodics_at_end(tstate, frame);
_PyFrame_StackPointerInvalidate(frame);
if (err != 0) {
JUMP_TO_LABEL(error);
{
assert(stack_pointer == _PyFrame_GetStackPointer(frame));
_PyFrame_StackPointerValidate(frame);
- int err = check_periodics(tstate);
+ int err = check_periodics_at_end(tstate, frame);
_PyFrame_StackPointerInvalidate(frame);
if (err != 0) {
JUMP_TO_LABEL(error);
{
assert(stack_pointer == _PyFrame_GetStackPointer(frame));
_PyFrame_StackPointerValidate(frame);
- int err = check_periodics(tstate);
+ int err = check_periodics_at_end(tstate, frame);
_PyFrame_StackPointerInvalidate(frame);
if (err != 0) {
JUMP_TO_LABEL(error);
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
_PyFrame_SetStackPointer(frame, stack_pointer);
_PyFrame_StackPointerValidate(frame);
- int err = check_periodics(tstate);
+ int err = check_periodics_at_end(tstate, frame);
_PyFrame_StackPointerInvalidate(frame);
if (err != 0) {
JUMP_TO_LABEL(error);
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
_PyFrame_SetStackPointer(frame, stack_pointer);
_PyFrame_StackPointerValidate(frame);
- int err = check_periodics(tstate);
+ int err = check_periodics_at_end(tstate, frame);
_PyFrame_StackPointerInvalidate(frame);
if (err != 0) {
JUMP_TO_LABEL(error);
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
_PyFrame_SetStackPointer(frame, stack_pointer);
_PyFrame_StackPointerValidate(frame);
- int err = check_periodics(tstate);
+ int err = check_periodics_at_end(tstate, frame);
_PyFrame_StackPointerInvalidate(frame);
if (err != 0) {
JUMP_TO_LABEL(error);
{
assert(stack_pointer == _PyFrame_GetStackPointer(frame));
_PyFrame_StackPointerValidate(frame);
- int err = check_periodics(tstate);
+ int err = check_periodics_at_end(tstate, frame);
_PyFrame_StackPointerInvalidate(frame);
if (err != 0) {
JUMP_TO_LABEL(error);
{
assert(stack_pointer == _PyFrame_GetStackPointer(frame));
_PyFrame_StackPointerValidate(frame);
- int err = check_periodics(tstate);
+ int err = check_periodics_at_end(tstate, frame);
_PyFrame_StackPointerInvalidate(frame);
if (err != 0) {
JUMP_TO_LABEL(error);
{
assert(stack_pointer == _PyFrame_GetStackPointer(frame));
_PyFrame_StackPointerValidate(frame);
- int err = check_periodics(tstate);
+ int err = check_periodics_at_end(tstate, frame);
_PyFrame_StackPointerInvalidate(frame);
if (err != 0) {
JUMP_TO_LABEL(error);
{
assert(stack_pointer == _PyFrame_GetStackPointer(frame));
_PyFrame_StackPointerValidate(frame);
- int err = check_periodics(tstate);
+ int err = check_periodics_at_end(tstate, frame);
_PyFrame_StackPointerInvalidate(frame);
if (err != 0) {
JUMP_TO_LABEL(error);
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
_PyFrame_SetStackPointer(frame, stack_pointer);
_PyFrame_StackPointerValidate(frame);
- int err = check_periodics(tstate);
+ int err = check_periodics_at_end(tstate, frame);
_PyFrame_StackPointerInvalidate(frame);
if (err != 0) {
JUMP_TO_LABEL(error);
{
assert(stack_pointer == _PyFrame_GetStackPointer(frame));
_PyFrame_StackPointerValidate(frame);
- int err = check_periodics(tstate);
+ int err = check_periodics_at_end(tstate, frame);
_PyFrame_StackPointerInvalidate(frame);
if (err != 0) {
JUMP_TO_LABEL(error);
{
assert(stack_pointer == _PyFrame_GetStackPointer(frame));
_PyFrame_StackPointerValidate(frame);
- int err = check_periodics(tstate);
+ int err = check_periodics_at_end(tstate, frame);
_PyFrame_StackPointerInvalidate(frame);
if (err != 0) {
JUMP_TO_LABEL(error);
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
_PyFrame_SetStackPointer(frame, stack_pointer);
_PyFrame_StackPointerValidate(frame);
- int err = check_periodics(tstate);
+ int err = check_periodics_at_end(tstate, frame);
_PyFrame_StackPointerInvalidate(frame);
if (err != 0) {
JUMP_TO_LABEL(error);
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
_PyFrame_SetStackPointer(frame, stack_pointer);
_PyFrame_StackPointerValidate(frame);
- int err = check_periodics(tstate);
+ int err = check_periodics_at_end(tstate, frame);
_PyFrame_StackPointerInvalidate(frame);
if (err != 0) {
JUMP_TO_LABEL(error);
case OPARG_REPLACED:
uop = _PyUOp_Replacements[uop];
assert(uop != 0);
-
uint32_t next_inst = target + 1 + _PyOpcode_Caches[_PyOpcode_Deopt[opcode]];
if (uop == _TIER2_RESUME_CHECK) {
- target = next_inst;
+ if (this_instr[-1].op.code == LOAD_SPECIAL) {
+ // Don't check eval breaker immediately after LOAD_SPECIAL
+ uop = _NOP;
+ }
+ else {
+ target = next_inst;
+ }
}
else {
int extended_arg = orig_oparg > 255;
Modules/_testinternalcapi.c - pending_identify_result -
Modules/_testinternalcapi.c - Test_EvalFrame_Resumes -
Modules/_testinternalcapi.c - Test_EvalFrame_Loads -
+Modules/_testinternalcapi.c - SelfInterruptingContextManager_Type -
Modules/_testinternalcapi/interpreter.c - Test_EvalFrame_Resumes -
Modules/_testinternalcapi/interpreter.c - Test_EvalFrame_Loads -
Modules/_testlimitedcapi/slots.c - TestMethods -