extmod/modbluetooth: Add support for running sync irq on system thread.

If the Bluetooth stack runs on another OS thread then synchronous BLE irq
callbacks, which block the Bluetooth stack until the callback to Python is
complete, must coordinate with the main thread and configure the
MicroPython thread-local-state.

This commit adds MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS_WITH_INTERLOCK which
can be enabled if the system has these requirements.

Signed-off-by: Damien George <damien@micropython.org>
pull/7046/head
Damien George 2021-03-17 12:34:49 +11:00
rodzic 4cf9928902
commit e05d0a6335
1 zmienionych plików z 72 dodań i 1 usunięć

Wyświetl plik

@ -34,6 +34,7 @@
#include "py/objarray.h"
#include "py/qstr.h"
#include "py/runtime.h"
#include "py/stackctrl.h"
#include "extmod/modbluetooth.h"
#include <string.h>
@ -1135,7 +1136,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(bluetooth_ble_invoke_irq_obj, bluetooth_ble_inv
#if MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS
STATIC mp_obj_t invoke_irq_handler(uint16_t event,
STATIC mp_obj_t invoke_irq_handler_run(uint16_t event,
const mp_int_t *numeric, size_t n_unsigned, size_t n_signed,
const uint8_t *addr,
const mp_obj_bluetooth_uuid_t *uuid,
@ -1185,6 +1186,76 @@ STATIC mp_obj_t invoke_irq_handler(uint16_t event,
return result;
}
#if MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS_WITH_INTERLOCK
// On some systems the BLE event callbacks may occur on a system thread which is not
// a MicroPython thread. In such cases the callback must set up relevant MicroPython
// state and obtain the GIL, to synchronised with the rest of the runtime.
#if MICROPY_ENABLE_PYSTACK
#error not supported
#endif
STATIC mp_obj_t invoke_irq_handler(uint16_t event,
const mp_int_t *numeric, size_t n_unsigned, size_t n_signed,
const uint8_t *addr,
const mp_obj_bluetooth_uuid_t *uuid,
const uint8_t **data, size_t *data_len, size_t n_data) {
// This code may run on an existing MicroPython thread, or a non-MicroPython thread
// that's not using the mp_thread_get_state() value. In the former case the state
// must be restored once this callback finishes.
mp_state_thread_t *ts_orig = mp_thread_get_state();
mp_state_thread_t ts;
if (ts_orig == NULL) {
mp_thread_set_state(&ts);
mp_stack_set_top(&ts + 1); // need to include ts in root-pointer scan
mp_stack_set_limit(MICROPY_PY_BLUETOOTH_SYNC_EVENT_STACK_SIZE - 1024);
ts.gc_lock_depth = 0;
ts.mp_pending_exception = MP_OBJ_NULL;
mp_locals_set(mp_state_ctx.thread.dict_locals); // set from the outer context
mp_globals_set(mp_state_ctx.thread.dict_globals); // set from the outer context
MP_THREAD_GIL_ENTER();
}
mp_obj_t result = mp_const_none;
nlr_buf_t nlr;
if (nlr_push(&nlr) == 0) {
mp_sched_lock();
result = invoke_irq_handler_run(event, numeric, n_unsigned, n_signed, addr, uuid, data, data_len, n_data);
mp_sched_unlock();
nlr_pop();
} else {
// Uncaught exception, print it out.
mp_sched_unlock();
mp_printf(MICROPY_ERROR_PRINTER, "Unhandled exception in IRQ callback handler\n");
mp_obj_print_exception(MICROPY_ERROR_PRINTER, MP_OBJ_FROM_PTR(nlr.ret_val));
}
if (ts_orig == NULL) {
MP_THREAD_GIL_EXIT();
mp_thread_set_state(ts_orig);
}
return result;
}
#else
// BLE event callbacks are called directly from the MicroPython runtime, so additional
// synchronisation is not needed, and BLE event handlers can be called directly.
STATIC mp_obj_t invoke_irq_handler(uint16_t event,
const mp_int_t *numeric, size_t n_unsigned, size_t n_signed,
const uint8_t *addr,
const mp_obj_bluetooth_uuid_t *uuid,
const uint8_t **data, size_t *data_len, size_t n_data) {
return invoke_irq_handler_run(event, numeric, n_unsigned, n_signed, addr, uuid, data, data_len, n_data);
}
#endif
#define NULL_NUMERIC NULL
#define NULL_ADDR NULL
#define NULL_UUID NULL