Skip to content

Commit d2860b5

Browse files
committed
Check background pending before sleep
There is a race between when we run background tasks and when we sleep. If an interrupt happens between the two, then we may delay executing the background task. On some ports we checked this for TinyUSB already. On iMX RT, we didn't which caused USB issues. This PR makes it more generic for all background tasks including USB. Fixes #5086 and maybe others.
1 parent 0569c20 commit d2860b5

File tree

12 files changed

+62
-25
lines changed

12 files changed

+62
-25
lines changed

ports/atmel-samd/supervisor/port.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,11 @@
8080
#include "shared_timers.h"
8181
#include "reset.h"
8282

83+
#include "supervisor/background_callback.h"
8384
#include "supervisor/shared/safe_mode.h"
8485
#include "supervisor/shared/stack.h"
8586
#include "supervisor/shared/tick.h"
8687

87-
#include "tusb.h"
88-
8988
#if CIRCUITPY_GAMEPADSHIFT
9089
#include "shared-module/gamepadshift/__init__.h"
9190
#endif
@@ -628,7 +627,7 @@ void port_idle_until_interrupt(void) {
628627
}
629628
#endif
630629
common_hal_mcu_disable_interrupts();
631-
if (!tud_task_event_ready() && sleep_ok && !_woken_up) {
630+
if (!background_callback_pending() && sleep_ok && !_woken_up) {
632631
__DSB();
633632
__WFI();
634633
}

ports/esp32s2/supervisor/port.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
#include "common-hal/watchdog/WatchDogTimer.h"
4949
#include "common-hal/socketpool/Socket.h"
5050
#include "common-hal/wifi/__init__.h"
51+
#include "supervisor/background_callback.h"
5152
#include "supervisor/memory.h"
5253
#include "supervisor/shared/tick.h"
5354
#include "shared-bindings/rtc/__init__.h"
@@ -329,7 +330,9 @@ void port_interrupt_after_ticks(uint32_t ticks) {
329330
// On the ESP we use FreeRTOS notifications instead of interrupts so this is a
330331
// bit of a misnomer.
331332
void port_idle_until_interrupt(void) {
332-
xTaskNotifyWait(0x01, 0x01, NULL, portMAX_DELAY);
333+
if (!background_callback_pending()) {
334+
xTaskNotifyWait(0x01, 0x01, NULL, portMAX_DELAY);
335+
}
333336
}
334337

335338
// Wrap main in app_main that the IDF expects.

ports/mimxrt10xx/Makefile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,13 @@ CFLAGS += -Os -ftree-vrp -DNDEBUG -ffreestanding
8181
CFLAGS += -DCFG_TUSB_MCU=OPT_MCU_MIMXRT10XX -DCFG_TUD_MIDI_RX_BUFSIZE=512 -DCFG_TUD_CDC_RX_BUFSIZE=512 -DCFG_TUD_MIDI_TX_BUFSIZE=512 -DCFG_TUD_CDC_TX_BUFSIZE=512 -DCFG_TUD_MSC_BUFSIZE=1024
8282

8383
#Debugging/Optimization
84+
# Never set -fno-inline because we use inline to move small functions into routines that must be
85+
# in RAM. If inlining is disallowed, then we may end up calling a function in flash when we cannot.
8486
ifeq ($(DEBUG), 1)
8587
# You may want to disable -flto if it interferes with debugging.
8688
# CFLAGS += -flto -flto-partition=none
8789
# You may want to enable these flags to make setting breakpoints easier.
88-
CFLAGS += -fno-inline -fno-ipa-sra
90+
CFLAGS += -fno-ipa-sra
8991
else
9092
#CFLAGS += -flto -flto-partition=none
9193
endif

ports/mimxrt10xx/linking/common.ld

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ SECTIONS
5151
KEEP(* (.boot_hdr.dcd_data))
5252
. = ALIGN(4);
5353
} > FLASH_IVT
54-
image_vector_table = LOADADDR(.ivt);
5554

5655
.text :
5756
{

ports/mimxrt10xx/supervisor/port.c

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,11 @@
4040
#include "common-hal/pwmio/PWMOut.h"
4141
#include "common-hal/rtc/RTC.h"
4242
#include "common-hal/busio/SPI.h"
43+
#include "shared-bindings/microcontroller/__init__.h"
4344

4445
#include "reset.h"
4546

46-
#include "tusb.h"
47+
#include "supervisor/background_callback.h"
4748

4849
#if CIRCUITPY_GAMEPADSHIFT
4950
#include "shared-module/gamepadshift/__init__.h"
@@ -400,10 +401,15 @@ void port_idle_until_interrupt(void) {
400401
__set_FPSCR(__get_FPSCR() & ~(0x9f));
401402
(void)__get_FPSCR();
402403
}
403-
NVIC_ClearPendingIRQ(SNVS_HP_WRAPPER_IRQn);
404-
CLOCK_SetMode(kCLOCK_ModeWait);
405-
__WFI();
406-
CLOCK_SetMode(kCLOCK_ModeRun);
404+
405+
common_hal_mcu_disable_interrupts();
406+
if (!background_callback_pending()) {
407+
NVIC_ClearPendingIRQ(SNVS_HP_WRAPPER_IRQn);
408+
CLOCK_SetMode(kCLOCK_ModeWait);
409+
__WFI();
410+
CLOCK_SetMode(kCLOCK_ModeRun);
411+
}
412+
common_hal_mcu_enable_interrupts();
407413
}
408414

409415
/**

ports/nrf/supervisor/port.c

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,10 @@
2525
* THE SOFTWARE.
2626
*/
2727

28-
#include <stdint.h>
2928
#include "supervisor/port.h"
29+
30+
#include <stdint.h>
31+
#include "supervisor/background_callback.h"
3032
#include "supervisor/board.h"
3133

3234
#include "nrfx/hal/nrf_clock.h"
@@ -39,6 +41,8 @@
3941
#include "nrf/power.h"
4042
#include "nrf/timers.h"
4143

44+
#include "nrf_nvic.h"
45+
4246
#include "common-hal/microcontroller/Pin.h"
4347
#include "common-hal/_bleio/__init__.h"
4448
#include "common-hal/analogio/AnalogIn.h"
@@ -360,7 +364,12 @@ void port_idle_until_interrupt(void) {
360364

361365
sd_softdevice_is_enabled(&sd_enabled);
362366
if (sd_enabled) {
363-
sd_app_evt_wait();
367+
uint8_t is_nested_critical_region;
368+
sd_nvic_critical_region_enter(&is_nested_critical_region);
369+
if (!background_callback_pending()) {
370+
sd_app_evt_wait();
371+
}
372+
sd_nvic_critical_region_exit(is_nested_critical_region);
364373
} else {
365374
// Call wait for interrupt ourselves if the SD isn't enabled.
366375
// Note that `wfi` should be called with interrupts disabled,
@@ -376,11 +385,7 @@ void port_idle_until_interrupt(void) {
376385
// function (whether or not SD is enabled)
377386
int nested = __get_PRIMASK();
378387
__disable_irq();
379-
bool ok = true;
380-
#if CIRCUITPY_USB
381-
ok = !tud_task_event_ready();
382-
#endif
383-
if (ok) {
388+
if (!background_callback_pending()) {
384389
__DSB();
385390
__WFI();
386391
}

ports/raspberrypi/supervisor/port.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <string.h>
2828
#include <stdlib.h>
2929

30+
#include "supervisor/background_callback.h"
3031
#include "supervisor/board.h"
3132
#include "supervisor/port.h"
3233

@@ -54,8 +55,6 @@
5455
#include "src/common/pico_time/include/pico/time.h"
5556
#include "src/common/pico_binary_info/include/pico/binary_info.h"
5657

57-
#include "tusb.h"
58-
5958
#include "pico/bootrom.h"
6059
#include "hardware/watchdog.h"
6160

@@ -233,7 +232,7 @@ void port_interrupt_after_ticks(uint32_t ticks) {
233232

234233
void port_idle_until_interrupt(void) {
235234
common_hal_mcu_disable_interrupts();
236-
if (!tud_task_event_ready()) {
235+
if (!background_callback_pending()) {
237236
// asm volatile ("dsb 0xF":::"memory");
238237
// __wfi();
239238
}

ports/stm/supervisor/port.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,13 @@
2626
*/
2727

2828
#include <stdint.h>
29-
#include "supervisor/port.h"
29+
#include "supervisor/background_callback.h"
3030
#include "supervisor/board.h"
31+
#include "supervisor/port.h"
3132
#include "lib/timeutils/timeutils.h"
3233

3334
#include "common-hal/microcontroller/Pin.h"
35+
#include "shared-bindings/microcontroller/__init__.h"
3436

3537
#ifdef CIRCUITPY_AUDIOPWMIO
3638
#include "common-hal/audiopwmio/PWMAudioOut.h"
@@ -366,7 +368,11 @@ void port_idle_until_interrupt(void) {
366368
if (stm32_peripherals_rtc_alarm_triggered(PERIPHERALS_ALARM_A)) {
367369
return;
368370
}
369-
__WFI();
371+
common_hal_mcu_disable_interrupts();
372+
if (!background_callback_pending()) {
373+
__WFI();
374+
}
375+
common_hal_mcu_enable_interrupts();
370376
}
371377

372378
// Required by __libc_init_array in startup code if we are compiling using

shared-bindings/storage/__init__.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,11 @@ MP_DEFINE_CONST_FUN_OBJ_0(storage_erase_filesystem_obj, storage_erase_filesystem
165165
//| ...
166166
//|
167167
STATIC mp_obj_t storage_disable_usb_drive(void) {
168+
#if CIRCUITPY_USB_MSC
168169
if (!common_hal_storage_disable_usb_drive()) {
170+
#else
171+
if (true) {
172+
#endif
169173
mp_raise_RuntimeError(translate("Cannot change USB devices now"));
170174
}
171175
return mp_const_none;
@@ -186,7 +190,11 @@ MP_DEFINE_CONST_FUN_OBJ_0(storage_disable_usb_drive_obj, storage_disable_usb_dri
186190
//| ...
187191
//|
188192
STATIC mp_obj_t storage_enable_usb_drive(void) {
193+
#if CIRCUITPY_USB_MSC
189194
if (!common_hal_storage_enable_usb_drive()) {
195+
#else
196+
if (true) {
197+
#endif
190198
mp_raise_RuntimeError(translate("Cannot change USB devices now"));
191199
}
192200
return mp_const_none;

supervisor/background_callback.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
#ifndef CIRCUITPY_INCLUDED_SUPERVISOR_BACKGROUND_CALLBACK_H
2828
#define CIRCUITPY_INCLUDED_SUPERVISOR_BACKGROUND_CALLBACK_H
2929

30+
#include <stdbool.h>
31+
3032
/** Background callbacks are a linked list of tasks to call in the background.
3133
*
3234
* Include a member of type `background_callback_t` inside an object
@@ -69,6 +71,10 @@ void background_callback_add(background_callback_t *cb, background_callback_fun
6971
* whenever the list is non-empty */
7072
void background_callback_run_all(void);
7173

74+
/* True when a background callback is pending. Helpful for checking background state when
75+
* interrupts are disabled. */
76+
bool background_callback_pending(void);
77+
7278
/* During soft reset, remove all pending callbacks and clear the critical section flag */
7379
void background_callback_reset(void);
7480

supervisor/shared/background_callback.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,13 @@ void background_callback_add(background_callback_t *cb, background_callback_fun
6767
background_callback_add_core(cb);
6868
}
6969

70+
bool PLACE_IN_ITCM(background_callback_pending)(void) {
71+
return callback_head != NULL;
72+
}
73+
7074
static bool in_background_callback;
7175
void PLACE_IN_ITCM(background_callback_run_all)() {
72-
if (!callback_head) {
76+
if (!background_callback_pending()) {
7377
return;
7478
}
7579
CALLBACK_CRITICAL_BEGIN;

supervisor/shared/usb/usb.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ void usb_init(void) {
9191

9292
// Set up USB defaults before any USB changes are made in boot.py
9393
void usb_set_defaults(void) {
94-
#if CIRCUITPY_STORAGE
94+
#if CIRCUITPY_STORAGE && CIRCUITPY_USB_MSC
9595
storage_usb_set_defaults();
9696
#endif
9797

0 commit comments

Comments
 (0)