← index #11837Issue #6893
Off-topic · high · value 0.845
QUERY · ISSUE

Zephyr port: async event loop implementation starves CPU

openby bogdanmopened 2023-06-21updated 2023-06-22
enhancementport-zephyr

When using asyncio in MicroPython, the event loop implementation (asyncio.run) ends up polling Python objects in a queue. The polling code does this (extmod/modselect.c:poll_poll_internal):

...
    mp_uint_t start_tick = mp_hal_ticks_ms();
    mp_uint_t n_ready;
    for (;;) {
        // poll the objects
        n_ready = poll_map_poll(&self->poll_map, NULL);
        if (n_ready > 0 || (timeout != (mp_uint_t)-1 && mp_hal_ticks_ms() - start_tick >= timeout)) {
            break;
        }
        MICROPY_EVENT_POLL_HOOK
    }
...

The for(;;) loop above is a busy wait loop. In the Zephyr port, it consumes a lot of CPU time and it starves other threads. Even when MICROPY_EVENT_POOL_HOOK is set to k_yield (and thus the loop gives control back to the OS scheduler after each iteration) the code will still take most of the CPU time. Setting MICROPY_EVENT_POOL_HOOK to something like k_msleep(100) (which waits 100ms before running another iteration of the loop) fixes the starving issue, but it delays the Python thread for no good reason.

I have a possible solution for this, but please let me know first if there is interest to move this forward, since lately I got the impression that the Zephyr port isn't exactly a "fist class citizen" in the world of MicroPython ports.

CANDIDATE · ISSUE

uasyncio Event unexpected behaviour with only one task

closedby peterhinchopened 2021-02-13updated 2024-09-13

[EDIT] Apologies, @kevinkk525 has pointed out that this is a duplcate of https://github.com/micropython/micropython/issues/5843

This prints "start" then runs to completion where I would expect it to wait indefinitely on the Event:

import uasyncio as asyncio

evt = asyncio.Event()

async def main_loop():
    while True:
        print("start")
        await evt.wait()
        await asyncio.sleep(1)
        print("Got event")

asyncio.run(main_loop())

If an additional task is running the main_loop task behaves as expected, waiting forever:

import uasyncio as asyncio

evt = asyncio.Event()

async def main_loop():
    while True:
        print("start")
        await evt.wait()
        await asyncio.sleep(1)
        print("Got event")

async def main():
    asyncio.create_task(main_loop())
    while True:
        await asyncio.sleep(1)

asyncio.run(main())

Keyboard

j / / n
next pair
k / / p
previous pair
1 / / h
show query pane
2 / / l
show candidate pane
c
copy suggested comment
r
toggle reasoning
g i
go to index
?
show this help
esc
close overlays

press ? or esc to close

copied