From 0fc3f0e162546132b59d394ed41614b7f0bd29e1 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 4 Jul 2025 10:57:39 -0500 Subject: [PATCH 1/2] guard esp8266 --- esphome/core/scheduler.cpp | 6 ++++++ esphome/core/scheduler.h | 2 ++ 2 files changed, 8 insertions(+) diff --git a/esphome/core/scheduler.cpp b/esphome/core/scheduler.cpp index 4c79f51b04..dea3f4428b 100644 --- a/esphome/core/scheduler.cpp +++ b/esphome/core/scheduler.cpp @@ -81,6 +81,7 @@ void HOT Scheduler::set_timer_common_(Component *component, SchedulerItem::Type item->callback = std::move(func); item->remove = false; +#ifndef USE_ESP8266 // Special handling for defer() (delay = 0, type = TIMEOUT) if (delay == 0 && type == SchedulerItem::TIMEOUT) { // Put in defer queue for guaranteed FIFO execution @@ -88,6 +89,7 @@ void HOT Scheduler::set_timer_common_(Component *component, SchedulerItem::Type this->defer_queue_.push_back(std::move(item)); return; } +#endif const auto now = this->millis_(); @@ -217,6 +219,7 @@ optional HOT Scheduler::next_schedule_in() { return item->next_execution_ - now; } void HOT Scheduler::call() { +#ifndef USE_ESP8266 // Process defer queue first to guarantee FIFO execution order for deferred items. // Previously, defer() used the heap which gave undefined order for equal timestamps, // causing race conditions on multi-core systems (ESP32, RP2040, BK7200). @@ -248,6 +251,7 @@ void HOT Scheduler::call() { this->execute_item_(item.get()); } } +#endif const auto now = this->millis_(); this->process_to_add(); @@ -432,12 +436,14 @@ bool HOT Scheduler::cancel_item_common_(Component *component, bool is_static_str bool ret = false; // Check all containers for matching items +#ifndef USE_ESP8266 for (auto &item : this->defer_queue_) { if (this->matches_item_(item, component, name_cstr, type)) { item->remove = true; ret = true; } } +#endif for (auto &item : this->items_) { if (this->matches_item_(item, component, name_cstr, type)) { diff --git a/esphome/core/scheduler.h b/esphome/core/scheduler.h index e617eb99c2..77b1c2902f 100644 --- a/esphome/core/scheduler.h +++ b/esphome/core/scheduler.h @@ -166,7 +166,9 @@ class Scheduler { Mutex lock_; std::vector> items_; std::vector> to_add_; +#ifndef USE_ESP8266 std::deque> defer_queue_; // FIFO queue for defer() calls +#endif uint32_t last_millis_{0}; uint16_t millis_major_{0}; uint32_t to_remove_{0}; From bdb7e19fd0814ef867c37b1a2858a2c7a0ac41fb Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 4 Jul 2025 10:59:58 -0500 Subject: [PATCH 2/2] guard esp8266 --- esphome/core/scheduler.cpp | 4 ++++ esphome/core/scheduler.h | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/esphome/core/scheduler.cpp b/esphome/core/scheduler.cpp index dea3f4428b..475639be48 100644 --- a/esphome/core/scheduler.cpp +++ b/esphome/core/scheduler.cpp @@ -83,6 +83,7 @@ void HOT Scheduler::set_timer_common_(Component *component, SchedulerItem::Type #ifndef USE_ESP8266 // Special handling for defer() (delay = 0, type = TIMEOUT) + // ESP8266 is excluded because it doesn't need thread-safe defer handling if (delay == 0 && type == SchedulerItem::TIMEOUT) { // Put in defer queue for guaranteed FIFO execution LockGuard guard{this->lock_}; @@ -227,6 +228,8 @@ void HOT Scheduler::call() { // - Deferred items (delay=0) go directly to defer_queue_ in set_timer_common_ // - Items execute in exact order they were deferred (FIFO guarantee) // - No deferred items exist in to_add_, so processing order doesn't affect correctness + // ESP8266 doesn't use this queue - it falls back to the heap-based approach since + // it's single-core and doesn't have thread safety concerns. while (!this->defer_queue_.empty()) { // IMPORTANT: The double-check pattern is REQUIRED for thread safety: // 1. First check: !defer_queue_.empty() without lock (may become stale) @@ -437,6 +440,7 @@ bool HOT Scheduler::cancel_item_common_(Component *component, bool is_static_str // Check all containers for matching items #ifndef USE_ESP8266 + // Only check defer_queue_ on platforms that have it for (auto &item : this->defer_queue_) { if (this->matches_item_(item, component, name_cstr, type)) { item->remove = true; diff --git a/esphome/core/scheduler.h b/esphome/core/scheduler.h index 77b1c2902f..75d4edfa72 100644 --- a/esphome/core/scheduler.h +++ b/esphome/core/scheduler.h @@ -167,6 +167,11 @@ class Scheduler { std::vector> items_; std::vector> to_add_; #ifndef USE_ESP8266 + // ESP8266 doesn't need the defer queue because: + // 1. It's single-core with no preemptive multitasking + // 2. All code runs in a single thread context + // 3. defer() calls can't have race conditions without true concurrency + // 4. Saves 40 bytes of RAM on memory-constrained ESP8266 devices std::deque> defer_queue_; // FIFO queue for defer() calls #endif uint32_t last_millis_{0};