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:
parent
847691798b
commit
872e0fb0e0
@ -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
|
||||
|
@ -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`:
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
||||
|
@ -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:
|
||||
"""
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user