Skip to content

Commit d54208a

Browse files
committed
py/scheduler: Implement VM abort flag and mp_sched_vm_abort().
This is intended to be used by the very outer caller of the VM/runtime. It allows setting a top-level NLR handler that can be jumped to directly, in order to forcefully abort the VM/runtime. Enable using: #define MICROPY_ENABLE_VM_ABORT (1) Set up the handler at the top level using: nlr_buf_t nlr; nlr.ret_val = NULL; if (nlr_push(&nlr) == 0) { nlr_set_abort(&nlr); // call into the VM/runtime ... nlr_pop(); } else { if (nlr.ret_val == NULL) { // handle abort ... } else { // handle other exception that propagated to the top level ... } } nlr_set_abort(NULL); Schedule an abort, eg from an interrupt handler, using: mp_sched_vm_abort(); Signed-off-by: Damien George <[email protected]>
1 parent 5d4bfce commit d54208a

File tree

7 files changed

+62
-2
lines changed

7 files changed

+62
-2
lines changed

py/mpconfig.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -909,6 +909,11 @@ typedef double mp_float_t;
909909
#define MICROPY_INTERNAL_PRINTF_PRINTER (&mp_plat_print)
910910
#endif
911911

912+
// Whether to support mp_sched_vm_abort to asynchronously abort to the top level.
913+
#ifndef MICROPY_ENABLE_VM_ABORT
914+
#define MICROPY_ENABLE_VM_ABORT (0)
915+
#endif
916+
912917
// Support for internal scheduler
913918
#ifndef MICROPY_ENABLE_SCHEDULER
914919
#define MICROPY_ENABLE_SCHEDULER (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES)
@@ -1740,6 +1745,10 @@ typedef double mp_float_t;
17401745
#define MICROPY_WRAP_MP_SCHED_SCHEDULE(f) f
17411746
#endif
17421747

1748+
#ifndef MICROPY_WRAP_MP_SCHED_VM_ABORT
1749+
#define MICROPY_WRAP_MP_SCHED_VM_ABORT(f) f
1750+
#endif
1751+
17431752
/*****************************************************************************/
17441753
/* Miscellaneous settings */
17451754

py/mpstate.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,11 @@ typedef struct _mp_state_vm_t {
226226
uint8_t sched_idx;
227227
#endif
228228

229+
#if MICROPY_ENABLE_VM_ABORT
230+
bool vm_abort;
231+
nlr_buf_t *nlr_abort;
232+
#endif
233+
229234
#if MICROPY_PY_THREAD_GIL
230235
// This is a global mutex used to make the VM/runtime thread-safe.
231236
mp_thread_mutex_t gil_mutex;

py/nlr.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,10 @@ void nlr_pop(void) {
4949
nlr_buf_t **top = &MP_STATE_THREAD(nlr_top);
5050
*top = (*top)->prev;
5151
}
52+
53+
#if MICROPY_ENABLE_VM_ABORT
54+
NORETURN void nlr_jump_abort(void) {
55+
MP_STATE_THREAD(nlr_top) = MP_STATE_VM(nlr_abort);
56+
nlr_jump(NULL);
57+
}
58+
#endif

py/nlr.h

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,16 @@
101101

102102
typedef struct _nlr_buf_t nlr_buf_t;
103103
struct _nlr_buf_t {
104-
// the entries here must all be machine word size
104+
// The entries in this struct must all be machine word size.
105+
106+
// Pointer to the previous nlr_buf_t in the chain.
107+
// Or NULL if it's the top-level one.
105108
nlr_buf_t *prev;
106-
void *ret_val; // always a concrete object (an exception instance)
109+
110+
// The exception that is being raised:
111+
// - NULL means the jump is because of a VM abort (only if MICROPY_ENABLE_VM_ABORT enabled)
112+
// - otherwise it's always a concrete object (an exception instance)
113+
void *ret_val;
107114

108115
#if MICROPY_NLR_SETJMP
109116
jmp_buf jmpbuf;
@@ -149,6 +156,12 @@ unsigned int nlr_push_tail(nlr_buf_t *top);
149156
void nlr_pop(void);
150157
NORETURN void nlr_jump(void *val);
151158

159+
#if MICROPY_ENABLE_VM_ABORT
160+
#define nlr_set_abort(buf) MP_STATE_VM(nlr_abort) = buf
161+
#define nlr_get_abort() MP_STATE_VM(nlr_abort)
162+
NORETURN void nlr_jump_abort(void);
163+
#endif
164+
152165
// This must be implemented by a port. It's called by nlr_jump
153166
// if no nlr buf has been pushed. It must not return, but rather
154167
// should bail out with a fatal error.

py/runtime.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@ void mp_deinit(void);
7575

7676
void mp_sched_exception(mp_obj_t exc);
7777
void mp_sched_keyboard_interrupt(void);
78+
#if MICROPY_ENABLE_VM_ABORT
79+
void mp_sched_vm_abort(void);
80+
#endif
7881
void mp_handle_pending(bool raise_exc);
7982

8083
#if MICROPY_ENABLE_SCHEDULER

py/scheduler.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,12 @@ void MICROPY_WRAP_MP_SCHED_KEYBOARD_INTERRUPT(mp_sched_keyboard_interrupt)(void)
5151
}
5252
#endif
5353

54+
#if MICROPY_ENABLE_VM_ABORT
55+
void MICROPY_WRAP_MP_SCHED_VM_ABORT(mp_sched_vm_abort)(void) {
56+
MP_STATE_VM(vm_abort) = true;
57+
}
58+
#endif
59+
5460
#if MICROPY_ENABLE_SCHEDULER
5561

5662
#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]);
203209
// Called periodically from the VM or from "waiting" code (e.g. sleep) to
204210
// process background tasks and pending exceptions (e.g. KeyboardInterrupt).
205211
void mp_handle_pending(bool raise_exc) {
212+
// Handle pending VM abort.
213+
#if MICROPY_ENABLE_VM_ABORT
214+
if (MP_STATE_VM(vm_abort) && mp_thread_is_main_thread()) {
215+
MP_STATE_VM(vm_abort) = false;
216+
if (raise_exc && nlr_get_abort() != NULL) {
217+
nlr_jump_abort();
218+
}
219+
}
220+
#endif
221+
222+
// Handle any pending exception.
206223
if (MP_STATE_THREAD(mp_pending_exception) != MP_OBJ_NULL) {
207224
mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION();
208225
mp_obj_t obj = MP_STATE_THREAD(mp_pending_exception);
@@ -215,6 +232,8 @@ void mp_handle_pending(bool raise_exc) {
215232
}
216233
MICROPY_END_ATOMIC_SECTION(atomic_state);
217234
}
235+
236+
// Handle any pending callbacks.
218237
#if MICROPY_ENABLE_SCHEDULER
219238
if (MP_STATE_VM(sched_state) == MP_SCHED_PENDING) {
220239
mp_sched_run_pending();

py/vm.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1333,6 +1333,10 @@ unwind_jump:;
13331333
// No scheduler: Just check pending exception.
13341334
MP_STATE_THREAD(mp_pending_exception) != MP_OBJ_NULL
13351335
#endif
1336+
#if MICROPY_ENABLE_VM_ABORT
1337+
// Check if the VM should abort execution.
1338+
|| MP_STATE_VM(vm_abort)
1339+
#endif
13361340
) {
13371341
MARK_EXC_IP_SELECTIVE();
13381342
mp_handle_pending(true);

0 commit comments

Comments
 (0)