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.
pull/971/head
matejcik 4 years ago committed by matejcik
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…
Cancel
Save