mirror of
https://github.com/esphome/esphome.git
synced 2026-05-24 18:06:27 +08:00
[core] Split feed_wdt into hot and cold entries
Separate Application::feed_wdt() into two entry points so the hot path callers stop paying for the time==0 check they never trigger: - feed_wdt_with_time(time): inline, hot path. Rate-limit check in 3 Xtensa instructions (load + sub + branch). [[unlikely]] tells the compiler the slow branch is rare so the common path stays fall-through. - feed_wdt(): cold, out of line. Fetches millis() and forwards through the same rate limit. Used by setup loops, upload helpers, yield(), and any other non-hot caller. feed_wdt_slow_() is now pure worker code — 11 bytes. It just calls arch_feed_wdt(), updates last_wdt_feed_, and runs the status LED re-dispatch. Both entries have already confirmed the rate limit was exceeded before calling. Hot call sites updated: - Application::loop() per-component feed - Scheduler::execute_item_() after each scheduled item runs - Application::teardown_components() inner loop (already has 'now')
This commit is contained in:
@@ -196,21 +196,26 @@ void Application::process_dump_config_() {
|
||||
this->dump_config_at_++;
|
||||
}
|
||||
|
||||
void HOT Application::feed_wdt_slow_(uint32_t time) {
|
||||
// Use provided time if available, otherwise get current time
|
||||
uint32_t now = time ? time : millis();
|
||||
// The inline wrapper already performs this check when time != 0;
|
||||
// repeat it here for the time == 0 entry and as a safety net.
|
||||
void Application::feed_wdt() {
|
||||
// Cold entry: callers without a millis() timestamp in hand. Fetches the
|
||||
// time and takes the same rate-limit path as feed_wdt_with_time().
|
||||
uint32_t now = millis();
|
||||
if (now - this->last_wdt_feed_ > WDT_FEED_INTERVAL_MS) {
|
||||
arch_feed_wdt();
|
||||
this->last_wdt_feed_ = now;
|
||||
#ifdef USE_STATUS_LED
|
||||
if (status_led::global_status_led != nullptr) {
|
||||
status_led::global_status_led->call();
|
||||
}
|
||||
#endif
|
||||
this->feed_wdt_slow_(now);
|
||||
}
|
||||
}
|
||||
|
||||
void HOT Application::feed_wdt_slow_(uint32_t time) {
|
||||
// Callers (both feed_wdt() and feed_wdt_with_time()) have already
|
||||
// confirmed the 3 ms rate limit was exceeded.
|
||||
arch_feed_wdt();
|
||||
this->last_wdt_feed_ = time;
|
||||
#ifdef USE_STATUS_LED
|
||||
if (status_led::global_status_led != nullptr) {
|
||||
status_led::global_status_led->call();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
void Application::reboot() {
|
||||
ESP_LOGI(TAG, "Forcing a reboot");
|
||||
for (auto &component : std::ranges::reverse_view(this->components_)) {
|
||||
@@ -299,7 +304,7 @@ void Application::teardown_components(uint32_t timeout_ms) {
|
||||
|
||||
while (pending_count > 0 && (now - start_time) < timeout_ms) {
|
||||
// Feed watchdog during teardown to prevent triggering
|
||||
this->feed_wdt(now);
|
||||
this->feed_wdt_with_time(now);
|
||||
|
||||
// Process components and compact the array, keeping only those still pending
|
||||
size_t still_pending = 0;
|
||||
|
||||
+11
-10
@@ -390,15 +390,16 @@ class Application {
|
||||
/// watchdog timeout (seconds) has orders of magnitude of safety margin.
|
||||
static constexpr uint32_t WDT_FEED_INTERVAL_MS = 3;
|
||||
|
||||
/// Feed the task watchdog. Hot-path inline rate-limit check: callers that
|
||||
/// already have a timestamp in hand pay only a load + sub + branch on the
|
||||
/// common (no-op) path. The actual arch feed + status LED update live in
|
||||
/// feed_wdt_slow_ to keep this small enough to inline freely.
|
||||
///
|
||||
/// Pass time==0 to request millis() be read for you (low-frequency callers
|
||||
/// only — always takes the slow path).
|
||||
void ESPHOME_ALWAYS_INLINE feed_wdt(uint32_t time = 0) {
|
||||
if (time == 0 || static_cast<uint32_t>(time - this->last_wdt_feed_) > WDT_FEED_INTERVAL_MS) {
|
||||
/// Feed the task watchdog. Cold entry — callers without a millis()
|
||||
/// timestamp in hand. Out of line to keep call sites tiny.
|
||||
void feed_wdt();
|
||||
|
||||
/// Feed the task watchdog, hot entry. Callers that already have a
|
||||
/// millis() timestamp pay only a load + sub + branch on the common
|
||||
/// (no-op) path. The actual arch feed + status LED update live in
|
||||
/// feed_wdt_slow_.
|
||||
void ESPHOME_ALWAYS_INLINE feed_wdt_with_time(uint32_t time) {
|
||||
if (static_cast<uint32_t>(time - this->last_wdt_feed_) > WDT_FEED_INTERVAL_MS) [[unlikely]] {
|
||||
this->feed_wdt_slow_(time);
|
||||
}
|
||||
}
|
||||
@@ -882,7 +883,7 @@ inline void ESPHOME_ALWAYS_INLINE Application::loop() {
|
||||
}
|
||||
new_app_state |= component->get_component_state();
|
||||
this->app_state_ |= new_app_state;
|
||||
this->feed_wdt(last_op_end_time);
|
||||
this->feed_wdt_with_time(last_op_end_time);
|
||||
}
|
||||
|
||||
this->after_loop_tasks_();
|
||||
|
||||
@@ -744,7 +744,7 @@ uint32_t HOT Scheduler::execute_item_(SchedulerItem *item, uint32_t now) {
|
||||
// queue paths go through here). A run of back-to-back callbacks cannot
|
||||
// starve the wdt. The inline fast path is a load + sub + branch — nearly
|
||||
// free when the 3 ms rate limit hasn't elapsed.
|
||||
App.feed_wdt(end);
|
||||
App.feed_wdt_with_time(end);
|
||||
return end;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user