fix(startup): prevent main thread hangs and optimize resource loading
All checks were successful
Code check / Check code (push) Successful in 1m33s

- run start_sh initialization via QTimer.singleShot with timeout
- add timeout protection to load_theme_fonts()
- load Steam/EGS/PortProton games in parallel instead of sequential
- delay game loading until UI is fully initialized
- fix callback chaining to avoid blocking operations
- add proper timeout + error handling for all Steam/EGS network requests
- add timeouts for flatpak subprocess calls
- improve file I/O error handling to avoid UI freeze
- optimize theme font loading:
  - delay font loading via QTimer.singleShot
  - load fonts in batches of 10
  - reduce font load timeout to 3s
  - remove fonts only when switching themes

Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
This commit is contained in:
2025-11-28 12:00:00 +05:00
parent 77b025f580
commit 3abaccb1e0
8 changed files with 282 additions and 97 deletions

View File

@@ -254,6 +254,8 @@ class PortProtonAPI:
try:
mod_time = os.path.getmtime(cache_file)
if time.time() - mod_time < AUTOINSTALL_CACHE_DURATION:
# Add timeout protection for file operations
start_time = time.time()
with open(cache_file, "rb") as f:
data = orjson.loads(f.read())
# Check signature
@@ -261,6 +263,10 @@ class PortProtonAPI:
current_signature = self._compute_scripts_signature(
os.path.join(self.portproton_location or "", "data", "scripts", "pw_autoinstall")
)
# Check for timeout during signature computation
if time.time() - start_time > 3: # 3 second timeout
logger.warning("Cache loading took too long, skipping cache")
return None
if cached_signature != current_signature:
logger.info("Scripts signature mismatch; invalidating cache")
return None
@@ -287,21 +293,26 @@ class PortProtonAPI:
def start_autoinstall_games_load(self, callback: Callable[[list[tuple]], None]) -> QThread | None:
"""Start loading auto-install games in a background thread. Returns the thread for management."""
# Check cache first (sync, fast)
cached_games = self._load_autoinstall_cache()
if cached_games is not None:
# Emit via callback immediately if cached
QThread.msleep(0) # Yield to Qt event loop
callback(cached_games)
return None # No thread needed
# No cache: Start background thread
class AutoinstallWorker(QThread):
finished = Signal(list)
api: "PortProtonAPI"
portproton_location: str | None
def run(self):
import time
# Check cache in this background thread, not in main thread
start_time = time.time()
cached_games = self.api._load_autoinstall_cache()
# If cache loading took too long (>2 seconds), skip cache and load directly
if time.time() - start_time > 2:
logger.warning("Cache loading took too long, proceeding without cache")
cached_games = None
if cached_games is not None:
self.finished.emit(cached_games)
return
# No cache: Load games from scratch
games = []
auto_dir = os.path.join(
self.portproton_location or "", "data", "scripts", "pw_autoinstall"