diff --git a/py/obj.h b/py/obj.h index 11d99ed606..592ae8afd0 100644 --- a/py/obj.h +++ b/py/obj.h @@ -330,6 +330,7 @@ extern const mp_obj_type_t mp_type_GeneratorExit; extern const mp_obj_type_t mp_type_ImportError; extern const mp_obj_type_t mp_type_IndentationError; extern const mp_obj_type_t mp_type_IndexError; +extern const mp_obj_type_t mp_type_KeyboardInterrupt; extern const mp_obj_type_t mp_type_KeyError; extern const mp_obj_type_t mp_type_LookupError; extern const mp_obj_type_t mp_type_MemoryError; diff --git a/py/objexcept.c b/py/objexcept.c index 203517b106..9b39788759 100644 --- a/py/objexcept.c +++ b/py/objexcept.c @@ -211,7 +211,7 @@ const mp_obj_type_t mp_type_ ## exc_name = { \ // http://docs.python.org/3/library/exceptions.html MP_DEFINE_EXCEPTION_BASE(BaseException) MP_DEFINE_EXCEPTION(SystemExit, BaseException) -//MP_DEFINE_EXCEPTION(KeyboardInterrupt, BaseException) +MP_DEFINE_EXCEPTION(KeyboardInterrupt, BaseException) MP_DEFINE_EXCEPTION(GeneratorExit, BaseException) MP_DEFINE_EXCEPTION(Exception, BaseException) MP_DEFINE_EXCEPTION_BASE(Exception) diff --git a/py/qstrdefs.h b/py/qstrdefs.h index c87a7a83c1..64658b587c 100644 --- a/py/qstrdefs.h +++ b/py/qstrdefs.h @@ -108,6 +108,7 @@ Q(GeneratorExit) Q(ImportError) Q(IndentationError) Q(IndexError) +Q(KeyboardInterrupt) Q(KeyError) Q(LookupError) Q(MemoryError) diff --git a/py/runtime.c b/py/runtime.c index 6cb5c7e197..f6f34be940 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -62,6 +62,9 @@ #define DEBUG_OP_printf(...) (void)0 #endif +// pending exception object (MP_OBJ_NULL if not pending) +mp_obj_t mp_pending_exception; + // locals and globals need to be pointers because they can be the same in outer module scope STATIC mp_obj_dict_t *dict_locals; STATIC mp_obj_dict_t *dict_globals; @@ -79,6 +82,9 @@ void mp_init(void) { qstr_init(); mp_stack_ctrl_init(); + // no pending exceptions to start with + mp_pending_exception = MP_OBJ_NULL; + #if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF mp_init_emergency_exception_buf(); #endif diff --git a/py/runtime.h b/py/runtime.h index 967a1fc097..5be470330b 100644 --- a/py/runtime.h +++ b/py/runtime.h @@ -116,6 +116,7 @@ mp_obj_t mp_convert_native_to_obj(mp_uint_t val, mp_uint_t type); mp_obj_t mp_native_call_function_n_kw(mp_obj_t fun_in, mp_uint_t n_args_kw, const mp_obj_t *args); NORETURN void mp_native_raise(mp_obj_t o); +extern mp_obj_t mp_pending_exception; extern struct _mp_obj_list_t mp_sys_path_obj; extern struct _mp_obj_list_t mp_sys_argv_obj; #define mp_sys_path ((mp_obj_t)&mp_sys_path_obj) diff --git a/py/vm.c b/py/vm.c index 36ea10f5c0..664f9377a8 100644 --- a/py/vm.c +++ b/py/vm.c @@ -110,10 +110,12 @@ mp_vm_return_kind_t mp_execute_bytecode(mp_code_state *code_state, volatile mp_o code_state->ip = ip; \ goto *entry_table[*ip++]; \ } while(0) + #define DISPATCH_WITH_PEND_EXC_CHECK() goto pending_exception_check #define ENTRY(op) entry_##op #define ENTRY_DEFAULT entry_default #else #define DISPATCH() break + #define DISPATCH_WITH_PEND_EXC_CHECK() goto pending_exception_check #define ENTRY(op) case op #define ENTRY_DEFAULT default #endif @@ -372,21 +374,21 @@ dispatch_loop: ENTRY(MP_BC_JUMP): DECODE_SLABEL; ip += unum; - DISPATCH(); + DISPATCH_WITH_PEND_EXC_CHECK(); ENTRY(MP_BC_POP_JUMP_IF_TRUE): DECODE_SLABEL; if (mp_obj_is_true(POP())) { ip += unum; } - DISPATCH(); + DISPATCH_WITH_PEND_EXC_CHECK(); ENTRY(MP_BC_POP_JUMP_IF_FALSE): DECODE_SLABEL; if (!mp_obj_is_true(POP())) { ip += unum; } - DISPATCH(); + DISPATCH_WITH_PEND_EXC_CHECK(); ENTRY(MP_BC_JUMP_IF_TRUE_OR_POP): DECODE_SLABEL; @@ -395,7 +397,7 @@ dispatch_loop: } else { sp--; } - DISPATCH(); + DISPATCH_WITH_PEND_EXC_CHECK(); ENTRY(MP_BC_JUMP_IF_FALSE_OR_POP): DECODE_SLABEL; @@ -404,7 +406,7 @@ dispatch_loop: } else { ip += unum; } - DISPATCH(); + DISPATCH_WITH_PEND_EXC_CHECK(); ENTRY(MP_BC_SETUP_WITH): { mp_obj_t obj = TOP(); @@ -502,7 +504,7 @@ unwind_jump: if (unum != 0) { sp--; } - DISPATCH(); + DISPATCH_WITH_PEND_EXC_CHECK(); // matched against: POP_BLOCK or POP_EXCEPT (anything else?) ENTRY(MP_BC_SETUP_EXCEPT): @@ -909,6 +911,15 @@ yield: #if !MICROPY_OPT_COMPUTED_GOTO } // switch #endif + +pending_exception_check: + if (mp_pending_exception != MP_OBJ_NULL) { + mp_obj_t obj = mp_pending_exception; + mp_pending_exception = MP_OBJ_NULL; + RAISE(obj); + } + DISPATCH(); + } // for loop } else { diff --git a/stmhal/pendsv.c b/stmhal/pendsv.c index a0eff7e5de..e71836d014 100644 --- a/stmhal/pendsv.c +++ b/stmhal/pendsv.c @@ -31,6 +31,7 @@ #include "misc.h" #include "qstr.h" #include "obj.h" +#include "runtime.h" #include "pendsv.h" static void *pendsv_object = NULL; @@ -40,12 +41,22 @@ void pendsv_init(void) { HAL_NVIC_SetPriority(PendSV_IRQn, 0xf, 0xf); } -// call this function to raise a pending exception during an interrupt -// it will wait until all interrupts are finished then raise the given -// exception object using nlr_jump in the context of the top-level thread +// Call this function to raise a pending exception during an interrupt. +// It will first try to raise the exception "softly" by setting the +// mp_pending_exception variable and hoping that the VM will notice it. +// If this function is called a second time (ie with the mp_pending_exception +// variable already set) then it will force the exception by using the hardware +// PENDSV feature. This will wait until all interrupts are finished then raise +// the given exception object using nlr_jump in the context of the top-level +// thread. void pendsv_nlr_jump(void *o) { - pendsv_object = o; - SCB->ICSR = SCB_ICSR_PENDSVSET_Msk; + if (mp_pending_exception == MP_OBJ_NULL) { + mp_pending_exception = o; + } else { + mp_pending_exception = MP_OBJ_NULL; + pendsv_object = o; + SCB->ICSR = SCB_ICSR_PENDSVSET_Msk; + } } // since we play tricks with the stack, the compiler must not generate a diff --git a/stmhal/usb.c b/stmhal/usb.c index c522b64432..4211c88296 100644 --- a/stmhal/usb.c +++ b/stmhal/usb.c @@ -50,7 +50,7 @@ STATIC mp_obj_t mp_const_vcp_interrupt = MP_OBJ_NULL; void pyb_usb_init0(void) { // create an exception object for interrupting by VCP - mp_const_vcp_interrupt = mp_obj_new_exception_msg(&mp_type_OSError, "VCPInterrupt"); + mp_const_vcp_interrupt = mp_obj_new_exception(&mp_type_KeyboardInterrupt); USBD_CDC_SetInterrupt(VCP_CHAR_NONE, mp_const_vcp_interrupt); } diff --git a/stmhal/usbd_cdc_interface.c b/stmhal/usbd_cdc_interface.c index d437955508..28f06067bb 100644 --- a/stmhal/usbd_cdc_interface.c +++ b/stmhal/usbd_cdc_interface.c @@ -374,7 +374,6 @@ static int8_t CDC_Itf_Receive(uint8_t* Buf, uint32_t *Len) { if (char_found) { // raise exception when interrupts are finished - user_interrupt_char = VCP_CHAR_NONE; pendsv_nlr_jump(user_interrupt_data); } diff --git a/unix/main.c b/unix/main.c index a8de9fdee3..44542af51b 100644 --- a/unix/main.c +++ b/unix/main.c @@ -34,6 +34,7 @@ #include #include #include +#include #include "mpconfig.h" #include "nlr.h" @@ -64,6 +65,20 @@ mp_uint_t mp_verbose_flag = 0; long heap_size = 128*1024 * (sizeof(mp_uint_t) / 4); #endif +STATIC mp_obj_t keyboard_interrupt_obj; + +STATIC void sighandler(int signum) { + if (signum == SIGINT) { + mp_obj_exception_clear_traceback(keyboard_interrupt_obj); + mp_pending_exception = keyboard_interrupt_obj; + // disable our handler so next we really die + struct sigaction sa; + sa.sa_handler = SIG_DFL; + sigemptyset(&sa.sa_mask); + sigaction(SIGINT, &sa, NULL); + } +} + #define FORCED_EXIT (0x100) // returns standard error codes: 0 for success, 1 for all other errors // if FORCED_EXIT bit is set then script raised SystemExit and the @@ -119,14 +134,23 @@ STATIC int execute_from_lexer(mp_lexer_t *lex, mp_parse_input_kind_t input_kind, return 0; } + // enable signal handler + struct sigaction sa; + sa.sa_handler = sighandler; + sigemptyset(&sa.sa_mask); + sigaction(SIGINT, &sa, NULL); + sa.sa_handler = SIG_DFL; + // execute it nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { mp_call_function_0(module_fun); + sigaction(SIGINT, &sa, NULL); nlr_pop(); return 0; } else { // uncaught exception + sigaction(SIGINT, &sa, NULL); // check for SystemExit mp_obj_t exc = (mp_obj_t)nlr.ret_val; if (mp_obj_is_subclass_fast(mp_obj_get_type(exc), &mp_type_SystemExit)) { @@ -305,6 +329,9 @@ int main(int argc, char **argv) { mp_init(); + // create keyboard interrupt object + keyboard_interrupt_obj = mp_obj_new_exception(&mp_type_KeyboardInterrupt); + char *home = getenv("HOME"); char *path = getenv("MICROPYPATH"); if (path == NULL) {