mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-07-16 19:48:09 +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__
|
/// 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
|
/// 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
|
// `io.POLL_READ` and `io.POLL_WRITE`) and assign the result into
|
||||||
@ -42,14 +42,14 @@
|
|||||||
/// If timeout occurs, False is returned, True otherwise.
|
/// If timeout occurs, False is returned, True otherwise.
|
||||||
/// """
|
/// """
|
||||||
STATIC mp_obj_t mod_trezorio_poll(mp_obj_t ifaces, mp_obj_t list_ref,
|
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);
|
mp_obj_list_t *ret = MP_OBJ_TO_PTR(list_ref);
|
||||||
if (!MP_OBJ_IS_TYPE(list_ref, &mp_type_list) || ret->len < 2) {
|
if (!MP_OBJ_IS_TYPE(list_ref, &mp_type_list) || ret->len < 2) {
|
||||||
mp_raise_TypeError("invalid list_ref");
|
mp_raise_TypeError("invalid list_ref");
|
||||||
}
|
}
|
||||||
|
|
||||||
const mp_uint_t timeout = trezor_obj_get_uint(timeout_us);
|
const mp_uint_t timeout = trezor_obj_get_uint(timeout_ms);
|
||||||
const mp_uint_t deadline = mp_hal_ticks_us() + timeout;
|
const mp_uint_t deadline = mp_hal_ticks_ms() + timeout;
|
||||||
mp_obj_iter_buf_t iterbuf;
|
mp_obj_iter_buf_t iterbuf;
|
||||||
|
|
||||||
for (;;) {
|
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;
|
break;
|
||||||
} else {
|
} else {
|
||||||
MICROPY_EVENT_POLL_HOOK
|
MICROPY_EVENT_POLL_HOOK
|
||||||
|
@ -68,7 +68,7 @@ class HID:
|
|||||||
|
|
||||||
|
|
||||||
# extmod/modtrezorio/modtrezorio-poll.h
|
# 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
|
Wait until one of `ifaces` is ready to read or write (using masks
|
||||||
`list_ref`:
|
`list_ref`:
|
||||||
|
@ -191,7 +191,7 @@ class Bip39Keyboard(ui.Layout):
|
|||||||
|
|
||||||
async def handle_input(self) -> None:
|
async def handle_input(self) -> None:
|
||||||
touch = loop.wait(io.TOUCH)
|
touch = loop.wait(io.TOUCH)
|
||||||
timeout = loop.sleep(1000 * 1000 * 1)
|
timeout = loop.sleep(1000)
|
||||||
race_touch = loop.race(touch)
|
race_touch = loop.race(touch)
|
||||||
race_timeout = loop.race(touch, timeout)
|
race_timeout = loop.race(touch, timeout)
|
||||||
|
|
||||||
|
@ -201,7 +201,7 @@ class Slip39Keyboard(ui.Layout):
|
|||||||
|
|
||||||
async def handle_input(self) -> None:
|
async def handle_input(self) -> None:
|
||||||
touch = loop.wait(io.TOUCH)
|
touch = loop.wait(io.TOUCH)
|
||||||
timeout = loop.sleep(1000 * 1000 * 1)
|
timeout = loop.sleep(1000)
|
||||||
race_touch = loop.race(touch)
|
race_touch = loop.race(touch)
|
||||||
race_timeout = loop.race(touch, timeout)
|
race_timeout = loop.race(touch, timeout)
|
||||||
|
|
||||||
|
@ -429,7 +429,7 @@ async def read_cmd(iface: io.HID) -> Optional[Cmd]:
|
|||||||
data = data[:bcnt]
|
data = data[:bcnt]
|
||||||
|
|
||||||
while datalen < 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 not isinstance(buf, bytes):
|
||||||
if __debug__:
|
if __debug__:
|
||||||
log.warning(__name__, "_ERR_MSG_TIMEOUT")
|
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:
|
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 * 1000))
|
ret = await loop.race(write, loop.sleep(_CTAP_HID_TIMEOUT_MS))
|
||||||
if ret is not None:
|
if ret is not None:
|
||||||
raise TimeoutError
|
raise TimeoutError
|
||||||
|
|
||||||
@ -1060,7 +1060,7 @@ class DialogManager:
|
|||||||
if self.state.keepalive_status() != _KEEPALIVE_STATUS_NONE:
|
if self.state.keepalive_status() != _KEEPALIVE_STATUS_NONE:
|
||||||
cmd = cmd_keepalive(self.state.cid, self.state.keepalive_status())
|
cmd = cmd_keepalive(self.state.cid, self.state.keepalive_status())
|
||||||
await send_cmd(cmd, self.iface)
|
await send_cmd(cmd, self.iface)
|
||||||
await loop.sleep(_KEEPALIVE_INTERVAL_MS * 1000)
|
await loop.sleep(_KEEPALIVE_INTERVAL_MS)
|
||||||
finally:
|
finally:
|
||||||
self.keepalive = None
|
self.keepalive = None
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ def schedule(
|
|||||||
if reschedule:
|
if reschedule:
|
||||||
_queue.discard(task)
|
_queue.discard(task)
|
||||||
if deadline is None:
|
if deadline is None:
|
||||||
deadline = utime.ticks_us()
|
deadline = utime.ticks_ms()
|
||||||
if finalizer is not None:
|
if finalizer is not None:
|
||||||
_finalizers[id(task)] = finalizer
|
_finalizers[id(task)] = finalizer
|
||||||
_queue.push(deadline, task, value)
|
_queue.push(deadline, task, value)
|
||||||
@ -123,9 +123,9 @@ def run() -> None:
|
|||||||
while _queue or _paused:
|
while _queue or _paused:
|
||||||
# compute the maximum amount of time we can wait for a message
|
# compute the maximum amount of time we can wait for a message
|
||||||
if _queue:
|
if _queue:
|
||||||
delay = utime.ticks_diff(_queue.peektime(), utime.ticks_us())
|
delay = utime.ticks_diff(_queue.peektime(), utime.ticks_ms())
|
||||||
else:
|
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__:
|
if __debug__:
|
||||||
# process synthetic events
|
# process synthetic events
|
||||||
@ -227,22 +227,21 @@ SLEEP_FOREVER = Syscall()
|
|||||||
|
|
||||||
|
|
||||||
class sleep(Syscall):
|
class sleep(Syscall):
|
||||||
"""
|
"""Pause current task and resume it after given delay.
|
||||||
Pause current task and resume it after given delay. Although the delay is
|
|
||||||
given in microseconds, sub-millisecond precision is not guaranteed. Result
|
Result value is the calculated deadline.
|
||||||
value is the calculated deadline.
|
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
>>> planned = await loop.sleep(1000 * 1000) # sleep for 1ms
|
>>> planned = await loop.sleep(1000) # sleep for 1s
|
||||||
>>> print('missed by %d us', utime.ticks_diff(utime.ticks_us(), planned))
|
>>> print('missed by %d ms', utime.ticks_diff(utime.ticks_ms(), planned))
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, delay_us: int) -> None:
|
def __init__(self, delay_ms: int) -> None:
|
||||||
self.delay_us = delay_us
|
self.delay_ms = delay_ms
|
||||||
|
|
||||||
def handle(self, task: Task) -> None:
|
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)
|
schedule(task, deadline, deadline)
|
||||||
|
|
||||||
|
|
||||||
|
@ -85,8 +85,8 @@ def pulse(period: int, offset: int = 0) -> float:
|
|||||||
|
|
||||||
|
|
||||||
async def _alert(count: int) -> None:
|
async def _alert(count: int) -> None:
|
||||||
short_sleep = loop.sleep(20000)
|
short_sleep = loop.sleep(20)
|
||||||
long_sleep = loop.sleep(80000)
|
long_sleep = loop.sleep(80)
|
||||||
for i in range(count * 2):
|
for i in range(count * 2):
|
||||||
if i % 2 == 0:
|
if i % 2 == 0:
|
||||||
display.backlight(style.BACKLIGHT_MAX)
|
display.backlight(style.BACKLIGHT_MAX)
|
||||||
@ -223,9 +223,9 @@ RENDER = const(-255)
|
|||||||
# Event dispatched when components should mark themselves for re-painting.
|
# Event dispatched when components should mark themselves for re-painting.
|
||||||
REPAINT = const(-256)
|
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.
|
# the render calls.
|
||||||
_RENDER_DELAY_US = const(10000) # 10 msec
|
_RENDER_DELAY_MS = const(10)
|
||||||
|
|
||||||
|
|
||||||
class Component:
|
class Component:
|
||||||
@ -306,7 +306,7 @@ class Layout(Component):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
BACKLIGHT_LEVEL = style.BACKLIGHT_NORMAL
|
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:
|
async def __iter__(self) -> ResultValue:
|
||||||
"""
|
"""
|
||||||
|
@ -208,7 +208,7 @@ class PassphraseKeyboard(ui.Layout):
|
|||||||
|
|
||||||
async def handle_input(self) -> None:
|
async def handle_input(self) -> None:
|
||||||
touch = loop.wait(io.TOUCH)
|
touch = loop.wait(io.TOUCH)
|
||||||
timeout = loop.sleep(1000 * 1000 * 1)
|
timeout = loop.sleep(1000)
|
||||||
race_touch = loop.race(touch)
|
race_touch = loop.race(touch)
|
||||||
race_timeout = loop.race(touch, timeout)
|
race_timeout = loop.race(touch, timeout)
|
||||||
|
|
||||||
|
@ -19,5 +19,5 @@ class Popup(ui.Layout):
|
|||||||
return self.handle_input(), self.handle_rendering(), self.handle_timeout()
|
return self.handle_input(), self.handle_rendering(), self.handle_timeout()
|
||||||
|
|
||||||
def handle_timeout(self) -> loop.Task: # type: ignore
|
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)
|
raise ui.Result(None)
|
||||||
|
@ -181,7 +181,7 @@ class IdleTimer:
|
|||||||
"""
|
"""
|
||||||
for callback, task in self.tasks.items():
|
for callback, task in self.tasks.items():
|
||||||
timeout_us = self.timeouts[callback]
|
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)
|
loop.schedule(task, None, deadline, reschedule=True)
|
||||||
|
|
||||||
def set(self, timeout_ms: int, callback: IdleCallback) -> None:
|
def set(self, timeout_ms: int, callback: IdleCallback) -> None:
|
||||||
@ -207,7 +207,7 @@ class IdleTimer:
|
|||||||
if callback in self.tasks:
|
if callback in self.tasks:
|
||||||
loop.close(self.tasks[callback])
|
loop.close(self.tasks[callback])
|
||||||
|
|
||||||
self.timeouts[callback] = timeout_ms * 1000
|
self.timeouts[callback] = timeout_ms
|
||||||
self.tasks[callback] = self._timeout_task(callback)
|
self.tasks[callback] = self._timeout_task(callback)
|
||||||
self.touch()
|
self.touch()
|
||||||
|
|
||||||
|
@ -157,9 +157,9 @@ syscalls is the preferred method of writing code.
|
|||||||
|
|
||||||
The following syscalls and constructs are available:
|
The following syscalls and constructs are available:
|
||||||
|
|
||||||
**`loop.sleep(delay_us: int)`**: Suspend execution until the given delay (in
|
**`loop.sleep(delay_ms: int)`**: Suspend execution until the given delay (in
|
||||||
microseconds) elapses. Sub-millisecond precision is not guaranteed, however.
|
milliseconds) elapses. Return value is the planned deadline in milliseconds since system
|
||||||
Return value is the planned deadline in microseconds since system start.
|
start.
|
||||||
|
|
||||||
Calling `await loop.sleep(0)` yields execution to other tasks, and schedules the current
|
Calling `await loop.sleep(0)` yields execution to other tasks, and schedules the current
|
||||||
task for the next tick.
|
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`:
|
It is possible to specify wait timeout for `loop.wait` by using `loop.race`:
|
||||||
```python
|
```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
|
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
|
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