From 790944a75e1ee8deee96457684cabdb2ffe67811 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Wed, 30 Sep 2026 20:53:42 +0000 Subject: [PATCH] tftSetup: don't claim the SPI bus after a timed-out take (#12025) ReentrantSpiLock::lock(uint32_t) recorded the calling thread as owner with depth 1 whether or not spiLock->lock(timeout) succeeded. After a timeout the thread's next lock() then took the reentrant path and skipped the real acquire, and the matching unlock() released a semaphore the thread never held, so device-ui and the radio could both reach the bus. Record ownership only when the take succeeds, and return false without touching owner or depth when it times out. Found while reviewing CodeRabbit's note on #12024. Claude-Session: https://claude.ai/code/session_01REkPVFh6kvG4AZJ5A8AtM9 Co-authored-by: Claude Opus 5.5 --- src/graphics/tftSetup.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/graphics/tftSetup.cpp b/src/graphics/tftSetup.cpp index 8e971cb60b..c4e9481508 100644 --- a/src/graphics/tftSetup.cpp +++ b/src/graphics/tftSetup.cpp @@ -280,10 +280,13 @@ class ReentrantSpiLock : public ISpiLock depth++; return true; } - bool result = spiLock->lock(timeout); + // A timed-out take holds nothing: claiming ownership anyway would let this thread's next + // lock() skip the real acquire, and its unlock() release a bus it never held. + if (!spiLock->lock(timeout)) + return false; owner = self; depth = 1; - return result; + return true; } void unlock(void) override