From ca920f72184c50f61002aa9d5cd01555b1e28b7b Mon Sep 17 00:00:00 2001 From: David Lechner Date: Mon, 10 May 2021 21:53:22 -0500 Subject: [PATCH] py/mpstate: Make exceptions thread-local. This moves mp_pending_exception from mp_state_vm_t to mp_state_thread_t. This allows exceptions to be scheduled on a specific thread. Signed-off-by: David Lechner --- ports/esp8266/modnetwork.c | 2 +- .../nrf/boards/microbit/modules/microbitdisplay.c | 2 +- ports/nrf/modules/music/modmusic.c | 2 +- ports/pic16bit/board.c | 2 +- ports/stm32/pendsv.c | 4 ++-- ports/unix/unix_mphal.c | 2 +- ports/windows/windows_mphal.c | 2 +- py/modthread.c | 3 ++- py/mpstate.h | 6 +++--- py/profile.c | 2 +- py/runtime.c | 2 +- py/scheduler.c | 14 +++++++------- py/vm.c | 10 +++++----- 13 files changed, 27 insertions(+), 26 deletions(-) diff --git a/ports/esp8266/modnetwork.c b/ports/esp8266/modnetwork.c index 3b8ef4b21f..8d153acf0f 100644 --- a/ports/esp8266/modnetwork.c +++ b/ports/esp8266/modnetwork.c @@ -237,7 +237,7 @@ STATIC mp_obj_t esp_scan(mp_obj_t self_in) { while (esp_scan_list != NULL) { // our esp_scan_cb is called via ets_loop_iter so it's safe to set the // esp_scan_list variable to NULL without disabling interrupts - if (MP_STATE_VM(mp_pending_exception) != NULL) { + if (MP_STATE_THREAD(mp_pending_exception) != NULL) { esp_scan_list = NULL; mp_handle_pending(true); } diff --git a/ports/nrf/boards/microbit/modules/microbitdisplay.c b/ports/nrf/boards/microbit/modules/microbitdisplay.c index c2eaf4179b..a0508886d7 100644 --- a/ports/nrf/boards/microbit/modules/microbitdisplay.c +++ b/ports/nrf/boards/microbit/modules/microbitdisplay.c @@ -149,7 +149,7 @@ STATIC void async_stop(void) { STATIC void wait_for_event(void) { while (!wakeup_event) { // allow CTRL-C to stop the animation - if (MP_STATE_VM(mp_pending_exception) != MP_OBJ_NULL) { + if (MP_STATE_THREAD(mp_pending_exception) != MP_OBJ_NULL) { async_stop(); return; } diff --git a/ports/nrf/modules/music/modmusic.c b/ports/nrf/modules/music/modmusic.c index 168e7613f5..e7e351f859 100644 --- a/ports/nrf/modules/music/modmusic.c +++ b/ports/nrf/modules/music/modmusic.c @@ -140,7 +140,7 @@ STATIC void wait_async_music_idle(void) { // wait for the async music state to become idle while (music_data->async_state != ASYNC_MUSIC_STATE_IDLE) { // allow CTRL-C to stop the music - if (MP_STATE_VM(mp_pending_exception) != MP_OBJ_NULL) { + if (MP_STATE_THREAD(mp_pending_exception) != MP_OBJ_NULL) { music_data->async_state = ASYNC_MUSIC_STATE_IDLE; pwm_set_duty_cycle(music_data->async_pin->pin, 0); // TODO: remove pin setting. break; diff --git a/ports/pic16bit/board.c b/ports/pic16bit/board.c index 977dd2cfa8..b0e9d17270 100644 --- a/ports/pic16bit/board.c +++ b/ports/pic16bit/board.c @@ -140,7 +140,7 @@ int switch_get(int sw) { // TODO need an irq void uart_rx_irq(void) { if (c == interrupt_char) { - MP_STATE_VM(mp_pending_exception) = MP_STATE_PORT(keyboard_interrupt_obj); + MP_STATE_THREAD(mp_pending_exception) = MP_STATE_PORT(keyboard_interrupt_obj); } } */ diff --git a/ports/stm32/pendsv.c b/ports/stm32/pendsv.c index 3181ba616a..2887ac5723 100644 --- a/ports/stm32/pendsv.c +++ b/ports/stm32/pendsv.c @@ -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_VM(mp_pending_exception) == MP_OBJ_NULL) { + if (MP_STATE_THREAD(mp_pending_exception) == MP_OBJ_NULL) { mp_sched_keyboard_interrupt(); } else { - MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL; + MP_STATE_THREAD(mp_pending_exception) = MP_OBJ_NULL; pendsv_object = &MP_STATE_VM(mp_kbd_exception); SCB->ICSR = SCB_ICSR_PENDSVSET_Msk; } diff --git a/ports/unix/unix_mphal.c b/ports/unix/unix_mphal.c index d62991d34d..111844abc4 100644 --- a/ports/unix/unix_mphal.c +++ b/ports/unix/unix_mphal.c @@ -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_VM(mp_pending_exception) == MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception))) { + if (MP_STATE_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); } diff --git a/ports/windows/windows_mphal.c b/ports/windows/windows_mphal.c index dd196f67af..ba1d982ad9 100644 --- a/ports/windows/windows_mphal.c +++ b/ports/windows/windows_mphal.c @@ -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_VM(mp_pending_exception) == MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception))) { + if (MP_STATE_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); } diff --git a/py/modthread.c b/py/modthread.c index 64fbb3f198..29b765493a 100644 --- a/py/modthread.c +++ b/py/modthread.c @@ -174,6 +174,8 @@ STATIC void *thread_entry(void *args_in) { // The GC starts off unlocked on this thread. ts.gc_lock_depth = 0; + ts.mp_pending_exception = MP_OBJ_NULL; + // set locals and globals from the calling context mp_locals_set(args->dict_locals); mp_globals_set(args->dict_globals); @@ -184,7 +186,6 @@ STATIC void *thread_entry(void *args_in) { mp_thread_start(); // TODO set more thread-specific state here: - // mp_pending_exception? (root pointer) // cur_exception (root pointer) DEBUG_printf("[thread] start ts=%p args=%p stack=%p\n", &ts, &args, MP_STATE_THREAD(stack_top)); diff --git a/py/mpstate.h b/py/mpstate.h index a0e3d4f143..f5fb5b707b 100644 --- a/py/mpstate.h +++ b/py/mpstate.h @@ -137,9 +137,6 @@ typedef struct _mp_state_vm_t { // dictionary with loaded modules (may be exposed as sys.modules) mp_obj_dict_t mp_loaded_modules_dict; - // pending exception object (MP_OBJ_NULL if not pending) - volatile mp_obj_t mp_pending_exception; - #if MICROPY_ENABLE_SCHEDULER mp_sched_item_t sched_queue[MICROPY_SCHEDULER_DEPTH]; #endif @@ -266,6 +263,9 @@ typedef struct _mp_state_thread_t { nlr_buf_t *nlr_top; + // pending exception object (MP_OBJ_NULL if not pending) + volatile mp_obj_t mp_pending_exception; + #if MICROPY_PY_SYS_SETTRACE mp_obj_t prof_trace_callback; bool prof_callback_is_executing; diff --git a/py/profile.c b/py/profile.c index e5fb35f0ed..054a0f9e61 100644 --- a/py/profile.c +++ b/py/profile.c @@ -297,7 +297,7 @@ STATIC mp_obj_t mp_prof_callback_invoke(mp_obj_t callback, prof_callback_args_t mp_prof_is_executing = false; - if (MP_STATE_VM(mp_pending_exception) != MP_OBJ_NULL) { + if (MP_STATE_THREAD(mp_pending_exception) != MP_OBJ_NULL) { mp_handle_pending(true); } return top; diff --git a/py/runtime.c b/py/runtime.c index 1ca0702e19..cda26a9cf8 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -62,7 +62,7 @@ void mp_init(void) { qstr_init(); // no pending exceptions to start with - MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL; + MP_STATE_THREAD(mp_pending_exception) = MP_OBJ_NULL; #if MICROPY_ENABLE_SCHEDULER MP_STATE_VM(sched_state) = MP_SCHED_IDLE; MP_STATE_VM(sched_idx) = 0; diff --git a/py/scheduler.c b/py/scheduler.c index 0671a34a8c..0114a7a58b 100644 --- a/py/scheduler.c +++ b/py/scheduler.c @@ -29,7 +29,7 @@ #include "py/runtime.h" void MICROPY_WRAP_MP_SCHED_EXCEPTION(mp_sched_exception)(mp_obj_t exc) { - MP_STATE_VM(mp_pending_exception) = exc; + MP_STATE_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; @@ -66,9 +66,9 @@ void mp_handle_pending(bool raise_exc) { mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); // Re-check state is still pending now that we're in the atomic section. if (MP_STATE_VM(sched_state) == MP_SCHED_PENDING) { - mp_obj_t obj = MP_STATE_VM(mp_pending_exception); + mp_obj_t obj = MP_STATE_THREAD(mp_pending_exception); if (obj != MP_OBJ_NULL) { - MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL; + MP_STATE_THREAD(mp_pending_exception) = MP_OBJ_NULL; if (!mp_sched_num_pending()) { MP_STATE_VM(sched_state) = MP_SCHED_IDLE; } @@ -115,7 +115,7 @@ void mp_sched_unlock(void) { assert(MP_STATE_VM(sched_state) < 0); if (++MP_STATE_VM(sched_state) == 0) { // vm became unlocked - if (MP_STATE_VM(mp_pending_exception) != MP_OBJ_NULL || mp_sched_num_pending()) { + if (MP_STATE_THREAD(mp_pending_exception) != MP_OBJ_NULL || mp_sched_num_pending()) { MP_STATE_VM(sched_state) = MP_SCHED_PENDING; } else { MP_STATE_VM(sched_state) = MP_SCHED_IDLE; @@ -148,9 +148,9 @@ bool MICROPY_WRAP_MP_SCHED_SCHEDULE(mp_sched_schedule)(mp_obj_t function, mp_obj // A variant of this is inlined in the VM at the pending exception check void mp_handle_pending(bool raise_exc) { - if (MP_STATE_VM(mp_pending_exception) != MP_OBJ_NULL) { - mp_obj_t obj = MP_STATE_VM(mp_pending_exception); - MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL; + if (MP_STATE_THREAD(mp_pending_exception) != MP_OBJ_NULL) { + mp_obj_t obj = MP_STATE_THREAD(mp_pending_exception); + MP_STATE_THREAD(mp_pending_exception) = MP_OBJ_NULL; if (raise_exc) { nlr_raise(obj); } diff --git a/py/vm.c b/py/vm.c index c97070b78b..f9a589c9d1 100644 --- a/py/vm.c +++ b/py/vm.c @@ -1375,9 +1375,9 @@ pending_exception_check: // Re-check state is still pending now that we're in the atomic section. if (MP_STATE_VM(sched_state) == MP_SCHED_PENDING) { MARK_EXC_IP_SELECTIVE(); - mp_obj_t obj = MP_STATE_VM(mp_pending_exception); + mp_obj_t obj = MP_STATE_THREAD(mp_pending_exception); if (obj != MP_OBJ_NULL) { - MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL; + MP_STATE_THREAD(mp_pending_exception) = MP_OBJ_NULL; if (!mp_sched_num_pending()) { MP_STATE_VM(sched_state) = MP_SCHED_IDLE; } @@ -1391,10 +1391,10 @@ pending_exception_check: } #else // This is an inlined variant of mp_handle_pending - if (MP_STATE_VM(mp_pending_exception) != MP_OBJ_NULL) { + if (MP_STATE_THREAD(mp_pending_exception) != MP_OBJ_NULL) { MARK_EXC_IP_SELECTIVE(); - mp_obj_t obj = MP_STATE_VM(mp_pending_exception); - MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL; + mp_obj_t obj = MP_STATE_THREAD(mp_pending_exception); + MP_STATE_THREAD(mp_pending_exception) = MP_OBJ_NULL; RAISE(obj); } #endif