1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-01-09 15:00:58 +00:00

fix(core): retry creation of homescreen layout

For reasons unknown, a previous homescreen layout can sometimes survive
a GC cycle in main's unimport loop. Two homescreen layouts can't exist
simultaneously, so creating a new one would fail.

It _seems_ that after restarting the session, the homescreen object
still exists but is not reachable anymore, so a second GC cycle properly
disposes of it.

So what we do is simply catch the possible MemoryError, invoke GC
explicitly, and try again.
This commit is contained in:
matejcik 2025-01-02 14:23:52 +01:00 committed by matejcik
parent e424fd8d3b
commit e0b4cab2db

View File

@ -6,10 +6,37 @@ from storage.cache_common import APP_COMMON_BUSY_DEADLINE_MS
from trezor import TR, ui
if TYPE_CHECKING:
from typing import Any, Iterator
from typing import Any, Callable, Iterator, ParamSpec, TypeVar
from trezor import loop
P = ParamSpec("P")
R = TypeVar("R")
def _retry_with_gc(layout: Callable[P, R], *args: P.args, **kwargs: P.kwargs) -> R:
"""Retry creating the layout after garbage collection.
For reasons unknown, a previous homescreen layout may survive an unimport, and still
exists in the GC arena. At the time a new one is instantiated, the old one still
holds a lock on the JPEG buffer, and creating a new layout will fail with a
MemoryError.
It seems that the previous layout's survival is a glitch, and at a later time it is
still in memory but not held anymore. We assume that triggering a GC cycle will
correctly throw it away, and we will be able to create the new layout.
We only try this once because if it didn't help, the above assumption is wrong, so
no point in trying again.
"""
try:
return layout(*args, **kwargs)
except MemoryError:
import gc
gc.collect()
return layout(*args, **kwargs)
class HomescreenBase(ui.Layout):
RENDER_INDICATOR: object | None = None
@ -56,7 +83,8 @@ class Homescreen(HomescreenBase):
level = 0
super().__init__(
layout=trezorui_api.show_homescreen(
layout=_retry_with_gc(
trezorui_api.show_homescreen,
label=label,
notification=notification,
notification_level=level,
@ -96,7 +124,8 @@ class Lockscreen(HomescreenBase):
not bootscreen and storage_cache.homescreen_shown is self.RENDER_INDICATOR
)
super().__init__(
layout=trezorui_api.show_lockscreen(
layout=_retry_with_gc(
trezorui_api.show_lockscreen,
label=label,
bootscreen=bootscreen,
skip_first_paint=skip,
@ -117,7 +146,8 @@ class Busyscreen(HomescreenBase):
def __init__(self, delay_ms: int) -> None:
super().__init__(
layout=trezorui_api.show_progress_coinjoin(
layout=_retry_with_gc(
trezorui_api.show_progress_coinjoin,
title=TR.coinjoin__waiting_for_others,
indeterminate=True,
time_ms=delay_ms,