mirror of
https://github.com/micropython/micropython.git
synced 2025-07-21 21:11:12 +02:00
This commit fixes three open issues related to the asyncio scheduler exiting prematurely when the main task queue is empty, in cases where CPython would not exit (for example, because the main task is not done because it's on a different queue). In the first case, the scheduler exits because running a task via `run_until_complete` did not schedule any dependent tasks. In the other two cases, the scheduler exits because the tasks are queued in an event queue. Tests have been added which reproduce the original issues. These test cases document the unauthorized use of `Event.set()` from a soft IRQ, and are skipped in unsupported environments (webassembly and native emitter). Fixes issues #16759, #16569 and #16318. Signed-off-by: Yoctopuce dev <dev@yoctopuce.com>
65 lines
1.7 KiB
Python
65 lines
1.7 KiB
Python
# Ensure that an asyncio task can wait on an Event when the
|
|
# _task_queue is empty
|
|
# https://github.com/micropython/micropython/issues/16569
|
|
|
|
try:
|
|
import asyncio
|
|
except ImportError:
|
|
print("SKIP")
|
|
raise SystemExit
|
|
|
|
# This test requires checking that the asyncio scheduler
|
|
# remains active "indefinitely" when the task queue is empty.
|
|
#
|
|
# To check this, we need another independent scheduler that
|
|
# can wait for a certain amount of time. So we have to
|
|
# create one using micropython.schedule() and time.ticks_ms()
|
|
#
|
|
# Technically, this code breaks the rules, as it is clearly
|
|
# documented that Event.set() should _NOT_ be called from a
|
|
# schedule (soft IRQ) because in some cases, a race condition
|
|
# can occur, resulting in a crash. However:
|
|
# - since the risk of a race condition in that specific
|
|
# case has been analysed and excluded
|
|
# - given that there is no other simple alternative to
|
|
# write this test case,
|
|
# an exception to the rule was deemed acceptable. See
|
|
# https://github.com/micropython/micropython/pull/16772
|
|
|
|
import micropython, time
|
|
|
|
try:
|
|
micropython.schedule
|
|
except AttributeError:
|
|
print("SKIP")
|
|
raise SystemExit
|
|
|
|
|
|
evt = asyncio.Event()
|
|
|
|
|
|
def schedule_watchdog(end_ticks):
|
|
if time.ticks_diff(end_ticks, time.ticks_ms()) <= 0:
|
|
print("asyncio still pending, unlocking event")
|
|
# Caution: about to call Event.set() from a schedule
|
|
# (see the note in the comment above)
|
|
evt.set()
|
|
return
|
|
micropython.schedule(schedule_watchdog, end_ticks)
|
|
|
|
|
|
async def foo():
|
|
print("foo waiting")
|
|
schedule_watchdog(time.ticks_add(time.ticks_ms(), 100))
|
|
await evt.wait()
|
|
print("foo done")
|
|
|
|
|
|
async def main():
|
|
print("main started")
|
|
await foo()
|
|
print("main done")
|
|
|
|
|
|
asyncio.run(main())
|