mirror of
https://github.com/esphome/esphome.git
synced 2026-09-03 19:46:02 +00:00
[core] Scheduler::next_schedule_in: return 0 when defer queue is non-empty
On multi-thread platforms (ESP32/LibreTiny/host), defer() items go directly into a separate defer_queue_ instead of the main items_ heap (scheduler.cpp:200). next_schedule_in previously only looked at items_[0], so a pending defer posted from a background task was invisible to the loop's sleep-duration calculation: if items_[0] fired seconds away, the main loop would sleep that long on ulTaskNotifyTake while the defer sat unexecuted. In practice this rarely bit anyone because concurrent socket activity usually woke the loop via the lwip fast-select path, but a defer with no accompanying network traffic could stall for seconds. Check defer_empty_() (cheap atomic/volatile load, scheduler.h:594) before the items_ check and return 0 if pending. process_defer_queue_ runs at the top of every scheduler.call(), so "defer pending" == "run immediately." Single-threaded platforms funnel defers into to_add_ like any other timeout, so the fix is gated on !ESPHOME_THREAD_SINGLE.
This commit is contained in:
@@ -417,6 +417,14 @@ optional<uint32_t> HOT Scheduler::next_schedule_in(uint32_t now) {
|
||||
// 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.
|
||||
|
||||
#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;
|
||||
#endif
|
||||
|
||||
// If no items, return empty optional
|
||||
if (!this->cleanup_())
|
||||
return {};
|
||||
|
||||
Reference in New Issue
Block a user