diff --git a/esphome/components/esp8266/core.cpp b/esphome/components/esp8266/core.cpp index b25e9f7ef8..62907ac6aa 100644 --- a/esphome/components/esp8266/core.cpp +++ b/esphome/components/esp8266/core.cpp @@ -23,7 +23,8 @@ void HOT yield() { ::yield(); } // Wiegand, ZyAura) also get the fast version. xt_rsil(15) guards the static // state against ISR re-entry; the critical section is bounded (≤9 while-loop // iterations, ~100 ns on the common path, or a constant-time /1000 ~2.5 μs on -// the rare path — well under WiFi's ~10 μs ISR latency budget). +// the rare path — well under WiFi's ~10 μs ISR latency budget). NMIs (level +// >15) are not masked, but the ESP8266 SDK's NMI handlers don't call millis(). // // Overflow: system_get_time() wraps every ~71.6 min; unsigned now_us - last_us // handles one wrap. Both the ESPHome main loop (1+N millis() calls per @@ -31,6 +32,10 @@ void HOT yield() { ::yield(); } // loop) keep state.last_us fresh well inside the 71-minute window. static constexpr uint32_t MILLIS_RARE_PATH_THRESHOLD_US = 10000; static constexpr uint32_t US_PER_MS = 1000; +// Guarantees state.remainder + delta cannot wrap uint32_t: the threshold caps +// remainder on entry, and delta ≤ UINT32_MAX (one system_get_time() wrap). +static_assert(MILLIS_RARE_PATH_THRESHOLD_US < UINT32_MAX / 2, + "threshold must leave headroom so remainder + delta cannot overflow uint32_t"); uint32_t IRAM_ATTR HOT millis() { // Struct packs the three statics so the compiler loads one base address