From e6334b0716591eec25db66fa4d21e8bd350a4913 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 28 Jun 2025 09:41:12 -0500 Subject: [PATCH] dry --- esphome/core/scheduler.cpp | 157 ++++++++++++------------------------- esphome/core/scheduler.h | 10 +-- 2 files changed, 54 insertions(+), 113 deletions(-) diff --git a/esphome/core/scheduler.cpp b/esphome/core/scheduler.cpp index 30c4cb8137..9035214faf 100644 --- a/esphome/core/scheduler.cpp +++ b/esphome/core/scheduler.cpp @@ -57,147 +57,92 @@ static void validate_static_string(const char *name) { // iterating over them from the loop task is fine; but iterating from any other context requires the lock to be held to // avoid the main thread modifying the list while it is being accessed. -// Template implementation for set_timeout -template -void HOT Scheduler::set_timeout_impl_(Component *component, const NameType &name, uint32_t timeout, - std::function func, bool make_copy) { +// Common implementation for both timeout and interval +void HOT Scheduler::set_timer_common_(Component *component, SchedulerItem::Type type, bool is_static_string, + const void *name_ptr, uint32_t delay, std::function func) { const auto now = this->millis_(); - // Handle empty name check based on type - bool is_empty = false; - if constexpr (std::is_same_v) { - is_empty = name.empty(); + // Get the name as const char* + const char *name_cstr = nullptr; + const std::string *name_str = nullptr; + + if (is_static_string) { + name_cstr = static_cast(name_ptr); } else { - is_empty = (name == nullptr || name[0] == '\0'); + name_str = static_cast(name_ptr); + name_cstr = name_str->c_str(); } - if (!is_empty) - this->cancel_timeout(component, name); + // Check if name is empty + bool is_empty = (name_cstr == nullptr || name_cstr[0] == '\0'); - if (timeout == SCHEDULER_DONT_RUN) + if (!is_empty) { + if (type == SchedulerItem::TIMEOUT) { + this->cancel_timeout(component, name_cstr); + } else { + this->cancel_interval(component, name_cstr); + } + } + + if (delay == SCHEDULER_DONT_RUN) return; + // For intervals, calculate offset + uint32_t offset = 0; + if (type == SchedulerItem::INTERVAL && delay != 0) { + offset = (random_uint32() % delay) / 2; + } + auto item = make_unique(); item->component = component; - // Set name based on type - if constexpr (std::is_same_v) { - item->set_name(name.c_str(), make_copy); - } else { - item->set_name(name, make_copy); -#ifdef ESPHOME_DEBUG_SCHEDULER - // Validate static strings in debug mode - if (!make_copy && name != nullptr) { - validate_static_string(name); - } -#endif - } + // Set name with appropriate copy flag + item->set_name(name_cstr, !is_static_string); - item->type = SchedulerItem::TIMEOUT; - item->next_execution_ = now + timeout; +#ifdef ESPHOME_DEBUG_SCHEDULER + // Validate static strings in debug mode + if (is_static_string && name_cstr != nullptr) { + validate_static_string(name_cstr); + } +#endif + + item->type = type; + item->interval = (type == SchedulerItem::INTERVAL) ? delay : 0; + item->next_execution_ = now + ((type == SchedulerItem::TIMEOUT) ? delay : offset); item->callback = std::move(func); item->remove = false; + #ifdef ESPHOME_DEBUG_SCHEDULER - const char *name_str = nullptr; - if constexpr (std::is_same_v) { - name_str = name.c_str(); + const char *type_str = (type == SchedulerItem::TIMEOUT) ? "timeout" : "interval"; + if (type == SchedulerItem::TIMEOUT) { + ESP_LOGD(TAG, "set_%s(name='%s/%s', %s=%" PRIu32 ")", type_str, item->get_source(), name_cstr, type_str, delay); } else { - name_str = name; + ESP_LOGD(TAG, "set_%s(name='%s/%s', %s=%" PRIu32 ", offset=%" PRIu32 ")", type_str, item->get_source(), name_cstr, + type_str, delay, offset); } - ESP_LOGD(TAG, "set_timeout(name='%s/%s', timeout=%" PRIu32 ")", item->get_source(), name_str, timeout); #endif this->push_(std::move(item)); } -// Explicit instantiations -template void Scheduler::set_timeout_impl_(Component *, const std::string &, uint32_t, - std::function, bool); -template void Scheduler::set_timeout_impl_(Component *, const char *const &, uint32_t, - std::function, bool); - void HOT Scheduler::set_timeout(Component *component, const char *name, uint32_t timeout, std::function func) { - return this->set_timeout_impl_(component, name, timeout, std::move(func), false); + this->set_timer_common_(component, SchedulerItem::TIMEOUT, true, name, timeout, std::move(func)); } void HOT Scheduler::set_timeout(Component *component, const std::string &name, uint32_t timeout, std::function func) { - return this->set_timeout_impl_(component, name, timeout, std::move(func), true); + this->set_timer_common_(component, SchedulerItem::TIMEOUT, false, &name, timeout, std::move(func)); } bool HOT Scheduler::cancel_timeout(Component *component, const std::string &name) { return this->cancel_item_(component, name, SchedulerItem::TIMEOUT); } -// Template implementation for set_interval -template -void HOT Scheduler::set_interval_impl_(Component *component, const NameType &name, uint32_t interval, - std::function func, bool make_copy) { - const auto now = this->millis_(); - - // Handle empty name check based on type - bool is_empty = false; - if constexpr (std::is_same_v) { - is_empty = name.empty(); - } else { - is_empty = (name == nullptr || name[0] == '\0'); - } - - if (!is_empty) - this->cancel_interval(component, name); - - if (interval == SCHEDULER_DONT_RUN) - return; - - // only put offset in lower half - uint32_t offset = 0; - if (interval != 0) - offset = (random_uint32() % interval) / 2; - - auto item = make_unique(); - item->component = component; - - // Set name based on type - if constexpr (std::is_same_v) { - item->set_name(name.c_str(), make_copy); - } else { - item->set_name(name, make_copy); -#ifdef ESPHOME_DEBUG_SCHEDULER - // Validate static strings in debug mode - if (!make_copy && name != nullptr) { - validate_static_string(name); - } -#endif - } - - item->type = SchedulerItem::INTERVAL; - item->interval = interval; - item->next_execution_ = now + offset; - item->callback = std::move(func); - item->remove = false; -#ifdef ESPHOME_DEBUG_SCHEDULER - const char *name_str = nullptr; - if constexpr (std::is_same_v) { - name_str = name.c_str(); - } else { - name_str = name; - } - ESP_LOGD(TAG, "set_interval(name='%s/%s', interval=%" PRIu32 ", offset=%" PRIu32 ")", item->get_source(), name_str, - interval, offset); -#endif - this->push_(std::move(item)); -} - -// Explicit instantiations -template void Scheduler::set_interval_impl_(Component *, const std::string &, uint32_t, - std::function, bool); -template void Scheduler::set_interval_impl_(Component *, const char *const &, uint32_t, - std::function, bool); - void HOT Scheduler::set_interval(Component *component, const std::string &name, uint32_t interval, std::function func) { - return this->set_interval_impl_(component, name, interval, std::move(func), true); + this->set_timer_common_(component, SchedulerItem::INTERVAL, false, &name, interval, std::move(func)); } + void HOT Scheduler::set_interval(Component *component, const char *name, uint32_t interval, std::function func) { - return this->set_interval_impl_(component, name, interval, std::move(func), false); + this->set_timer_common_(component, SchedulerItem::INTERVAL, true, name, interval, std::move(func)); } bool HOT Scheduler::cancel_interval(Component *component, const std::string &name) { return this->cancel_item_(component, name, SchedulerItem::INTERVAL); diff --git a/esphome/core/scheduler.h b/esphome/core/scheduler.h index 73940d7fff..7fc2e42b99 100644 --- a/esphome/core/scheduler.h +++ b/esphome/core/scheduler.h @@ -55,13 +55,9 @@ class Scheduler { void process_to_add(); protected: - // Template helper to handle both const char* and std::string efficiently - template - void set_timeout_impl_(Component *component, const NameType &name, uint32_t timeout, std::function func, - bool make_copy); - template - void set_interval_impl_(Component *component, const NameType &name, uint32_t interval, std::function func, - bool make_copy); + // Common implementation for both timeout and interval + void set_timer_common_(Component *component, SchedulerItem::Type type, bool is_static_string, const void *name_ptr, + uint32_t delay, std::function func); struct SchedulerItem { // Ordered by size to minimize padding