From fdb5c4c5ca3afa20812e9105bfc79bab59d6c85b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 12 Mar 2026 21:17:37 -1000 Subject: [PATCH 1/3] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- esphome/components/rp2040/helpers.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/rp2040/helpers.cpp b/esphome/components/rp2040/helpers.cpp index 3d71b7a5cc..a69b8da480 100644 --- a/esphome/components/rp2040/helpers.cpp +++ b/esphome/components/rp2040/helpers.cpp @@ -35,7 +35,7 @@ bool random_bytes(uint8_t *data, size_t len) { return true; } -// RP2040 Mutex is defined inline in helpers.h when ESPHOME_THREAD_SINGLE is set. +// RP2040 Mutex is defined inline in helpers.h for RP2040/ESP8266 builds. IRAM_ATTR InterruptLock::InterruptLock() { state_ = save_and_disable_interrupts(); } IRAM_ATTR InterruptLock::~InterruptLock() { restore_interrupts(state_); } From 4a3faeafdce530638d41224ee4593195cb89c80c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 12 Mar 2026 21:17:59 -1000 Subject: [PATCH 2/3] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- esphome/components/esp8266/helpers.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/esphome/components/esp8266/helpers.cpp b/esphome/components/esp8266/helpers.cpp index 4c849948fd..2153ee944b 100644 --- a/esphome/components/esp8266/helpers.cpp +++ b/esphome/components/esp8266/helpers.cpp @@ -12,7 +12,8 @@ namespace esphome { uint32_t random_uint32() { return os_random(); } bool random_bytes(uint8_t *data, size_t len) { return os_get_random(data, len) == 0; } -// ESP8266 Mutex is defined inline in helpers.h when ESPHOME_THREAD_SINGLE is set. +// ESP8266 Mutex is defined inline as a no-op in helpers.h when USE_ESP8266 (or USE_RP2040) is set, +// independent of the ESPHOME_THREAD_SINGLE thread model define. IRAM_ATTR InterruptLock::InterruptLock() { state_ = xt_rsil(15); } IRAM_ATTR InterruptLock::~InterruptLock() { xt_wsr_ps(state_); } From 12d98414335daf162238ed3afe91dc5a6f0b84b0 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 13 Mar 2026 22:23:30 -1000 Subject: [PATCH 3/3] [core] Inline Mutex on FreeRTOS platforms (ESP32, LibreTiny) Move FreeRTOS Mutex methods inline into helpers.h, eliminating duplicate out-of-line definitions in esp32/helpers.cpp and libretiny/helpers.cpp. Hot path impact (disassembled from ELF): | Platform | Before | After | Saved | |--------------------|----------|----------|---------| | ESP32 (Xtensa) | 1304 B | 1270 B | -34 B | | BK72xx (ARM M4) | 1400 B | 1396 B | -4 B | | RTL87xx (ARM M33) | 1248 B | 1246 B | -2 B | | ESP32-C3 (RISC-V) | 1498 B | 1494 B | -4 B | GCC generates ISRA clones that hoist the handle_ load into callers and use tail calls to xQueueSemaphoreTake/xQueueGenericSend. --- esphome/components/esp32/helpers.cpp | 6 ------ esphome/components/libretiny/helpers.cpp | 6 ------ esphome/core/helpers.h | 14 ++++++++++---- 3 files changed, 10 insertions(+), 16 deletions(-) diff --git a/esphome/components/esp32/helpers.cpp b/esphome/components/esp32/helpers.cpp index 051b7ce162..76f1c59c73 100644 --- a/esphome/components/esp32/helpers.cpp +++ b/esphome/components/esp32/helpers.cpp @@ -20,12 +20,6 @@ bool random_bytes(uint8_t *data, size_t len) { return true; } -Mutex::Mutex() { handle_ = xSemaphoreCreateMutex(); } -Mutex::~Mutex() {} -void Mutex::lock() { xSemaphoreTake(this->handle_, portMAX_DELAY); } -bool Mutex::try_lock() { return xSemaphoreTake(this->handle_, 0) == pdTRUE; } -void Mutex::unlock() { xSemaphoreGive(this->handle_); } - // only affects the executing core // so should not be used as a mutex lock, only to get accurate timing IRAM_ATTR InterruptLock::InterruptLock() { portDISABLE_INTERRUPTS(); } diff --git a/esphome/components/libretiny/helpers.cpp b/esphome/components/libretiny/helpers.cpp index 37ae0fb455..586ada5137 100644 --- a/esphome/components/libretiny/helpers.cpp +++ b/esphome/components/libretiny/helpers.cpp @@ -15,12 +15,6 @@ bool random_bytes(uint8_t *data, size_t len) { return true; } -Mutex::Mutex() { handle_ = xSemaphoreCreateMutex(); } -Mutex::~Mutex() {} -void Mutex::lock() { xSemaphoreTake(this->handle_, portMAX_DELAY); } -bool Mutex::try_lock() { return xSemaphoreTake(this->handle_, 0) == pdTRUE; } -void Mutex::unlock() { xSemaphoreGive(this->handle_); } - // only affects the executing core // so should not be used as a mutex lock, only to get accurate timing IRAM_ATTR InterruptLock::InterruptLock() { portDISABLE_INTERRUPTS(); } diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index a87ac92dbd..fad6fd06f9 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -1876,6 +1876,16 @@ class Mutex { void lock() {} bool try_lock() { return true; } void unlock() {} +#elif defined(USE_ESP32) || defined(USE_LIBRETINY) + // FreeRTOS platforms: inline to avoid out-of-line call overhead. + Mutex() { handle_ = xSemaphoreCreateMutex(); } + ~Mutex() = default; + void lock() { xSemaphoreTake(this->handle_, portMAX_DELAY); } + bool try_lock() { return xSemaphoreTake(this->handle_, 0) == pdTRUE; } + void unlock() { xSemaphoreGive(this->handle_); } + + private: + SemaphoreHandle_t handle_; #else Mutex(); ~Mutex(); @@ -1884,13 +1894,9 @@ class Mutex { void unlock(); private: -#if defined(USE_ESP32) || defined(USE_LIBRETINY) - SemaphoreHandle_t handle_; -#else // d-pointer to store private data on new platforms void *handle_; // NOLINT(clang-diagnostic-unused-private-field) #endif -#endif // single-threaded check }; /** Helper class that wraps a mutex with a RAII-style API.