From 12ad6637232d667cc652265abbc7af2baf711956 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 22 Apr 2026 07:54:11 +0200 Subject: [PATCH] Address review: NMI note + static_assert hard-encoding remainder+delta overflow invariant --- esphome/components/esp8266/core.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) 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