From 12d227dee850de129972659a6719a4f72a47f0e8 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 11 Apr 2026 23:22:12 -1000 Subject: [PATCH] [rp2040] Bound the while loop in millis() accumulator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Same approach as ESP8266: split into common path (while loop, ≤10 iterations) and rare path (constant-time /1000 multiply for gaps >10 ms). RP2040 has no ISR callers so no interrupt guard needed, but the bounded loop avoids worst-case latency if millis() is called from a context that was blocked for a long time. --- esphome/components/rp2040/core.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/esphome/components/rp2040/core.cpp b/esphome/components/rp2040/core.cpp index 74c7376018..032669e129 100644 --- a/esphome/components/rp2040/core.cpp +++ b/esphome/components/rp2040/core.cpp @@ -26,7 +26,8 @@ void HOT yield() { ::yield(); } // s_last_us and ::micros() start at 0, so no special initialization needed. // // Also installed as __wrap_millis (via -Wl,--wrap=millis) so Arduino library -// code calling ::millis() directly gets the fast version. +// code calling ::millis() directly gets the fast version. No interrupt guard +// needed — no ESPHome ISR calls millis() on RP2040. uint32_t HOT millis() { static struct { uint32_t cache; @@ -37,9 +38,18 @@ uint32_t HOT millis() { uint32_t delta = now_us - state.last_us; state.last_us = now_us; state.remainder += delta; - while (state.remainder >= 1000) { - state.cache++; - state.remainder -= 1000; + if (state.remainder >= 10000) { + // Rare path: large gap (>10 ms — boot, long block). Constant-time + // multiply-by-reciprocal via /1000. + uint32_t ms = state.remainder / 1000; + state.cache += ms; + state.remainder -= ms * 1000; + } else { + // Common path: small gap. Loop runs at most 10 times. + while (state.remainder >= 1000) { + state.cache++; + state.remainder -= 1000; + } } return state.cache; }