From 84a98e6ecec4109f7a727df65a434cf194d0ce8e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 14 Apr 2026 21:22:02 -1000 Subject: [PATCH] internal --- esphome/core/application.h | 2 +- esphome/core/component.h | 3 ++- esphome/core/millis_internal.h | 42 ++++++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 esphome/core/millis_internal.h diff --git a/esphome/core/application.h b/esphome/core/application.h index 60087d527d..1c2bc523cf 100644 --- a/esphome/core/application.h +++ b/esphome/core/application.h @@ -878,7 +878,7 @@ inline void ESPHOME_ALWAYS_INLINE Application::before_loop_tasks_(uint32_t loop_ inline void ESPHOME_ALWAYS_INLINE Application::loop() { // Get the initial loop time at the start - uint32_t last_op_end_time = millis(); + uint32_t last_op_end_time = MillisInternal::get(); this->before_loop_tasks_(last_op_end_time); diff --git a/esphome/core/component.h b/esphome/core/component.h index 3307c5ae76..30db0514c1 100644 --- a/esphome/core/component.h +++ b/esphome/core/component.h @@ -9,6 +9,7 @@ #include "esphome/core/hal.h" #include "esphome/core/helpers.h" #include "esphome/core/log.h" +#include "esphome/core/millis_internal.h" #include "esphome/core/optional.h" // Forward declarations for friend access from codegen-generated setup() @@ -648,7 +649,7 @@ class WarnIfComponentBlockingGuard { #ifdef USE_RUNTIME_STATS this->component_->runtime_stats_.record_time(micros() - this->started_us_); #endif - uint32_t curr_time = millis(); + uint32_t curr_time = MillisInternal::get(); #ifndef USE_BENCHMARK // Fast path: compare against constant threshold in ms (computed at compile time from centiseconds) static constexpr uint32_t WARN_IF_BLOCKING_OVER_MS = static_cast(WARN_IF_BLOCKING_OVER_CS) * 10U; diff --git a/esphome/core/millis_internal.h b/esphome/core/millis_internal.h new file mode 100644 index 0000000000..6b73476680 --- /dev/null +++ b/esphome/core/millis_internal.h @@ -0,0 +1,42 @@ +#pragma once + +#include "esphome/core/hal.h" +#include "esphome/core/helpers.h" + +#if defined(USE_ESP32) +#include +#include +#include +#endif + +namespace esphome { + +// Friend-gated accessor for a fast millis() variant intended only for +// known task-context callers on the main loop hot path (Application::loop() +// and WarnIfComponentBlockingGuard::finish()). It skips the ISR-context +// dispatch that the public esphome::millis() pays on ESP32. +// +// MUST NOT be called from ISR context: on ESP32 it calls the non-FromISR +// FreeRTOS API directly, which is undefined behavior in ISR context. +// +// Adding new callers requires adding a friend declaration here — that +// is the review point. Do not relax the access (e.g. by making get() +// public) without considering the ISR-safety contract. +// +// Other platforms currently delegate to the public millis(); the friend +// gate still enforces the intent so platform-specific fast paths can be +// added later without changing call sites. +class MillisInternal { + private: + static ESPHOME_ALWAYS_INLINE uint32_t get() { +#if defined(USE_ESP32) && CONFIG_FREERTOS_HZ == 1000 + return xTaskGetTickCount(); +#else + return millis(); +#endif + } + friend class Application; + friend class WarnIfComponentBlockingGuard; +}; + +} // namespace esphome