feat(core): introduce timeout to loop.wait()

matejcik/global-layout-new
matejcik 11 months ago
parent dc9a735393
commit 748d516ac4

@ -371,7 +371,7 @@ class Cmd:
async def _read_cmd(iface: HID) -> Cmd | None: async def _read_cmd(iface: HID) -> Cmd | None:
desc_init = frame_init() desc_init = frame_init()
desc_cont = frame_cont() desc_cont = frame_cont()
read = loop.wait(iface.iface_num() | io.POLL_READ) read = loop.wait(iface.iface_num() | io.POLL_READ, timeout_ms=_CTAP_HID_TIMEOUT_MS)
buf = await read buf = await read
while True: while True:
@ -409,8 +409,9 @@ async def _read_cmd(iface: HID) -> Cmd | None:
data = data[:bcnt] data = data[:bcnt]
while datalen < bcnt: while datalen < bcnt:
buf = await loop.race(read, loop.sleep(_CTAP_HID_TIMEOUT_MS)) try:
if not isinstance(buf, bytes): buf = await read
except loop.Timeout:
if __debug__: if __debug__:
warning(__name__, "_ERR_MSG_TIMEOUT") warning(__name__, "_ERR_MSG_TIMEOUT")
await send_cmd(cmd_error(ifrm_cid, _ERR_MSG_TIMEOUT), iface) await send_cmd(cmd_error(ifrm_cid, _ERR_MSG_TIMEOUT), iface)
@ -493,7 +494,9 @@ async def send_cmd(cmd: Cmd, iface: HID) -> None:
if offset < datalen: if offset < datalen:
frm = overlay_struct(buf, cont_desc) frm = overlay_struct(buf, cont_desc)
write = loop.wait(iface.iface_num() | io.POLL_WRITE) write = loop.wait(
iface.iface_num() | io.POLL_WRITE, timeout_ms=_CTAP_HID_TIMEOUT_MS
)
while offset < datalen: while offset < datalen:
frm.seq = seq frm.seq = seq
copied = utils.memcpy(frm.data, 0, cmd.data, offset, datalen) copied = utils.memcpy(frm.data, 0, cmd.data, offset, datalen)
@ -501,10 +504,7 @@ async def send_cmd(cmd: Cmd, iface: HID) -> None:
if copied < _FRAME_CONT_SIZE: if copied < _FRAME_CONT_SIZE:
frm.data[copied:] = bytearray(_FRAME_CONT_SIZE - copied) frm.data[copied:] = bytearray(_FRAME_CONT_SIZE - copied)
while True: while True:
ret = await loop.race(write, loop.sleep(_CTAP_HID_TIMEOUT_MS)) await write
if ret is not None:
raise TimeoutError
if iface.write(buf) > 0: if iface.write(buf) > 0:
break break
seq += 1 seq += 1

@ -20,9 +20,10 @@ if TYPE_CHECKING:
Callable, Callable,
Coroutine, Coroutine,
Generator, Generator,
Union,
) )
Task = Coroutine | Generator Task = Union[Coroutine, Generator, "wait"]
AwaitableTask = Task | Awaitable AwaitableTask = Task | Awaitable
Finalizer = Callable[[Task, Any], None] Finalizer = Callable[[Task, Any], None]
@ -205,6 +206,13 @@ class Syscall:
pass pass
class Timeout(Exception):
pass
_TIMEOUT_ERROR = Timeout()
class sleep(Syscall): class sleep(Syscall):
"""Pause current task and resume it after given delay. """Pause current task and resume it after given delay.
@ -236,11 +244,41 @@ class wait(Syscall):
>>> event, x, y = await loop.wait(io.TOUCH) # await touch event >>> event, x, y = await loop.wait(io.TOUCH) # await touch event
""" """
def __init__(self, msg_iface: int) -> None: _DO_NOT_RESCHEDULE = Syscall()
def __init__(self, msg_iface: int, timeout_ms: int | None = None) -> None:
self.msg_iface = msg_iface self.msg_iface = msg_iface
self.timeout_ms = timeout_ms
self.task: Task | None = None
def handle(self, task: Task) -> None: def handle(self, task: Task) -> None:
pause(task, self.msg_iface) self.task = task
pause(self, self.msg_iface)
if self.timeout_ms is not None:
deadline = utime.ticks_add(utime.ticks_ms(), self.timeout_ms)
schedule(self, _TIMEOUT_ERROR, deadline)
def send(self, __value: Any) -> Any:
assert self.task is not None
_paused[self.msg_iface].discard(self)
_queue.discard(self)
_step(self.task, __value)
return self._DO_NOT_RESCHEDULE
throw = send
def close(self) -> None:
pass
def __iter__(self) -> Generator:
try:
return (yield self)
except BaseException:
# exception was raised on the waiting task externally with
# close() or throw(), kill the children tasks and re-raise
_queue.discard(self)
_paused[self.msg_iface].discard(self)
raise
_type_gen: type[Generator] = type((lambda: (yield))()) _type_gen: type[Generator] = type((lambda: (yield))())

Loading…
Cancel
Save