1
0
mirror of https://github.com/esphome/esphome.git synced 2025-09-10 15:22:24 +01:00

[core] Reduce unnecessary nesting in scheduler loop

This commit is contained in:
J. Nick Koston
2025-09-07 22:02:03 -05:00
parent 8d90f13e97
commit 9a9783bb21

View File

@@ -436,85 +436,79 @@ void HOT Scheduler::call(uint32_t now) {
this->to_remove_ = 0; this->to_remove_ = 0;
} }
while (!this->items_.empty()) { while (!this->items_.empty()) {
// use scoping to indicate visibility of `item` variable // Don't copy-by value yet
{ auto &item = this->items_[0];
// Don't copy-by value yet if (item->get_next_execution() > now_64) {
auto &item = this->items_[0]; // Not reached timeout yet, done for this call
if (item->get_next_execution() > now_64) { break;
// Not reached timeout yet, done for this call }
break; // Don't run on failed components
} if (item->component != nullptr && item->component->is_failed()) {
// Don't run on failed components LockGuard guard{this->lock_};
if (item->component != nullptr && item->component->is_failed()) { this->pop_raw_();
LockGuard guard{this->lock_}; continue;
this->pop_raw_(); }
continue;
}
// Check if item is marked for removal // Check if item is marked for removal
// This handles two cases: // This handles two cases:
// 1. Item was marked for removal after cleanup_() but before we got here // 1. Item was marked for removal after cleanup_() but before we got here
// 2. Item is marked for removal but wasn't at the front of the heap during cleanup_() // 2. Item is marked for removal but wasn't at the front of the heap during cleanup_()
#ifdef ESPHOME_THREAD_MULTI_NO_ATOMICS #ifdef ESPHOME_THREAD_MULTI_NO_ATOMICS
// Multi-threaded platforms without atomics: must take lock to safely read remove flag // Multi-threaded platforms without atomics: must take lock to safely read remove flag
{ {
LockGuard guard{this->lock_}; LockGuard guard{this->lock_};
if (is_item_removed_(item.get())) {
this->pop_raw_();
this->to_remove_--;
continue;
}
}
#else
// Single-threaded or multi-threaded with atomics: can check without lock
if (is_item_removed_(item.get())) { if (is_item_removed_(item.get())) {
LockGuard guard{this->lock_};
this->pop_raw_(); this->pop_raw_();
this->to_remove_--; this->to_remove_--;
continue; continue;
} }
}
#else
// Single-threaded or multi-threaded with atomics: can check without lock
if (is_item_removed_(item.get())) {
LockGuard guard{this->lock_};
this->pop_raw_();
this->to_remove_--;
continue;
}
#endif #endif
#ifdef ESPHOME_DEBUG_SCHEDULER #ifdef ESPHOME_DEBUG_SCHEDULER
const char *item_name = item->get_name(); const char *item_name = item->get_name();
ESP_LOGV(TAG, "Running %s '%s/%s' with interval=%" PRIu32 " next_execution=%" PRIu64 " (now=%" PRIu64 ")", ESP_LOGV(TAG, "Running %s '%s/%s' with interval=%" PRIu32 " next_execution=%" PRIu64 " (now=%" PRIu64 ")",
item->get_type_str(), LOG_STR_ARG(item->get_source()), item_name ? item_name : "(null)", item->interval, item->get_type_str(), LOG_STR_ARG(item->get_source()), item_name ? item_name : "(null)", item->interval,
item->get_next_execution(), now_64); item->get_next_execution(), now_64);
#endif /* ESPHOME_DEBUG_SCHEDULER */ #endif /* ESPHOME_DEBUG_SCHEDULER */
// Warning: During callback(), a lot of stuff can happen, including: // Warning: During callback(), a lot of stuff can happen, including:
// - timeouts/intervals get added, potentially invalidating vector pointers // - timeouts/intervals get added, potentially invalidating vector pointers
// - timeouts/intervals get cancelled // - timeouts/intervals get cancelled
this->execute_item_(item.get(), now); this->execute_item_(item.get(), now);
LockGuard guard{this->lock_};
auto executed_item = std::move(this->items_[0]);
// Only pop after function call, this ensures we were reachable
// during the function call and know if we were cancelled.
this->pop_raw_();
if (executed_item->remove) {
// We were removed/cancelled in the function call, stop
this->to_remove_--;
continue;
} }
{ if (executed_item->type == SchedulerItem::INTERVAL) {
LockGuard guard{this->lock_}; executed_item->set_next_execution(now_64 + executed_item->interval);
// Add new item directly to to_add_
// new scope, item from before might have been moved in the vector // since we have the lock held
auto item = std::move(this->items_[0]); this->to_add_.push_back(std::move(executed_item));
// Only pop after function call, this ensures we were reachable } else {
// during the function call and know if we were cancelled. // Timeout completed - recycle it
this->pop_raw_(); this->recycle_item_(std::move(executed_item));
if (item->remove) {
// We were removed/cancelled in the function call, stop
this->to_remove_--;
continue;
}
if (item->type == SchedulerItem::INTERVAL) {
item->set_next_execution(now_64 + item->interval);
// Add new item directly to to_add_
// since we have the lock held
this->to_add_.push_back(std::move(item));
} else {
// Timeout completed - recycle it
this->recycle_item_(std::move(item));
}
has_added_items |= !this->to_add_.empty();
} }
has_added_items |= !this->to_add_.empty();
} }
if (has_added_items) { if (has_added_items) {