summaryrefslogtreecommitdiff
path: root/extmod/uasyncio/core.py
AgeCommit message (Collapse)Author
2023-06-19extmod/asyncio: Rename uasyncio to asyncio.Jim Mussared
The asyncio module now has much better CPython compatibility and deserves to be just called "asyncio". This will avoid people having to write `from uasyncio import asyncio`. Renames all files, and updates port manifests to use the new path. Also renames the built-in _uasyncio to _asyncio. This work was funded through GitHub Sponsors. Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
2023-02-02top: Update Python formatting to black "2023 stable style".Jim Mussared
See https://black.readthedocs.io/en/stable/the_black_code_style/index.html Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
2022-04-22extmod/uasyncio: Rename and merge TaskQueue push/pop methods.Damien George
These are internal names and can be safely renamed without affecting user code. push_sorted() and push_head() are merged into a single push() method, which is already how the C version is implemented. pop_head() is simply renamed to pop(). The changes are: - q.push_sorted(task, t) -> q.push(task, t) - q.push_head(task) -> q.push(task) - q.pop_head() -> q.pop() The shorter names and removal of push_head() leads to a code size reduction of between 40 and 64 bytes on bare-metal targets. Signed-off-by: Damien George <damien@micropython.org>
2022-03-30extmod/uasyncio: Allow task state to be a callable.Damien George
This implements a form of CPython's "add_done_callback()", but at this stage it is a hidden feature and only intended to be used internally. Signed-off-by: Damien George <damien@micropython.org>
2021-06-16extmod/uasyncio: Fix race with cancelled task waiting on finished task.Damien George
This commit fixes a problem with a race between cancellation of task A and completion of task B, when A waits on B. If task B completes just before task A is cancelled then the cancellation of A does not work. Instead, the CancelledError meant to cancel A gets passed through to B (that's expected behaviour) but B handles it as a "Task exception wasn't retrieved" scenario, printing out such a message (this is because finished tasks point their "coro" attribute to themselves to indicate they are done, and implement the throw() method, but that method inadvertently catches the CancelledError). The correct behaviour is for B to bounce that CancelledError back out. This bug is mainly seen when wait_for() is used, and in that context the symptoms are: - occurs when using wait_for(T, S), if the task T being waited on finishes at exactly the same time as the wait-for timeout S expires - task T will have run to completion - the "Task exception wasn't retrieved message" is printed with "<class 'CancelledError'>" as the error (ie no traceback) - the wait_for(T, S) call never returns (it's never put back on the uasyncio run queue) and all tasks waiting on this are blocked forever from running - uasyncio otherwise continues to function and other tasks continue to be scheduled as normal The fix here reworks the "waiting" attribute of Task to be called "state" and uses it to indicate whether a task is: running and not awaited on, running and awaited on, finished and not awaited on, or finished and awaited on. This means the task does not need to point "coro" to itself to indicate finished, and also allows removal of the throw() method. A benefit of this is that "Task exception wasn't retrieved" messages can go back to being able to print the name of the coroutine function. Fixes issue #7386. Signed-off-by: Damien George <damien@micropython.org>
2021-02-13extmod/uasyncio: Add asyncio.current_task().Jim Mussared
Matches CPython behavior. Fixes #6686
2020-12-02extmod/uasyncio: Delay calling Loop.call_exception_handler by 1 loop.Damien George
When a tasks raises an exception which is uncaught, and no other task await's on that task, then an error message is printed (or a user function called) via a call to Loop.call_exception_handler. In CPython this call is made when the Task object is freed (eg via reference counting) because it's at that point that it is known that the exception that was raised will never be handled. MicroPython does not have reference counting and the current behaviour is to deal with uncaught exceptions as early as possible, ie as soon as they terminate the task. But this can be undesirable because in certain cases a task can start and raise an exception immediately (before any await is executed in that task's coro) and before any other task gets a chance to await on it to catch the exception. This commit changes the behaviour so that tasks which end due to an uncaught exception are scheduled one more time for execution, and if they are not await'ed on by the next scheduling loop, then the exception handler is called (eg the exception is printed out). Signed-off-by: Damien George <damien@micropython.org>
2020-08-22extmod/uasyncio: Truncate negative sleeps to 0.Damien George
Otherwise a task that continuously awaits on a large negative sleep can monopolise the scheduler (because its wake time is always less than everything else in the pairing heap). Signed-off-by: Damien George <damien@micropython.org>
2020-04-13extmod/uasyncio: Add Loop.new_event_loop method.Damien George
This commit adds Loop.new_event_loop() which is used to reset the singleton event loop. This functionality is put here instead of in Loop.close() to make it possible to write code that is compatible with CPython.
2020-04-04extmod/uasyncio: Add global exception handling methods.Kevin Köck
This commit adds support for global exception handling in uasyncio according to the CPython error handling: https://docs.python.org/3/library/asyncio-eventloop.html#error-handling-api This allows a program to receive exceptions from detached tasks and log them to an appropriate location, instead of them being printed to the REPL. The implementation preallocates a context dictionary so in case of an exception there shouldn't be any RAM allocation. The approach here is compatible with CPython except that in CPython the exception handler is called once the task that threw an uncaught exception is freed, whereas in MicroPython the exception handler is called immediately when the exception is thrown.
2020-04-02extmod/uasyncio: Implement Loop.stop() to stop the event loop.Damien George
2020-04-01extmod/uasyncio: Don't create a Loop instance in get_event_loop().Damien George
The event loop is (for now) just a singleton so make it so that Loop instances are not needed.
2020-03-26extmod/uasyncio: Add optional implementation of core uasyncio in C.Damien George
Implements Task and TaskQueue classes in C, using a pairing-heap data structure. Using this reduces RAM use of each Task, and improves overall performance of the uasyncio scheduler.
2020-03-26extmod/uasyncio: Add new implementation of uasyncio module.Damien George
This commit adds a completely new implementation of the uasyncio module. The aim of this version (compared to the original one in micropython-lib) is to be more compatible with CPython's asyncio module, so that one can more easily write code that runs under both MicroPython and CPython (and reuse CPython asyncio libraries, follow CPython asyncio tutorials, etc). Async code is not easy to write and any knowledge users already have from CPython asyncio should transfer to uasyncio without effort, and vice versa. The implementation here attempts to provide good compatibility with CPython's asyncio while still being "micro" enough to run where MicroPython runs. This follows the general philosophy of MicroPython itself, to make it feel like Python. The main change is to use a Task object for each coroutine. This allows more flexibility to queue tasks in various places, eg the main run loop, tasks waiting on events, locks or other tasks. It no longer requires pre-allocating a fixed queue size for the main run loop. A pairing heap is used to queue Tasks. It's currently implemented in pure Python, separated into components with lazy importing for optional components. In the future parts of this implementation can be moved to C to improve speed and reduce memory usage. But the aim is to maintain a pure-Python version as a reference version.