py/mpstate: Schedule KeyboardInterrupt on main thread.
This introduces a new macro to get the main thread and uses it to ensure that asynchronous exceptions such as KeyboardInterrupt (CTRL+C) are only scheduled on the main thread. This is more deterministic than being scheduled on a random thread and is more in line with CPython that only allow signal handlers to run on the main thread. Fixes issue #7026. Signed-off-by: David Lechner <david@pybricks.com>
This commit is contained in:
parent
ca920f7218
commit
259d9b69fe
@ -140,7 +140,7 @@ int switch_get(int sw) {
|
||||
// TODO need an irq
|
||||
void uart_rx_irq(void) {
|
||||
if (c == interrupt_char) {
|
||||
MP_STATE_THREAD(mp_pending_exception) = MP_STATE_PORT(keyboard_interrupt_obj);
|
||||
MP_STATE_MAIN_THREAD(mp_pending_exception) = MP_STATE_PORT(keyboard_interrupt_obj);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
@ -60,10 +60,10 @@ void pendsv_init(void) {
|
||||
// the given exception object using nlr_jump in the context of the top-level
|
||||
// thread.
|
||||
void pendsv_kbd_intr(void) {
|
||||
if (MP_STATE_THREAD(mp_pending_exception) == MP_OBJ_NULL) {
|
||||
if (MP_STATE_MAIN_THREAD(mp_pending_exception) == MP_OBJ_NULL) {
|
||||
mp_sched_keyboard_interrupt();
|
||||
} else {
|
||||
MP_STATE_THREAD(mp_pending_exception) = MP_OBJ_NULL;
|
||||
MP_STATE_MAIN_THREAD(mp_pending_exception) = MP_OBJ_NULL;
|
||||
pendsv_object = &MP_STATE_VM(mp_kbd_exception);
|
||||
SCB->ICSR = SCB_ICSR_PENDSVSET_Msk;
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ STATIC void sighandler(int signum) {
|
||||
sigprocmask(SIG_SETMASK, &mask, NULL);
|
||||
nlr_raise(MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception)));
|
||||
#else
|
||||
if (MP_STATE_THREAD(mp_pending_exception) == MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception))) {
|
||||
if (MP_STATE_MAIN_THREAD(mp_pending_exception) == MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception))) {
|
||||
// this is the second time we are called, so die straight away
|
||||
exit(1);
|
||||
}
|
||||
|
@ -80,7 +80,7 @@ void mp_hal_stdio_mode_orig(void) {
|
||||
// the thread created for handling it might not be running yet so we'd miss the notification.
|
||||
BOOL WINAPI console_sighandler(DWORD evt) {
|
||||
if (evt == CTRL_C_EVENT) {
|
||||
if (MP_STATE_THREAD(mp_pending_exception) == MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception))) {
|
||||
if (MP_STATE_MAIN_THREAD(mp_pending_exception) == MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception))) {
|
||||
// this is the second time we are called, so die straight away
|
||||
exit(1);
|
||||
}
|
||||
|
@ -285,12 +285,13 @@ extern mp_state_ctx_t mp_state_ctx;
|
||||
|
||||
#define MP_STATE_VM(x) (mp_state_ctx.vm.x)
|
||||
#define MP_STATE_MEM(x) (mp_state_ctx.mem.x)
|
||||
#define MP_STATE_MAIN_THREAD(x) (mp_state_ctx.thread.x)
|
||||
|
||||
#if MICROPY_PY_THREAD
|
||||
extern mp_state_thread_t *mp_thread_get_state(void);
|
||||
#define MP_STATE_THREAD(x) (mp_thread_get_state()->x)
|
||||
#else
|
||||
#define MP_STATE_THREAD(x) (mp_state_ctx.thread.x)
|
||||
#define MP_STATE_THREAD(x) MP_STATE_MAIN_THREAD(x)
|
||||
#endif
|
||||
|
||||
#endif // MICROPY_INCLUDED_PY_MPSTATE_H
|
||||
|
@ -28,8 +28,10 @@
|
||||
|
||||
#include "py/runtime.h"
|
||||
|
||||
// Schedules an exception on the main thread (for exceptions "thrown" by async
|
||||
// sources such as interrupts and UNIX signal handlers).
|
||||
void MICROPY_WRAP_MP_SCHED_EXCEPTION(mp_sched_exception)(mp_obj_t exc) {
|
||||
MP_STATE_THREAD(mp_pending_exception) = exc;
|
||||
MP_STATE_MAIN_THREAD(mp_pending_exception) = exc;
|
||||
#if MICROPY_ENABLE_SCHEDULER
|
||||
if (MP_STATE_VM(sched_state) == MP_SCHED_IDLE) {
|
||||
MP_STATE_VM(sched_state) = MP_SCHED_PENDING;
|
||||
|
Loading…
x
Reference in New Issue
Block a user