mirror of
https://github.com/micropython/micropython.git
synced 2025-09-06 01:40:34 +02:00
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>
55 lines
1.6 KiB
Python
55 lines
1.6 KiB
Python
# MicroPython uasyncio module
|
|
# MIT license; Copyright (c) 2019-2020 Damien P. George
|
|
|
|
from . import core
|
|
|
|
|
|
async def wait_for(aw, timeout, sleep=core.sleep):
|
|
aw = core._promote_to_task(aw)
|
|
if timeout is None:
|
|
return await aw
|
|
|
|
def cancel(aw, timeout, sleep):
|
|
await sleep(timeout)
|
|
aw.cancel()
|
|
|
|
cancel_task = core.create_task(cancel(aw, timeout, sleep))
|
|
try:
|
|
ret = await aw
|
|
except core.CancelledError:
|
|
# Ignore CancelledError from aw, it's probably due to timeout
|
|
pass
|
|
finally:
|
|
# Cancel the "cancel" task if it's still active (optimisation instead of cancel_task.cancel())
|
|
if cancel_task.coro is not cancel_task:
|
|
core._task_queue.remove(cancel_task)
|
|
if cancel_task.coro is cancel_task:
|
|
# Cancel task ran to completion, ie there was a timeout
|
|
raise core.TimeoutError
|
|
return ret
|
|
|
|
|
|
def wait_for_ms(aw, timeout):
|
|
return wait_for(aw, timeout, core.sleep_ms)
|
|
|
|
|
|
async def gather(*aws, return_exceptions=False):
|
|
ts = [core._promote_to_task(aw) for aw in aws]
|
|
for i in range(len(ts)):
|
|
try:
|
|
# TODO handle cancel of gather itself
|
|
# if ts[i].coro:
|
|
# iter(ts[i]).waiting.push_head(cur_task)
|
|
# try:
|
|
# yield
|
|
# except CancelledError as er:
|
|
# # cancel all waiting tasks
|
|
# raise er
|
|
ts[i] = await ts[i]
|
|
except Exception as er:
|
|
if return_exceptions:
|
|
ts[i] = er
|
|
else:
|
|
raise er
|
|
return ts
|