Skip to content
Snippets Groups Projects
Commit 124df6f8 authored by Damien George's avatar Damien George
Browse files

py: Add mp_pending_exception global variable, for VM soft interrupt.

This allows to implement KeyboardInterrupt on unix, and a much safer
ctrl-C in stmhal port.  First ctrl-C is a soft one, with hope that VM
will notice it; second ctrl-C is a hard one that kills anything (for
both unix and stmhal).

One needs to check for a pending exception in the VM only for jump
opcodes.  Others can't produce an infinite loop (infinite recursion is
caught by stack check).
parent d7353fe6
Branches
No related tags found
No related merge requests found
...@@ -330,6 +330,7 @@ extern const mp_obj_type_t mp_type_GeneratorExit; ...@@ -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_ImportError;
extern const mp_obj_type_t mp_type_IndentationError; 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_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_KeyError;
extern const mp_obj_type_t mp_type_LookupError; extern const mp_obj_type_t mp_type_LookupError;
extern const mp_obj_type_t mp_type_MemoryError; extern const mp_obj_type_t mp_type_MemoryError;
......
...@@ -211,7 +211,7 @@ const mp_obj_type_t mp_type_ ## exc_name = { \ ...@@ -211,7 +211,7 @@ const mp_obj_type_t mp_type_ ## exc_name = { \
// http://docs.python.org/3/library/exceptions.html // http://docs.python.org/3/library/exceptions.html
MP_DEFINE_EXCEPTION_BASE(BaseException) MP_DEFINE_EXCEPTION_BASE(BaseException)
MP_DEFINE_EXCEPTION(SystemExit, BaseException) MP_DEFINE_EXCEPTION(SystemExit, BaseException)
//MP_DEFINE_EXCEPTION(KeyboardInterrupt, BaseException) MP_DEFINE_EXCEPTION(KeyboardInterrupt, BaseException)
MP_DEFINE_EXCEPTION(GeneratorExit, BaseException) MP_DEFINE_EXCEPTION(GeneratorExit, BaseException)
MP_DEFINE_EXCEPTION(Exception, BaseException) MP_DEFINE_EXCEPTION(Exception, BaseException)
MP_DEFINE_EXCEPTION_BASE(Exception) MP_DEFINE_EXCEPTION_BASE(Exception)
......
...@@ -108,6 +108,7 @@ Q(GeneratorExit) ...@@ -108,6 +108,7 @@ Q(GeneratorExit)
Q(ImportError) Q(ImportError)
Q(IndentationError) Q(IndentationError)
Q(IndexError) Q(IndexError)
Q(KeyboardInterrupt)
Q(KeyError) Q(KeyError)
Q(LookupError) Q(LookupError)
Q(MemoryError) Q(MemoryError)
......
...@@ -62,6 +62,9 @@ ...@@ -62,6 +62,9 @@
#define DEBUG_OP_printf(...) (void)0 #define DEBUG_OP_printf(...) (void)0
#endif #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 // 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_locals;
STATIC mp_obj_dict_t *dict_globals; STATIC mp_obj_dict_t *dict_globals;
...@@ -79,6 +82,9 @@ void mp_init(void) { ...@@ -79,6 +82,9 @@ void mp_init(void) {
qstr_init(); qstr_init();
mp_stack_ctrl_init(); mp_stack_ctrl_init();
// no pending exceptions to start with
mp_pending_exception = MP_OBJ_NULL;
#if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF #if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF
mp_init_emergency_exception_buf(); mp_init_emergency_exception_buf();
#endif #endif
......
...@@ -116,6 +116,7 @@ mp_obj_t mp_convert_native_to_obj(mp_uint_t val, mp_uint_t type); ...@@ -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); 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); 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_path_obj;
extern struct _mp_obj_list_t mp_sys_argv_obj; extern struct _mp_obj_list_t mp_sys_argv_obj;
#define mp_sys_path ((mp_obj_t)&mp_sys_path_obj) #define mp_sys_path ((mp_obj_t)&mp_sys_path_obj)
......
...@@ -110,10 +110,12 @@ mp_vm_return_kind_t mp_execute_bytecode(mp_code_state *code_state, volatile mp_o ...@@ -110,10 +110,12 @@ mp_vm_return_kind_t mp_execute_bytecode(mp_code_state *code_state, volatile mp_o
code_state->ip = ip; \ code_state->ip = ip; \
goto *entry_table[*ip++]; \ goto *entry_table[*ip++]; \
} while(0) } while(0)
#define DISPATCH_WITH_PEND_EXC_CHECK() goto pending_exception_check
#define ENTRY(op) entry_##op #define ENTRY(op) entry_##op
#define ENTRY_DEFAULT entry_default #define ENTRY_DEFAULT entry_default
#else #else
#define DISPATCH() break #define DISPATCH() break
#define DISPATCH_WITH_PEND_EXC_CHECK() goto pending_exception_check
#define ENTRY(op) case op #define ENTRY(op) case op
#define ENTRY_DEFAULT default #define ENTRY_DEFAULT default
#endif #endif
...@@ -372,21 +374,21 @@ dispatch_loop: ...@@ -372,21 +374,21 @@ dispatch_loop:
ENTRY(MP_BC_JUMP): ENTRY(MP_BC_JUMP):
DECODE_SLABEL; DECODE_SLABEL;
ip += unum; ip += unum;
DISPATCH(); DISPATCH_WITH_PEND_EXC_CHECK();
ENTRY(MP_BC_POP_JUMP_IF_TRUE): ENTRY(MP_BC_POP_JUMP_IF_TRUE):
DECODE_SLABEL; DECODE_SLABEL;
if (mp_obj_is_true(POP())) { if (mp_obj_is_true(POP())) {
ip += unum; ip += unum;
} }
DISPATCH(); DISPATCH_WITH_PEND_EXC_CHECK();
ENTRY(MP_BC_POP_JUMP_IF_FALSE): ENTRY(MP_BC_POP_JUMP_IF_FALSE):
DECODE_SLABEL; DECODE_SLABEL;
if (!mp_obj_is_true(POP())) { if (!mp_obj_is_true(POP())) {
ip += unum; ip += unum;
} }
DISPATCH(); DISPATCH_WITH_PEND_EXC_CHECK();
ENTRY(MP_BC_JUMP_IF_TRUE_OR_POP): ENTRY(MP_BC_JUMP_IF_TRUE_OR_POP):
DECODE_SLABEL; DECODE_SLABEL;
...@@ -395,7 +397,7 @@ dispatch_loop: ...@@ -395,7 +397,7 @@ dispatch_loop:
} else { } else {
sp--; sp--;
} }
DISPATCH(); DISPATCH_WITH_PEND_EXC_CHECK();
ENTRY(MP_BC_JUMP_IF_FALSE_OR_POP): ENTRY(MP_BC_JUMP_IF_FALSE_OR_POP):
DECODE_SLABEL; DECODE_SLABEL;
...@@ -404,7 +406,7 @@ dispatch_loop: ...@@ -404,7 +406,7 @@ dispatch_loop:
} else { } else {
ip += unum; ip += unum;
} }
DISPATCH(); DISPATCH_WITH_PEND_EXC_CHECK();
ENTRY(MP_BC_SETUP_WITH): { ENTRY(MP_BC_SETUP_WITH): {
mp_obj_t obj = TOP(); mp_obj_t obj = TOP();
...@@ -502,7 +504,7 @@ unwind_jump: ...@@ -502,7 +504,7 @@ unwind_jump:
if (unum != 0) { if (unum != 0) {
sp--; sp--;
} }
DISPATCH(); DISPATCH_WITH_PEND_EXC_CHECK();
// matched against: POP_BLOCK or POP_EXCEPT (anything else?) // matched against: POP_BLOCK or POP_EXCEPT (anything else?)
ENTRY(MP_BC_SETUP_EXCEPT): ENTRY(MP_BC_SETUP_EXCEPT):
...@@ -909,6 +911,15 @@ yield: ...@@ -909,6 +911,15 @@ yield:
#if !MICROPY_OPT_COMPUTED_GOTO #if !MICROPY_OPT_COMPUTED_GOTO
} // switch } // switch
#endif #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 } // for loop
} else { } else {
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include "misc.h" #include "misc.h"
#include "qstr.h" #include "qstr.h"
#include "obj.h" #include "obj.h"
#include "runtime.h"
#include "pendsv.h" #include "pendsv.h"
static void *pendsv_object = NULL; static void *pendsv_object = NULL;
...@@ -40,13 +41,23 @@ void pendsv_init(void) { ...@@ -40,13 +41,23 @@ void pendsv_init(void) {
HAL_NVIC_SetPriority(PendSV_IRQn, 0xf, 0xf); HAL_NVIC_SetPriority(PendSV_IRQn, 0xf, 0xf);
} }
// call this function to raise a pending exception during an interrupt // Call this function to raise a pending exception during an interrupt.
// it will wait until all interrupts are finished then raise the given // It will first try to raise the exception "softly" by setting the
// exception object using nlr_jump in the context of the top-level thread // 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) { void pendsv_nlr_jump(void *o) {
if (mp_pending_exception == MP_OBJ_NULL) {
mp_pending_exception = o;
} else {
mp_pending_exception = MP_OBJ_NULL;
pendsv_object = o; pendsv_object = o;
SCB->ICSR = SCB_ICSR_PENDSVSET_Msk; SCB->ICSR = SCB_ICSR_PENDSVSET_Msk;
} }
}
// since we play tricks with the stack, the compiler must not generate a // since we play tricks with the stack, the compiler must not generate a
// prelude for this function // prelude for this function
......
...@@ -50,7 +50,7 @@ STATIC mp_obj_t mp_const_vcp_interrupt = MP_OBJ_NULL; ...@@ -50,7 +50,7 @@ STATIC mp_obj_t mp_const_vcp_interrupt = MP_OBJ_NULL;
void pyb_usb_init0(void) { void pyb_usb_init0(void) {
// create an exception object for interrupting by VCP // 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); USBD_CDC_SetInterrupt(VCP_CHAR_NONE, mp_const_vcp_interrupt);
} }
......
...@@ -374,7 +374,6 @@ static int8_t CDC_Itf_Receive(uint8_t* Buf, uint32_t *Len) { ...@@ -374,7 +374,6 @@ static int8_t CDC_Itf_Receive(uint8_t* Buf, uint32_t *Len) {
if (char_found) { if (char_found) {
// raise exception when interrupts are finished // raise exception when interrupts are finished
user_interrupt_char = VCP_CHAR_NONE;
pendsv_nlr_jump(user_interrupt_data); pendsv_nlr_jump(user_interrupt_data);
} }
......
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <errno.h> #include <errno.h>
#include <signal.h>
#include "mpconfig.h" #include "mpconfig.h"
#include "nlr.h" #include "nlr.h"
...@@ -64,6 +65,20 @@ mp_uint_t mp_verbose_flag = 0; ...@@ -64,6 +65,20 @@ mp_uint_t mp_verbose_flag = 0;
long heap_size = 128*1024 * (sizeof(mp_uint_t) / 4); long heap_size = 128*1024 * (sizeof(mp_uint_t) / 4);
#endif #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) #define FORCED_EXIT (0x100)
// returns standard error codes: 0 for success, 1 for all other errors // returns standard error codes: 0 for success, 1 for all other errors
// if FORCED_EXIT bit is set then script raised SystemExit and the // 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, ...@@ -119,14 +134,23 @@ STATIC int execute_from_lexer(mp_lexer_t *lex, mp_parse_input_kind_t input_kind,
return 0; 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 // execute it
nlr_buf_t nlr; nlr_buf_t nlr;
if (nlr_push(&nlr) == 0) { if (nlr_push(&nlr) == 0) {
mp_call_function_0(module_fun); mp_call_function_0(module_fun);
sigaction(SIGINT, &sa, NULL);
nlr_pop(); nlr_pop();
return 0; return 0;
} else { } else {
// uncaught exception // uncaught exception
sigaction(SIGINT, &sa, NULL);
// check for SystemExit // check for SystemExit
mp_obj_t exc = (mp_obj_t)nlr.ret_val; mp_obj_t exc = (mp_obj_t)nlr.ret_val;
if (mp_obj_is_subclass_fast(mp_obj_get_type(exc), &mp_type_SystemExit)) { if (mp_obj_is_subclass_fast(mp_obj_get_type(exc), &mp_type_SystemExit)) {
...@@ -305,6 +329,9 @@ int main(int argc, char **argv) { ...@@ -305,6 +329,9 @@ int main(int argc, char **argv) {
mp_init(); mp_init();
// create keyboard interrupt object
keyboard_interrupt_obj = mp_obj_new_exception(&mp_type_KeyboardInterrupt);
char *home = getenv("HOME"); char *home = getenv("HOME");
char *path = getenv("MICROPYPATH"); char *path = getenv("MICROPYPATH");
if (path == NULL) { if (path == NULL) {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment