diff --git a/py/mpconfig.h b/py/mpconfig.h index db9c623f49fe6..80e58d1cc41fc 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -909,6 +909,11 @@ typedef double mp_float_t; #define MICROPY_INTERNAL_PRINTF_PRINTER (&mp_plat_print) #endif +// Whether to support mp_sched_vm_abort to asynchronously abort to the top level. +#ifndef MICROPY_ENABLE_VM_ABORT +#define MICROPY_ENABLE_VM_ABORT (0) +#endif + // Support for internal scheduler #ifndef MICROPY_ENABLE_SCHEDULER #define MICROPY_ENABLE_SCHEDULER (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) @@ -1740,6 +1745,10 @@ typedef double mp_float_t; #define MICROPY_WRAP_MP_SCHED_SCHEDULE(f) f #endif +#ifndef MICROPY_WRAP_MP_SCHED_VM_ABORT +#define MICROPY_WRAP_MP_SCHED_VM_ABORT(f) f +#endif + /*****************************************************************************/ /* Miscellaneous settings */ diff --git a/py/mpstate.h b/py/mpstate.h index ba47c7648235b..f6b911af5658c 100644 --- a/py/mpstate.h +++ b/py/mpstate.h @@ -226,6 +226,11 @@ typedef struct _mp_state_vm_t { uint8_t sched_idx; #endif + #if MICROPY_ENABLE_VM_ABORT + bool vm_abort; + nlr_buf_t *nlr_abort; + #endif + #if MICROPY_PY_THREAD_GIL // This is a global mutex used to make the VM/runtime thread-safe. mp_thread_mutex_t gil_mutex; @@ -297,8 +302,10 @@ extern mp_state_ctx_t mp_state_ctx; #if MICROPY_PY_THREAD extern mp_state_thread_t *mp_thread_get_state(void); #define MP_STATE_THREAD(x) (mp_thread_get_state()->x) +#define mp_thread_is_main_thread() (mp_thread_get_state() == &mp_state_ctx.thread) #else #define MP_STATE_THREAD(x) MP_STATE_MAIN_THREAD(x) +#define mp_thread_is_main_thread() (true) #endif #endif // MICROPY_INCLUDED_PY_MPSTATE_H diff --git a/py/nlr.c b/py/nlr.c index a35e7d229c51e..92db8ffb19799 100644 --- a/py/nlr.c +++ b/py/nlr.c @@ -49,3 +49,10 @@ void nlr_pop(void) { nlr_buf_t **top = &MP_STATE_THREAD(nlr_top); *top = (*top)->prev; } + +#if MICROPY_ENABLE_VM_ABORT +NORETURN void nlr_jump_abort(void) { + MP_STATE_THREAD(nlr_top) = MP_STATE_VM(nlr_abort); + nlr_jump(NULL); +} +#endif diff --git a/py/nlr.h b/py/nlr.h index 338a67570409f..09ef66ef7202c 100644 --- a/py/nlr.h +++ b/py/nlr.h @@ -101,9 +101,16 @@ typedef struct _nlr_buf_t nlr_buf_t; struct _nlr_buf_t { - // the entries here must all be machine word size + // The entries in this struct must all be machine word size. + + // Pointer to the previous nlr_buf_t in the chain. + // Or NULL if it's the top-level one. nlr_buf_t *prev; - void *ret_val; // always a concrete object (an exception instance) + + // The exception that is being raised: + // - NULL means the jump is because of a VM abort (only if MICROPY_ENABLE_VM_ABORT enabled) + // - otherwise it's always a concrete object (an exception instance) + void *ret_val; #if MICROPY_NLR_SETJMP jmp_buf jmpbuf; @@ -149,6 +156,12 @@ unsigned int nlr_push_tail(nlr_buf_t *top); void nlr_pop(void); NORETURN void nlr_jump(void *val); +#if MICROPY_ENABLE_VM_ABORT +#define nlr_set_abort(buf) MP_STATE_VM(nlr_abort) = buf +#define nlr_get_abort() MP_STATE_VM(nlr_abort) +NORETURN void nlr_jump_abort(void); +#endif + // This must be implemented by a port. It's called by nlr_jump // if no nlr buf has been pushed. It must not return, but rather // should bail out with a fatal error. diff --git a/py/runtime.h b/py/runtime.h index 36b3caa6c73e6..d57c25c92c8cc 100644 --- a/py/runtime.h +++ b/py/runtime.h @@ -75,6 +75,9 @@ void mp_deinit(void); void mp_sched_exception(mp_obj_t exc); void mp_sched_keyboard_interrupt(void); +#if MICROPY_ENABLE_VM_ABORT +void mp_sched_vm_abort(void); +#endif void mp_handle_pending(bool raise_exc); #if MICROPY_ENABLE_SCHEDULER diff --git a/py/scheduler.c b/py/scheduler.c index db090b09911ce..165b26dc86521 100644 --- a/py/scheduler.c +++ b/py/scheduler.c @@ -51,6 +51,12 @@ void MICROPY_WRAP_MP_SCHED_KEYBOARD_INTERRUPT(mp_sched_keyboard_interrupt)(void) } #endif +#if MICROPY_ENABLE_VM_ABORT +void MICROPY_WRAP_MP_SCHED_VM_ABORT(mp_sched_vm_abort)(void) { + MP_STATE_VM(vm_abort) = true; +} +#endif + #if MICROPY_ENABLE_SCHEDULER #define IDX_MASK(i) ((i) & (MICROPY_SCHEDULER_DEPTH - 1)) @@ -203,6 +209,17 @@ MP_REGISTER_ROOT_POINTER(mp_sched_item_t sched_queue[MICROPY_SCHEDULER_DEPTH]); // Called periodically from the VM or from "waiting" code (e.g. sleep) to // process background tasks and pending exceptions (e.g. KeyboardInterrupt). void mp_handle_pending(bool raise_exc) { + // Handle pending VM abort. + #if MICROPY_ENABLE_VM_ABORT + if (MP_STATE_VM(vm_abort) && mp_thread_is_main_thread()) { + MP_STATE_VM(vm_abort) = false; + if (raise_exc && nlr_get_abort() != NULL) { + nlr_jump_abort(); + } + } + #endif + + // Handle any pending exception. if (MP_STATE_THREAD(mp_pending_exception) != MP_OBJ_NULL) { mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); mp_obj_t obj = MP_STATE_THREAD(mp_pending_exception); @@ -215,6 +232,8 @@ void mp_handle_pending(bool raise_exc) { } MICROPY_END_ATOMIC_SECTION(atomic_state); } + + // Handle any pending callbacks. #if MICROPY_ENABLE_SCHEDULER if (MP_STATE_VM(sched_state) == MP_SCHED_PENDING) { mp_sched_run_pending(); diff --git a/py/vm.c b/py/vm.c index 9273dda02eead..385d13ee4097c 100644 --- a/py/vm.c +++ b/py/vm.c @@ -1333,6 +1333,10 @@ unwind_jump:; // No scheduler: Just check pending exception. MP_STATE_THREAD(mp_pending_exception) != MP_OBJ_NULL #endif + #if MICROPY_ENABLE_VM_ABORT + // Check if the VM should abort execution. + || MP_STATE_VM(vm_abort) + #endif ) { MARK_EXC_IP_SELECTIVE(); mp_handle_pending(true); pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy