Skip to content

Commit

Permalink
bricks/stm32: Force exit on shutdown.
Browse files Browse the repository at this point in the history
Since the removal of the longjump in 6d06933,
the user could prevent the hub from stopping the program and shutting down
by catching all exceptions.

This commit ensures we can force a safe exit on shutdown in all cases.
  • Loading branch information
laurensvalk committed Nov 14, 2022
1 parent 42dd2fb commit 4d2a768
Show file tree
Hide file tree
Showing 6 changed files with 42 additions and 20 deletions.
36 changes: 25 additions & 11 deletions bricks/_common/micropython.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,33 @@
#include "py/stackctrl.h"
#include "py/stream.h"

// Outermost nlr buffer.
nlr_buf_t nlr_main;

// Implementation for MICROPY_EVENT_POLL_HOOK
void pb_event_poll_hook(void) {
while (pbio_do_one_event()) {
}

// On forced exit, pop back to outer nlr buffer so the exception will
// always be raised and the application program exits cleanly. We also need
// MP_STATE_THREAD(mp_pending_exception) to be the system exit exception
// but we don't check it because it is set along with pyexec_system_exit.
if (pyexec_system_exit == PYEXEC_FORCED_EXIT) {
while (MP_STATE_MAIN_THREAD(nlr_top) != &nlr_main) {
nlr_pop();
}
pyexec_system_exit = 0;
}

mp_handle_pending(true);

// Platform-specific code to run on completing the poll hook.
pb_event_poll_hook_leave();
}

// callback for when stop button is pressed in IDE or on hub
void pbsys_main_stop_program(void) {
void pbsys_main_stop_program(bool force_stop) {

static const mp_obj_tuple_t args = {
.base = { .type = &mp_type_tuple },
Expand All @@ -53,9 +67,6 @@ void pbsys_main_stop_program(void) {
};
static mp_obj_exception_t system_exit;

// Trigger soft reboot.
pyexec_system_exit = PYEXEC_FORCED_EXIT;

// Schedule SystemExit exception.
system_exit.base.type = &mp_type_SystemExit;
system_exit.traceback_alloc = 0;
Expand All @@ -68,6 +79,11 @@ void pbsys_main_stop_program(void) {
MP_STATE_VM(sched_state) = MP_SCHED_PENDING;
}
#endif

// IDE stop button and long-press power button will force an exit.
if (force_stop) {
pyexec_system_exit = PYEXEC_FORCED_EXIT;
}
}

bool pbsys_main_stdin_event(uint8_t c) {
Expand Down Expand Up @@ -112,16 +128,15 @@ static void mp_vfs_map_minimal_new_reader(mp_reader_t *reader, mp_vfs_map_minima
static void run_repl() {
// Reset REPL history.
readline_init0();
nlr_buf_t nlr;
if (nlr_push(&nlr) == 0) {
if (nlr_push(&nlr_main) == 0) {
// Run the REPL.
pyexec_friendly_repl();
nlr_pop();
} else {
// clear any pending exceptions (and run any callbacks).
mp_handle_pending(false);
// Print which exception triggered this.
mp_obj_print_exception(&mp_plat_print, (mp_obj_t)nlr.ret_val);
mp_obj_print_exception(&mp_plat_print, (mp_obj_t)nlr_main.ret_val);
}
}
#endif
Expand Down Expand Up @@ -209,8 +224,7 @@ static mpy_info_t *mpy_data_find(qstr name) {
*/
static void run_user_program(void) {

nlr_buf_t nlr;
if (nlr_push(&nlr) == 0) {
if (nlr_push(&nlr_main) == 0) {
mpy_info_t *info = mpy_data_find(MP_QSTR___main__);

if (!info) {
Expand Down Expand Up @@ -241,11 +255,11 @@ static void run_user_program(void) {
mp_handle_pending(false);

// Print which exception triggered this.
mp_obj_print_exception(&mp_plat_print, (mp_obj_t)nlr.ret_val);
mp_obj_print_exception(&mp_plat_print, (mp_obj_t)nlr_main.ret_val);

#if PYBRICKS_OPT_COMPILER
// On KeyboardInterrupt, drop to REPL for debugging.
if (mp_obj_exception_match((mp_obj_t)nlr.ret_val, &mp_type_KeyboardInterrupt)) {
if (mp_obj_exception_match((mp_obj_t)nlr_main.ret_val, &mp_type_KeyboardInterrupt)) {

// The global scope is preserved to facilitate debugging, but we
// stop active resources like motors and sounds. They are stopped
Expand Down
2 changes: 1 addition & 1 deletion bricks/virtualhub/mp_port.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
#define FORCED_EXIT (0x100)

// callback for when stop button is pressed in IDE or on hub
void pbsys_main_stop_program(void) {
void pbsys_main_stop_program(bool force_stop) {
static const mp_rom_obj_tuple_t args = {
.base = { .type = &mp_type_tuple },
.len = 2,
Expand Down
8 changes: 6 additions & 2 deletions lib/pbio/include/pbsys/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,12 @@ void pbsys_main_run_program(pbsys_main_program_t *program);
* Stops (cancels) the main application program.
*
* This should be provided by the application running on top of pbio.
*
* @param [in] force_stop Whether to force stop the program instead of asking
* nicely. This is true when the application must stop
* on shutdown.
*/
void pbsys_main_stop_program(void);
void pbsys_main_stop_program(bool force_stop);

/**
* Handles standard input.
Expand All @@ -68,7 +72,7 @@ bool pbsys_main_stdin_event(uint8_t c);

#else

static inline void pbsys_main_stop_program(void) {
static inline void pbsys_main_stop_program(bool force_stop) {
}

static inline bool pbsys_main_stdin_event(uint8_t c) {
Expand Down
2 changes: 1 addition & 1 deletion lib/pbio/sys/command.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pbio_pybricks_error_t pbsys_command(const uint8_t *data, uint32_t size) {

switch (cmd) {
case PBIO_PYBRICKS_COMMAND_STOP_USER_PROGRAM:
pbsys_program_stop();
pbsys_program_stop(false);
return PBIO_PYBRICKS_ERROR_OK;
case PBIO_PYBRICKS_COMMAND_START_USER_PROGRAM:
return pbio_pybricks_error_from_pbio_error(pbsys_program_load_start_user_program());
Expand Down
12 changes: 8 additions & 4 deletions lib/pbio/sys/program_stop.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,14 @@ static bool program_stop_on_shutdown_requested;
/**
* Request the user program to stop. For example, in MicroPython, this may raise
* a SystemExit exception.
*
* @param [in] force_stop Whether to force stop the program instead of asking
* nicely. This is true when the application must stop
* on shutdown.
*/
void pbsys_program_stop(void) {
void pbsys_program_stop(bool force_stop) {
if (pbsys_status_test(PBIO_PYBRICKS_STATUS_USER_PROGRAM_RUNNING)) {
pbsys_main_stop_program();
pbsys_main_stop_program(force_stop);
}
}

Expand All @@ -48,7 +52,7 @@ void pbsys_program_stop_poll(void) {
// Should not request stop more than once.
return;
} else if (pbsys_status_test(PBIO_PYBRICKS_STATUS_SHUTDOWN_REQUEST)) {
pbsys_program_stop();
pbsys_program_stop(true);
program_stop_on_shutdown_requested = true;
return;
}
Expand All @@ -63,7 +67,7 @@ void pbsys_program_stop_poll(void) {
if ((btn & stop_buttons) == stop_buttons) {
if (!stop_button_pressed) {
stop_button_pressed = true;
pbsys_program_stop();
pbsys_program_stop(false);
}
} else {
stop_button_pressed = false;
Expand Down
2 changes: 1 addition & 1 deletion lib/pbio/sys/program_stop.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
#define _PBSYS_SYS_USER_PROGRAM_H_

void pbsys_program_stop_poll(void);
void pbsys_program_stop(void);
void pbsys_program_stop(bool force_stop);

#endif // _PBSYS_SYS_USER_PROGRAM_H_

0 comments on commit 4d2a768

Please sign in to comment.