microbit-foundation / micropython-microbit-v2

Temporary home for MicroPython for micro:bit v2 as we stablise it before pushing upstream
MIT License
41 stars 22 forks source link

This programme cannot be stopped with Ctrl+C #192

Open microbit-carlos opened 2 months ago

microbit-carlos commented 2 months ago

Tested with v2.1.2 and the lates recording&playback branch.

I've managed to reduce the issue it to this exampe. When connected to serial Ctrl+C does not stop the programme.

from microbit import *

def radio_received_msgs():
    msg_length = 0
    while True:
        if msg_length:
            yield audio.AudioFrame()

audio.play(radio_received_msgs())
dpgeorge commented 2 months ago

The problem is that the generator is looping forever and never yielding. A simpler reproduction is:

def gen():
    while True:
        pass
    yield
audio.play(gen())

This is really hard to fix, because the generator code is running in the background. In MicroPython, ctrl-C can only break out of code running in the foreground. It's assumed that you don't write nasty code that runs in the background!

If we allow ctrl-C to break out of the generator then foreground code may miss a ctrl-C. Eg if you have a well behaved generator then it may still catch ctrl-C:

def gen():
    f = AudioFrame()
    while True:
        yield f

audio.play(gen())

# pressing ctrl-C might not stop this loop, it might instead stop the generator
while True:
    pass
microbit-carlos commented 2 months ago

Thanks Damien!

Adding a sleep(1) to the generator does work with Ctrl+C, is that because CODAL might be yielding to the MicroPython foreground?

Either way, I suspected it'd be something along these lines, so happy to close this as a "won't fix", just needed to check if there some something sensible that could be done to resolve it.

dpgeorge commented 2 months ago

Adding a sleep(1) to the generator does work with Ctrl+C, is that because CODAL might be yielding to the MicroPython foreground?

The sleep() function explicitly checks for ctrl-C, so that's why it's being handled there. But using sleep() within an audio generator callback is definitely not recommended! That will significantly affect the scheduling of the entire system, not just audio.

so happy to close this as a "won't fix",

I think we should document audio generators as callbacks that happen in the background and that must return quickly, and make it clear they are a bit different to normal foreground Python code (ie can't use ctrl-C to stop them).