[core] Scheduler: don't sleep while defer queue is non-empty (#15968)

This commit is contained in:
J. Nick Koston
2026-04-24 08:11:29 -05:00
committed by GitHub
parent ac7f0f0b74
commit 773b4d887b
+21 -2
View File
@@ -414,8 +414,27 @@ bool HOT Scheduler::cancel_retry(Component *component, uint32_t id) {
optional<uint32_t> HOT Scheduler::next_schedule_in(uint32_t now) {
// IMPORTANT: This method should only be called from the main thread (loop task).
// It performs cleanup and accesses items_[0] without holding a lock, which is only
// safe when called from the main thread. Other threads must not call this method.
// Accesses items_[0] and the fast-path empty checks without holding a lock, which
// is only safe from the main thread. Other threads must not call this method.
//
// Note: cleanup_() is only invoked on the items_[0] path below. The early returns
// skip it because they don't read items_[0], and Scheduler::call() at the top of
// every loop iteration already performs its own cleanup before the next sleep-
// duration computation happens.
#ifndef ESPHOME_THREAD_SINGLE
// defer() items live in a separate queue that is drained at the top of every
// loop tick via process_defer_queue_(). If any are pending, the next loop
// iteration has work to do right now -- don't let the caller sleep.
if (!this->defer_empty_())
return 0;
#else
// On single-threaded builds, defer() routes through set_timeout(..., 0) which
// stages in to_add_. process_to_add() runs at the top of every scheduler.call(),
// so anything in to_add_ becomes runnable on the next iteration; don't sleep.
if (!this->to_add_empty_())
return 0;
#endif
// If no items, return empty optional
if (!this->cleanup_())