Description
Running the following minimal example on the Raspberry Pi Pico:
import _thread
def burn():
while True:
pass
_thread.start_new_thread(burn, [])
... starts an endless loop on the second CPU core (aka core 1). If a soft reboot via Ctrl+D is then performed, Micropython sometimes locks up to the point where only a hardware reset of the Pico helps. (You might have to try this several times, it does not happen every soft reboot.)
I've traced this to an endless wait for the spinlock in _osal_q_lock. I think, this is because the TinyUSB task is called on both cores via MICROPY_HW_USBDEV_TASK_HOOK
:
micropython/ports/rp2/mpconfigport.h
Line 232 in 5873390
... which is called via
MICROPY_VM_HOOK_LOOP
in vm.c.
Therefore, I think this is what happens: During a soft reboot, core 1 is reset:
micropython/ports/rp2/mpthreadport.c
Line 50 in 5873390
If this happens at an unfavorable moment, when core 1 holds the Tiny USB spinlock, said lock is never released and core 0 hangs indefinitely when trying to acquire the lock the next time.
At first glance, only calling the TinyUSB task on core 0:
#define MICROPY_HW_USBDEV_TASK_HOOK extern void tud_task(void); if (get_core_num() == 0) tud_task();
... seems to fix the problem, as I cannot observe any random lockups.
However, I'm not familiar enough with Micropython's internals to know if there are situations in which the task needs to run on core 1, too. Therefore, I did not propose this tentative fix as a PR.