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.
The static class members in Millis64Impl's ESPHOME_THREAD_SINGLE branch
kept the trailing-underscore convention used for instance members, but
the project's clang-tidy config (readability-identifier-naming.
ClassMemberCase = lower_case) wants static class members without the
suffix. This violation was latent because defines.h previously
hardcoded MULTI_ATOMICS, so the SINGLE branch was never analyzed;
now that defines.h picks the model per platform, the ESP8266 tidy env
analyzes this branch.
Drop the trailing _ from last_millis / millis_major; their types and
initial values are unchanged.
defines.h previously hardcoded ESPHOME_THREAD_MULTI_ATOMICS regardless
of the active USE_<platform>, so the clang-tidy envs ended up
compiling e.g. wake_esp8266.cpp with USE_ESP8266 + MULTI_ATOMICS both
set — a combination that cannot occur in a real build and that
mismatched the extern in wake.h.
Pick the model that real codegen uses:
USE_ESP8266 / USE_RP2040 / USE_NRF52 → SINGLE
USE_BK72XX (ARMv5TE, no LDREX/STREX) → MULTI_NO_ATOMICS
everything else (ESP32, host, RTL87XX, LN882X) → MULTI_ATOMICS
With that, the single-only wake TUs (esp8266, rp2040, generic) can
drop the defensive conditional and just define g_wake_requested as
plain volatile again.
ESPHome's defines.h unconditionally #defines every feature flag for
static analysis, so clang-tidy sees ESPHOME_THREAD_MULTI_ATOMICS even
when building wake_esp8266.cpp / wake_rp2040.cpp / wake_generic.cpp.
In that mode wake.h picks the std::atomic<uint8_t> extern, and the
plain-volatile definitions in those TUs trip a redefinition error.
Guard each TU's g_wake_requested with the same
ESPHOME_THREAD_MULTI_ATOMICS check wake.h uses. Real builds still pick
volatile on every SINGLE platform.
wake.h's platform #include already ensures only one header contributes
code, but all five wake/*.cpp files were still being copied and compiled
(as empty TUs) on every target. Extend FILTER_SOURCE_FILES to map each
wake_<platform>.cpp to the exact set of PlatformFramework values that
need it — same pattern ring_buffer.cpp already uses.
Cover the four paths introduced by the wake split:
- default manifests (recursive_sources=False) skip subdirs entirely,
- opt-in manifests walk non-subpackage subdirs with /-joined paths,
- subdirs containing __init__.py are skipped to avoid double-counting
existing component subpackages, and __pycache__ is always skipped,
- FILTER_SOURCE_FILES entries accept /-joined subpaths.
Move each platform branch out of wake.h/wake.cpp into its own
esphome/core/wake/wake_<platform>.{h,cpp} pair (wake_freertos for
ESP32/LibreTiny, plus wake_esp8266, wake_rp2040, wake_host, and
wake_generic for the Zephyr/NRF52 fallback). wake.h becomes a thin
dispatcher that defines only the cross-platform wake_request_set/
wake_request_take helpers and then #include's the right platform
header based on USE_*. wake.cpp is removed; each platform's
g_wake_requested (and g_main_loop_woke where applicable) now lives
in the translation unit that actually uses it, guarded by the same
USE_* check.
Because esphome/core/ source discovery was previously flat, add an
opt-in recursive_sources flag to ComponentManifest and enable it only
on the core ("esphome") manifest. Subdirectories that are themselves
Python subpackages are still skipped so components (which already
register platform subpackages independently) are unaffected.