lionsos icon indicating copy to clipboard operation
lionsos copied to clipboard

Support for Micropython Keyboard Interrupts

Open Kswin01 opened this issue 1 year ago • 4 comments

This PR adapts the micropython port to support keyboard interrupts. We buffer the input characters from the sDDF serial subsystem in internal micropython buffers, so that we can process interrupt chars as they come in. We then read characters from our internal buffers when reads are requested. The transmit path remains unchanged.

Kswin01 avatar Jun 13 '24 05:06 Kswin01

One issue is that while inputting ^C will kill executions that wait for IO, it won't do anything for executions that don't. Example:

Inputting ^C will kill

async def f():
    await asyncio.sleep(10)
asyncio.run(f())

but won't kill

for i in range(100000):
    print(i)

JE-Archer avatar Jun 15 '24 09:06 JE-Archer

One issue is that while inputting ^C will kill executions that wait for IO, it won't do anything for executions that don't. Example:

Inputting ^C will kill

async def f():
    await asyncio.sleep(10)
asyncio.run(f())

but won't kill

for i in range(100000):
    print(i)

That's correct, if we never switch to the t_event co-routine, we will never process input and therefore not receive the keyboard interrupts. We couldn't think of a great way to deliver these characters within the current design.

Kswin01 avatar Jun 17 '24 00:06 Kswin01

Essentially we will not get back to the micropython pyexec repl loop if we are in the case of executing an infinite loop. The repl will consume each "function" and lex + compile it (which counts as a loop and its contents), then execute it. If we are in a while loop, we will never return back to the main handling loop where we actually do the processing of characters, and while therefore not be able to parse the ctrl+c character.

Kswin01 avatar Apr 07 '25 06:04 Kswin01

The CI is failing.

Ivan-Velickovic avatar May 09 '25 03:05 Ivan-Velickovic

My 3 comments from looking at micropython:

We define the EVENT_POLL_HOOK as a 500ms delay, which seems not right.

https://github.com/au-ts/lionsos/blob/eb870083009aac2786179a97fd7eadd97491f5d1/components/micropython/mpconfigport.h#L113

Other ports, e.g. Zephyr call mp_handle_pending: https://github.com/micropython/micropython/blob/d441788975a6dfb277ad9a3d54d651ae48817d16/ports/zephyr/mpconfigport.h#L156-L172

There's also the MICROPY_ASYNC_KBD_INTR interrupt which uses signals:

https://github.com/micropython/micropython/blob/d441788975a6dfb277ad9a3d54d651ae48817d16/py/mpconfig.h#L730-L734

which the UNIX port uses:

https://github.com/micropython/micropython/blob/d441788975a6dfb277ad9a3d54d651ae48817d16/ports/unix/variants/mpconfigvariant_common.h#L32

https://github.com/micropython/micropython/blob/d441788975a6dfb277ad9a3d54d651ae48817d16/ports/unix/unix_mphal.c#L49-L71

midnightveil avatar Sep 15 '25 02:09 midnightveil

Other ports, e.g. Zephyr call mp_handle_pending

This PR uses mp_handle_pending

There's also the MICROPY_ASYNC_KBD_INTR interrupt which uses signals:

This requires the port to implement some sort of threading library from memory. This is also what the UNIX port does. mpthreadport.

Kswin01 avatar Sep 15 '25 04:09 Kswin01

KBD interrupts now work for long executions as well as sleeps, currently file system blocking calls and i2c operations can't be interrupted (although easy to change in the future). There is very unlikely but possible chance of a notification that ought to be discarded (since it was interrupted), getting through anyway (if it is received during the period of time where it should be discarded, but it is not processed before the next call to wait on the timer channel). In this case, the Micropython cothread may be awoken early from a call to delay.

Has been tested on the fileIO and webserver examples on odroidc4 and qemu.

Courtney3141 avatar Sep 22 '25 05:09 Courtney3141