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>
pull/7425/head
David Lechner 2021-05-10 22:18:17 -05:00 zatwierdzone przez Damien George
rodzic ca920f7218
commit 259d9b69fe
6 zmienionych plików z 10 dodań i 7 usunięć

Wyświetl plik

@ -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);
}
}
*/

Wyświetl plik

@ -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;
}

Wyświetl plik

@ -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);
}

Wyświetl plik

@ -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);
}

Wyświetl plik

@ -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

Wyświetl plik

@ -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;