From c30aa4aad4c34df7226d4dc5002e101aeca000c5 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 27 Apr 2026 04:55:43 -0500 Subject: [PATCH] [core] wakeable_delay: yield on already-woken fast path (ESP8266, RP2040) When `g_main_loop_woke` is already set on entry to `wakeable_delay()`, both the ESP8266 and RP2040 paths consume the flag and return without yielding. That's safe in isolation, but if a caller loops on `wakeable_delay()` (e.g. `LWIPRawImpl::wait_for_data_()` waiting for SO_RCVTIMEO), and ISR sources (GPIO, timer, WiFi RX on ESP8266; alarm / async on RP2040) keep re-setting the flag between iterations, every iteration takes the fast path and the loop never yields. That can starve the SDK / async context, blocking actual TCP delivery to our socket and causing OTA reads to time out on busy devices even though there is data in flight. The PR that introduced `wait_for_data_()` (#14675) relied on `wakeable_delay()` to yield on every iteration; this restores that property on the fast path. Adds `delay(0)` on ESP8266 and `yield()` on RP2040 to the fast path, matching the yield behaviour those platforms already use for the `ms == 0` poll case. --- esphome/core/wake/wake_esp8266.h | 4 ++++ esphome/core/wake/wake_rp2040.cpp | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/esphome/core/wake/wake_esp8266.h b/esphome/core/wake/wake_esp8266.h index 80cd61035b..7eaaae5293 100644 --- a/esphome/core/wake/wake_esp8266.h +++ b/esphome/core/wake/wake_esp8266.h @@ -36,6 +36,10 @@ inline void ESPHOME_ALWAYS_INLINE wakeable_delay(uint32_t ms) { } if (g_main_loop_woke) { g_main_loop_woke = false; + // Yield even on the already-woken fast path so callers in tight loops + // (e.g. lwIP raw TCP wait_for_data_) make forward progress when ISRs + // keep re-setting g_main_loop_woke between iterations. + delay(0); return; } esp_delay(ms, []() { return !g_main_loop_woke; }); diff --git a/esphome/core/wake/wake_rp2040.cpp b/esphome/core/wake/wake_rp2040.cpp index b18248dbd2..bdcbb1ad00 100644 --- a/esphome/core/wake/wake_rp2040.cpp +++ b/esphome/core/wake/wake_rp2040.cpp @@ -36,6 +36,10 @@ void wakeable_delay(uint32_t ms) { } if (g_main_loop_woke) { g_main_loop_woke = false; + // Yield even on the already-woken fast path so callers in tight loops + // (e.g. lwIP raw TCP wait_for_data_) make forward progress when async + // wakes keep re-setting g_main_loop_woke between iterations. + yield(); return; } s_delay_expired = false;