From 3629ffef2c448d2fc034398dd59edf0776a1d4ec Mon Sep 17 00:00:00 2001 From: Adolfo Reyna Date: Sat, 11 Jul 2026 21:32:11 -0400 Subject: [PATCH] fix(displayidle): avoid LVGL->MCP lock inversion that could freeze RLCD Read MCP override state outside LVGL lock in tick(). Previously: LVGL lock -> MCP mutex (tick) vs MCP mutex -> LVGL lock (video task startMcpScreensaver) = deadlock risk observed on RLCD when text tool called. Now: MCP state read first, then LVGL lock. Preserves <1s flash guard (isMcpActive). Board: waveshare-esp32-s3-rlcd 0x2b78b0 32%% free, flashed to /dev/cu.usbmodem101 Co-Authored-By: internal-model --- .../Source/service/displayidle/DisplayIdle.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/Tactility/Source/service/displayidle/DisplayIdle.cpp b/Tactility/Source/service/displayidle/DisplayIdle.cpp index 0dcf7f7c..c5f6afb6 100644 --- a/Tactility/Source/service/displayidle/DisplayIdle.cpp +++ b/Tactility/Source/service/displayidle/DisplayIdle.cpp @@ -147,6 +147,16 @@ void DisplayIdleService::updateScreensaver() { } void DisplayIdleService::tick() { + // Check if MCP override is active — must not be auto-stopped by idle logic + // READ OUTSIDE LVGL lock to avoid lock inversion: + // MCP video task locks mutex -> LVGL, we must NOT do LVGL -> mutex + bool isMcpActive = false; + { + auto& st = mcp::getState(); + std::lock_guard lk(st.mutex); + isMcpActive = (st.drawArea != nullptr) || st.overrideActive; + } + if (!lvgl::lock(100)) { return; } @@ -163,14 +173,6 @@ void DisplayIdleService::tick() { uint32_t inactive_ms = 0; inactive_ms = lv_display_get_inactive_time(nullptr); - // Check if MCP override is active — it must not be auto-stopped by idle logic - bool isMcpActive = false; - { - auto& st = mcp::getState(); - std::lock_guard lk(st.mutex); - isMcpActive = (st.drawArea != nullptr) || st.overrideActive; - } - // Only update if not stopping (prevents lag on touch) — skip for MCP (no animation) if (displayDimmed && screensaverOverlay && !stopScreensaverRequested.load(std::memory_order_acquire) && !isMcpActive) { // Check if screensaver should auto-off after 5 minutes