[scheduler] Skip cancel for anonymous items, add empty-container fast path (#15397)

This commit is contained in:
J. Nick Koston
2026-04-06 15:26:40 -10:00
committed by GitHub
parent d15fa84f4f
commit 82dc80a413
2 changed files with 36 additions and 18 deletions
+21 -3
View File
@@ -214,8 +214,9 @@ void HOT Scheduler::set_timer_common_(Component *component, SchedulerItem::Type
#endif /* ESPHOME_DEBUG_SCHEDULER */
}
// Common epilogue: atomic cancel-and-add (unless skip_cancel is true)
if (!skip_cancel) {
// Common epilogue: atomic cancel-and-add (unless skip_cancel is true or anonymous)
// Anonymous items (STATIC_STRING with nullptr) can never match anything, so skip the scan.
if (!skip_cancel && (name_type != NameType::STATIC_STRING || static_name != nullptr)) {
this->cancel_item_locked_(component, name_type, static_name, hash_or_id, type, /* match_retry= */ false,
/* find_first= */ true);
}
@@ -742,6 +743,23 @@ bool HOT Scheduler::cancel_item_(Component *component, NameType name_type, const
// When find_first=false, cancels ALL matches across all containers (needed for
// public cancel path where DelayAction parallel mode can create duplicates).
// name_type determines matching: STATIC_STRING uses static_name, others use hash_or_id
size_t Scheduler::mark_matching_items_removed_slow_locked_(std::vector<SchedulerItem *> &container,
Component *component, NameType name_type,
const char *static_name, uint32_t hash_or_id,
SchedulerItem::Type type, bool match_retry,
bool find_first) {
size_t count = 0;
for (auto *item : container) {
if (this->matches_item_locked_(item, component, name_type, static_name, hash_or_id, type, match_retry)) {
this->set_item_removed_(item, true);
if (find_first)
return 1;
count++;
}
}
return count;
}
bool HOT Scheduler::cancel_item_locked_(Component *component, NameType name_type, const char *static_name,
uint32_t hash_or_id, SchedulerItem::Type type, bool match_retry,
bool find_first) {
@@ -767,7 +785,7 @@ bool HOT Scheduler::cancel_item_locked_(Component *component, NameType name_type
// The main loop may be executing an item's callback right now, and recycling
// would destroy the callback while it's running (use-after-free).
// Only the main loop in call() should recycle items after execution completes.
if (!this->items_.empty()) {
{
size_t heap_cancelled = this->mark_matching_items_removed_locked_(this->items_, component, name_type, static_name,
hash_or_id, type, match_retry, find_first);
total_cancelled += heap_cancelled;
+15 -15
View File
@@ -495,23 +495,23 @@ class Scheduler {
// name_type determines matching: STATIC_STRING uses static_name, others use hash_or_id
// Returns the number of items marked for removal.
// IMPORTANT: Must be called with scheduler lock held
__attribute__((noinline)) size_t mark_matching_items_removed_locked_(std::vector<SchedulerItem *> &container,
Component *component, NameType name_type,
const char *static_name, uint32_t hash_or_id,
SchedulerItem::Type type, bool match_retry,
bool find_first = false) {
size_t count = 0;
for (auto *item : container) {
if (this->matches_item_locked_(item, component, name_type, static_name, hash_or_id, type, match_retry)) {
this->set_item_removed_(item, true);
if (find_first)
return 1;
count++;
}
}
return count;
// Inlined: the fast path (empty container) avoids calling the out-of-line scan.
inline size_t HOT mark_matching_items_removed_locked_(std::vector<SchedulerItem *> &container, Component *component,
NameType name_type, const char *static_name,
uint32_t hash_or_id, SchedulerItem::Type type, bool match_retry,
bool find_first = false) {
if (container.empty())
return 0;
return this->mark_matching_items_removed_slow_locked_(container, component, name_type, static_name, hash_or_id,
type, match_retry, find_first);
}
// Out-of-line slow path for mark_matching_items_removed_locked_ when container is non-empty.
// IMPORTANT: Must be called with scheduler lock held
__attribute__((noinline)) size_t mark_matching_items_removed_slow_locked_(
std::vector<SchedulerItem *> &container, Component *component, NameType name_type, const char *static_name,
uint32_t hash_or_id, SchedulerItem::Type type, bool match_retry, bool find_first);
Mutex lock_;
std::vector<SchedulerItem *> items_;
std::vector<SchedulerItem *> to_add_;