diff --git a/docs/library/uasyncio.rst b/docs/library/uasyncio.rst index ebe0c34eb..ff3232ebd 100644 --- a/docs/library/uasyncio.rst +++ b/docs/library/uasyncio.rst @@ -251,13 +251,17 @@ Event Loop .. method:: Loop.run_forever() - Run the event loop forever. + Run the event loop until `stop()` is called. .. method:: Loop.run_until_complete(awaitable) Run the given *awaitable* until it completes. If *awaitable* is not a task then it will be promoted to one. +.. method:: Loop.stop() + + Stop the event loop. + .. method:: Loop.close() Close the event loop. diff --git a/extmod/uasyncio/core.py b/extmod/uasyncio/core.py index 4a8597a3f..dd0229ee4 100644 --- a/extmod/uasyncio/core.py +++ b/extmod/uasyncio/core.py @@ -214,17 +214,33 @@ def run(coro): # Event loop wrapper +async def _stopper(): + pass + + +_stop_task = None + + class Loop: def create_task(coro): return create_task(coro) def run_forever(): - run_until_complete() + global _stop_task + _stop_task = Task(_stopper(), globals()) + run_until_complete(_stop_task) # TODO should keep running until .stop() is called, even if there're no tasks left def run_until_complete(aw): return run_until_complete(_promote_to_task(aw)) + def stop(): + global _stop_task + if _stop_task is not None: + _task_queue.push_head(_stop_task) + # If stop() is called again, do nothing + _stop_task = None + def close(): pass diff --git a/tests/extmod/uasyncio_loop_stop.py b/tests/extmod/uasyncio_loop_stop.py new file mode 100644 index 000000000..23507f9a7 --- /dev/null +++ b/tests/extmod/uasyncio_loop_stop.py @@ -0,0 +1,45 @@ +# Test Loop.stop() to stop the event loop + +try: + import uasyncio as asyncio +except ImportError: + try: + import asyncio + except ImportError: + print("SKIP") + raise SystemExit + + +async def task(): + print("task") + + +async def main(): + print("start") + + # Stop the loop after next yield + loop.stop() + + # Check that calling stop() again doesn't do/break anything + loop.stop() + + # This await should stop + print("sleep") + await asyncio.sleep(0) + + # Schedule stop, then create a new task, then yield + loop.stop() + asyncio.create_task(task()) + await asyncio.sleep(0) + + # Final stop + print("end") + loop.stop() + + +loop = asyncio.get_event_loop() +loop.create_task(main()) + +for i in range(3): + print("run", i) + loop.run_forever() diff --git a/tests/extmod/uasyncio_loop_stop.py.exp b/tests/extmod/uasyncio_loop_stop.py.exp new file mode 100644 index 000000000..bada5f0d8 --- /dev/null +++ b/tests/extmod/uasyncio_loop_stop.py.exp @@ -0,0 +1,7 @@ +run 0 +start +sleep +run 1 +run 2 +task +end