mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 15:12:06 +00:00 
			
		
		
		
	fix defer churn
This commit is contained in:
		| @@ -101,6 +101,22 @@ void HOT Scheduler::set_timer_common_(Component *component, SchedulerItem::Type | |||||||
|   // Take lock early to protect scheduler_item_pool_ access |   // Take lock early to protect scheduler_item_pool_ access | ||||||
|   LockGuard guard{this->lock_}; |   LockGuard guard{this->lock_}; | ||||||
|  |  | ||||||
|  |   // Optimization: if we're updating a defer that hasn't executed yet, just update its callback | ||||||
|  |   // This avoids allocating a new item and cancelling/re-adding | ||||||
|  |   if (delay == 0 && type == SchedulerItem::TIMEOUT && !skip_cancel && name_cstr != nullptr) { | ||||||
|  | #ifdef ESPHOME_THREAD_SINGLE | ||||||
|  |     // Single-threaded: check to_add_ for defers that haven't been moved to heap yet | ||||||
|  |     if (this->try_update_defer_in_container_(this->to_add_, component, name_cstr, std::move(func))) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  | #else | ||||||
|  |     // Multi-threaded: check defer_queue_ | ||||||
|  |     if (this->try_update_defer_in_container_(this->defer_queue_, component, name_cstr, std::move(func))) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  | #endif | ||||||
|  |   } | ||||||
|  |  | ||||||
|   // Create and populate the scheduler item |   // Create and populate the scheduler item | ||||||
|   std::unique_ptr<SchedulerItem> item; |   std::unique_ptr<SchedulerItem> item; | ||||||
|   if (!this->scheduler_item_pool_.empty()) { |   if (!this->scheduler_item_pool_.empty()) { | ||||||
|   | |||||||
| @@ -217,6 +217,15 @@ class Scheduler { | |||||||
|   // Common implementation for cancel operations |   // Common implementation for cancel operations | ||||||
|   bool cancel_item_(Component *component, bool is_static_string, const void *name_ptr, SchedulerItem::Type type); |   bool cancel_item_(Component *component, bool is_static_string, const void *name_ptr, SchedulerItem::Type type); | ||||||
|  |  | ||||||
|  |   // Helper to check if two scheduler item names match | ||||||
|  |   inline bool HOT names_match_(const char *name1, const char *name2) const { | ||||||
|  |     // Check pointer equality first (common for static strings), then string contents | ||||||
|  |     // The core ESPHome codebase uses static strings (const char*) for component names, | ||||||
|  |     // making pointer comparison effective. The std::string overloads exist only for | ||||||
|  |     // compatibility with external components but are rarely used in practice. | ||||||
|  |     return (name1 != nullptr && name2 != nullptr) && ((name1 == name2) || (strcmp(name1, name2) == 0)); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   // Helper function to check if item matches criteria for cancellation |   // Helper function to check if item matches criteria for cancellation | ||||||
|   inline bool HOT matches_item_(const std::unique_ptr<SchedulerItem> &item, Component *component, const char *name_cstr, |   inline bool HOT matches_item_(const std::unique_ptr<SchedulerItem> &item, Component *component, const char *name_cstr, | ||||||
|                                 SchedulerItem::Type type, bool match_retry, bool skip_removed = true) const { |                                 SchedulerItem::Type type, bool match_retry, bool skip_removed = true) const { | ||||||
| @@ -224,19 +233,7 @@ class Scheduler { | |||||||
|         (match_retry && !item->is_retry)) { |         (match_retry && !item->is_retry)) { | ||||||
|       return false; |       return false; | ||||||
|     } |     } | ||||||
|     const char *item_name = item->get_name(); |     return this->names_match_(item->get_name(), name_cstr); | ||||||
|     if (item_name == nullptr) { |  | ||||||
|       return false; |  | ||||||
|     } |  | ||||||
|     // Fast path: if pointers are equal |  | ||||||
|     // This is effective because the core ESPHome codebase uses static strings (const char*) |  | ||||||
|     // for component names. The std::string overloads exist only for compatibility with |  | ||||||
|     // external components, but are rarely used in practice. |  | ||||||
|     if (item_name == name_cstr) { |  | ||||||
|       return true; |  | ||||||
|     } |  | ||||||
|     // Slow path: compare string contents |  | ||||||
|     return strcmp(name_cstr, item_name) == 0; |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   // Helper to execute a scheduler item |   // Helper to execute a scheduler item | ||||||
| @@ -313,6 +310,28 @@ class Scheduler { | |||||||
|     return cancelled; |     return cancelled; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   // Template helper to try updating a defer in a container instead of allocating a new one | ||||||
|  |   // Returns true if the defer was updated, false if not found | ||||||
|  |   template<typename Container> | ||||||
|  |   bool try_update_defer_in_container_(Container &container, Component *component, const char *name_cstr, | ||||||
|  |                                       std::function<void()> &&func) { | ||||||
|  |     if (container.empty()) { | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     auto &last_item = container.back(); | ||||||
|  |  | ||||||
|  |     // Check if last item is a matching defer (timeout with 0 delay) and names match | ||||||
|  |     if (last_item->component != component || last_item->type != SchedulerItem::TIMEOUT || last_item->interval != 0 || | ||||||
|  |         is_item_removed_(last_item.get()) || !this->names_match_(last_item->get_name(), name_cstr)) { | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Same defer at the end - just update the callback, no allocation needed | ||||||
|  |     last_item->callback = std::move(func); | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   Mutex lock_; |   Mutex lock_; | ||||||
|   std::vector<std::unique_ptr<SchedulerItem>> items_; |   std::vector<std::unique_ptr<SchedulerItem>> items_; | ||||||
|   std::vector<std::unique_ptr<SchedulerItem>> to_add_; |   std::vector<std::unique_ptr<SchedulerItem>> to_add_; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user