mirror of
https://github.com/esphome/esphome.git
synced 2026-05-25 18:47:56 +08:00
[scheduler] Feed watchdog after each scheduled item, drop top-of-loop feed
The main loop used to feed the watchdog unconditionally right after Scheduler::call() returned, regardless of whether the scheduler had any actual work to do. On an idle device this meant every outer loop iteration paid the inline rate-limit check (load + sub + branch) for no benefit. Move the feed into Scheduler::execute_item_() so it fires only after a scheduled callback actually runs, and covers both the main heap path and the defer queue path (both go through execute_item_). This also bounds the max feed gap during a burst of back-to-back scheduled items by max(item_runtime) instead of sum(item_runtime). The top-of-loop feed in Application::before_loop_tasks_() is now unnecessary — when Scheduler::call does no work, the only elapsed time is the sleep wake plus a few instructions, and when it does have work, it fed the wdt as it went.
This commit is contained in:
@@ -829,12 +829,13 @@ inline void ESPHOME_ALWAYS_INLINE Application::before_loop_tasks_(uint32_t loop_
|
||||
this->drain_wake_notifications_();
|
||||
#endif
|
||||
|
||||
// Process scheduled tasks
|
||||
// Process scheduled tasks. Scheduler::call now feeds the watchdog itself
|
||||
// after each scheduled item that actually runs, so we no longer need an
|
||||
// unconditional feed here — when Scheduler::call has no work to do, the
|
||||
// only elapsed time is a sleep wake + a few instructions, and when it does
|
||||
// have work, it fed the wdt as it went.
|
||||
this->scheduler.call(loop_start_time);
|
||||
|
||||
// Feed the watchdog timer
|
||||
this->feed_wdt(loop_start_time);
|
||||
|
||||
// Process any pending enable_loop requests from ISRs
|
||||
// This must be done before marking in_loop_ = true to avoid race conditions
|
||||
if (this->has_pending_enable_loop_requests_) {
|
||||
|
||||
@@ -739,7 +739,13 @@ uint32_t HOT Scheduler::execute_item_(SchedulerItem *item, uint32_t now) {
|
||||
App.set_current_component(item->component);
|
||||
WarnIfComponentBlockingGuard guard{item->component, now};
|
||||
item->callback();
|
||||
return guard.finish();
|
||||
uint32_t end = guard.finish();
|
||||
// Feed the watchdog after each scheduled item (both main heap and defer
|
||||
// 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);
|
||||
return end;
|
||||
}
|
||||
|
||||
// Common implementation for cancel operations - handles locking
|
||||
|
||||
Reference in New Issue
Block a user