1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-11-26 01:18:28 +00:00

core: lower scheduler resolution to milliseconds

This avoids problems with large timeouts causing the scheduler queue to
think the time counter has overflown, and ordering the autolock task before
immediate tasks.

The maximum reasonable time difference is 0x20000000, which in
microseconds is ~8 minutes, but in milliseconds a more reasonable ~6
days.
This commit is contained in:
matejcik 2020-06-02 11:02:06 +02:00 committed by matejcik
parent 847691798b
commit 872e0fb0e0
11 changed files with 35 additions and 36 deletions

View File

@ -28,7 +28,7 @@
/// package: trezorio.__init__
/// def poll(ifaces: Iterable[int], list_ref: List, timeout_us: int) -> bool:
/// def poll(ifaces: Iterable[int], list_ref: List, timeout_ms: int) -> bool:
/// """
/// Wait until one of `ifaces` is ready to read or write (using masks
// `io.POLL_READ` and `io.POLL_WRITE`) and assign the result into
@ -42,14 +42,14 @@
/// If timeout occurs, False is returned, True otherwise.
/// """
STATIC mp_obj_t mod_trezorio_poll(mp_obj_t ifaces, mp_obj_t list_ref,
mp_obj_t timeout_us) {
mp_obj_t timeout_ms) {
mp_obj_list_t *ret = MP_OBJ_TO_PTR(list_ref);
if (!MP_OBJ_IS_TYPE(list_ref, &mp_type_list) || ret->len < 2) {
mp_raise_TypeError("invalid list_ref");
}
const mp_uint_t timeout = trezor_obj_get_uint(timeout_us);
const mp_uint_t deadline = mp_hal_ticks_us() + timeout;
const mp_uint_t timeout = trezor_obj_get_uint(timeout_ms);
const mp_uint_t deadline = mp_hal_ticks_ms() + timeout;
mp_obj_iter_buf_t iterbuf;
for (;;) {
@ -125,7 +125,7 @@ STATIC mp_obj_t mod_trezorio_poll(mp_obj_t ifaces, mp_obj_t list_ref,
}
}
if (mp_hal_ticks_us() >= deadline) {
if (mp_hal_ticks_ms() >= deadline) {
break;
} else {
MICROPY_EVENT_POLL_HOOK

View File

@ -68,7 +68,7 @@ class HID:
# extmod/modtrezorio/modtrezorio-poll.h
def poll(ifaces: Iterable[int], list_ref: List, timeout_us: int) -> bool:
def poll(ifaces: Iterable[int], list_ref: List, timeout_ms: int) -> bool:
"""
Wait until one of `ifaces` is ready to read or write (using masks
`list_ref`:

View File

@ -191,7 +191,7 @@ class Bip39Keyboard(ui.Layout):
async def handle_input(self) -> None:
touch = loop.wait(io.TOUCH)
timeout = loop.sleep(1000 * 1000 * 1)
timeout = loop.sleep(1000)
race_touch = loop.race(touch)
race_timeout = loop.race(touch, timeout)

View File

@ -201,7 +201,7 @@ class Slip39Keyboard(ui.Layout):
async def handle_input(self) -> None:
touch = loop.wait(io.TOUCH)
timeout = loop.sleep(1000 * 1000 * 1)
timeout = loop.sleep(1000)
race_touch = loop.race(touch)
race_timeout = loop.race(touch, timeout)

View File

@ -429,7 +429,7 @@ async def read_cmd(iface: io.HID) -> Optional[Cmd]:
data = data[:bcnt]
while datalen < bcnt:
buf = await loop.race(read, loop.sleep(_CTAP_HID_TIMEOUT_MS * 1000))
buf = await loop.race(read, loop.sleep(_CTAP_HID_TIMEOUT_MS))
if not isinstance(buf, bytes):
if __debug__:
log.warning(__name__, "_ERR_MSG_TIMEOUT")
@ -520,7 +520,7 @@ async def send_cmd(cmd: Cmd, iface: io.HID) -> None:
if copied < _FRAME_CONT_SIZE:
frm.data[copied:] = bytearray(_FRAME_CONT_SIZE - copied)
while True:
ret = await loop.race(write, loop.sleep(_CTAP_HID_TIMEOUT_MS * 1000))
ret = await loop.race(write, loop.sleep(_CTAP_HID_TIMEOUT_MS))
if ret is not None:
raise TimeoutError
@ -1060,7 +1060,7 @@ class DialogManager:
if self.state.keepalive_status() != _KEEPALIVE_STATUS_NONE:
cmd = cmd_keepalive(self.state.cid, self.state.keepalive_status())
await send_cmd(cmd, self.iface)
await loop.sleep(_KEEPALIVE_INTERVAL_MS * 1000)
await loop.sleep(_KEEPALIVE_INTERVAL_MS)
finally:
self.keepalive = None

View File

@ -74,7 +74,7 @@ def schedule(
if reschedule:
_queue.discard(task)
if deadline is None:
deadline = utime.ticks_us()
deadline = utime.ticks_ms()
if finalizer is not None:
_finalizers[id(task)] = finalizer
_queue.push(deadline, task, value)
@ -123,9 +123,9 @@ def run() -> None:
while _queue or _paused:
# compute the maximum amount of time we can wait for a message
if _queue:
delay = utime.ticks_diff(_queue.peektime(), utime.ticks_us())
delay = utime.ticks_diff(_queue.peektime(), utime.ticks_ms())
else:
delay = 1000000 # wait for 1 sec maximum if queue is empty
delay = 1000 # wait for 1 sec maximum if queue is empty
if __debug__:
# process synthetic events
@ -227,22 +227,21 @@ SLEEP_FOREVER = Syscall()
class sleep(Syscall):
"""
Pause current task and resume it after given delay. Although the delay is
given in microseconds, sub-millisecond precision is not guaranteed. Result
value is the calculated deadline.
"""Pause current task and resume it after given delay.
Result value is the calculated deadline.
Example:
>>> planned = await loop.sleep(1000 * 1000) # sleep for 1ms
>>> print('missed by %d us', utime.ticks_diff(utime.ticks_us(), planned))
>>> planned = await loop.sleep(1000) # sleep for 1s
>>> print('missed by %d ms', utime.ticks_diff(utime.ticks_ms(), planned))
"""
def __init__(self, delay_us: int) -> None:
self.delay_us = delay_us
def __init__(self, delay_ms: int) -> None:
self.delay_ms = delay_ms
def handle(self, task: Task) -> None:
deadline = utime.ticks_add(utime.ticks_us(), self.delay_us)
deadline = utime.ticks_add(utime.ticks_ms(), self.delay_ms)
schedule(task, deadline, deadline)

View File

@ -85,8 +85,8 @@ def pulse(period: int, offset: int = 0) -> float:
async def _alert(count: int) -> None:
short_sleep = loop.sleep(20000)
long_sleep = loop.sleep(80000)
short_sleep = loop.sleep(20)
long_sleep = loop.sleep(80)
for i in range(count * 2):
if i % 2 == 0:
display.backlight(style.BACKLIGHT_MAX)
@ -223,9 +223,9 @@ RENDER = const(-255)
# Event dispatched when components should mark themselves for re-painting.
REPAINT = const(-256)
# How long, in microseconds, should the layout rendering task sleep betweeen
# How long, in milliseconds, should the layout rendering task sleep betweeen
# the render calls.
_RENDER_DELAY_US = const(10000) # 10 msec
_RENDER_DELAY_MS = const(10)
class Component:
@ -306,7 +306,7 @@ class Layout(Component):
"""
BACKLIGHT_LEVEL = style.BACKLIGHT_NORMAL
RENDER_SLEEP = loop.sleep(_RENDER_DELAY_US) # type: loop.Syscall
RENDER_SLEEP = loop.sleep(_RENDER_DELAY_MS) # type: loop.Syscall
async def __iter__(self) -> ResultValue:
"""

View File

@ -208,7 +208,7 @@ class PassphraseKeyboard(ui.Layout):
async def handle_input(self) -> None:
touch = loop.wait(io.TOUCH)
timeout = loop.sleep(1000 * 1000 * 1)
timeout = loop.sleep(1000)
race_touch = loop.race(touch)
race_timeout = loop.race(touch, timeout)

View File

@ -19,5 +19,5 @@ class Popup(ui.Layout):
return self.handle_input(), self.handle_rendering(), self.handle_timeout()
def handle_timeout(self) -> loop.Task: # type: ignore
yield loop.sleep(self.time_ms * 1000)
yield loop.sleep(self.time_ms)
raise ui.Result(None)

View File

@ -181,7 +181,7 @@ class IdleTimer:
"""
for callback, task in self.tasks.items():
timeout_us = self.timeouts[callback]
deadline = utime.ticks_add(utime.ticks_us(), timeout_us)
deadline = utime.ticks_add(utime.ticks_ms(), timeout_us)
loop.schedule(task, None, deadline, reschedule=True)
def set(self, timeout_ms: int, callback: IdleCallback) -> None:
@ -207,7 +207,7 @@ class IdleTimer:
if callback in self.tasks:
loop.close(self.tasks[callback])
self.timeouts[callback] = timeout_ms * 1000
self.timeouts[callback] = timeout_ms
self.tasks[callback] = self._timeout_task(callback)
self.touch()

View File

@ -157,9 +157,9 @@ syscalls is the preferred method of writing code.
The following syscalls and constructs are available:
**`loop.sleep(delay_us: int)`**: Suspend execution until the given delay (in
microseconds) elapses. Sub-millisecond precision is not guaranteed, however.
Return value is the planned deadline in microseconds since system start.
**`loop.sleep(delay_ms: int)`**: Suspend execution until the given delay (in
milliseconds) elapses. Return value is the planned deadline in milliseconds since system
start.
Calling `await loop.sleep(0)` yields execution to other tasks, and schedules the current
task for the next tick.
@ -174,7 +174,7 @@ the first of them finishes.
It is possible to specify wait timeout for `loop.wait` by using `loop.race`:
```python
result = await loop.race(loop.wait(io.TOUCH), loop.sleep(1000 * 1000))
result = await loop.race(loop.wait(io.TOUCH), loop.sleep(1000))
```
This introduces scheduling gaps: every child is treated as a task and scheduled
to run. This means that if the child is a syscall, as in the above example, its action