diff --git a/esphome/core/component.cpp b/esphome/core/component.cpp index b7c0cedb7..97ab2edb5 100644 --- a/esphome/core/component.cpp +++ b/esphome/core/component.cpp @@ -138,10 +138,19 @@ void Component::set_retry(const std::string &name, uint32_t initial_wait_time, u App.scheduler.set_retry(this, name, initial_wait_time, max_attempts, std::move(f), backoff_increase_factor); } +void Component::set_retry(const char *name, uint32_t initial_wait_time, uint8_t max_attempts, + std::function &&f, float backoff_increase_factor) { // NOLINT + App.scheduler.set_retry(this, name, initial_wait_time, max_attempts, std::move(f), backoff_increase_factor); +} + bool Component::cancel_retry(const std::string &name) { // NOLINT return App.scheduler.cancel_retry(this, name); } +bool Component::cancel_retry(const char *name) { // NOLINT + return App.scheduler.cancel_retry(this, name); +} + void Component::set_timeout(const std::string &name, uint32_t timeout, std::function &&f) { // NOLINT App.scheduler.set_timeout(this, name, timeout, std::move(f)); } diff --git a/esphome/core/component.h b/esphome/core/component.h index 3d45a020c..32f594d6f 100644 --- a/esphome/core/component.h +++ b/esphome/core/component.h @@ -367,6 +367,9 @@ class Component { void set_retry(const std::string &name, uint32_t initial_wait_time, uint8_t max_attempts, // NOLINT std::function &&f, float backoff_increase_factor = 1.0f); // NOLINT + void set_retry(const char *name, uint32_t initial_wait_time, uint8_t max_attempts, // NOLINT + std::function &&f, float backoff_increase_factor = 1.0f); // NOLINT + void set_retry(uint32_t initial_wait_time, uint8_t max_attempts, std::function &&f, // NOLINT float backoff_increase_factor = 1.0f); // NOLINT @@ -376,6 +379,7 @@ class Component { * @return Whether a retry function was deleted. */ bool cancel_retry(const std::string &name); // NOLINT + bool cancel_retry(const char *name); // NOLINT /** Set a timeout function with a unique name. * diff --git a/esphome/core/scheduler.cpp b/esphome/core/scheduler.cpp index f84495950..8b713523b 100644 --- a/esphome/core/scheduler.cpp +++ b/esphome/core/scheduler.cpp @@ -204,13 +204,21 @@ bool HOT Scheduler::cancel_interval(Component *component, const char *name) { } struct RetryArgs { + // Ordered to minimize padding on 32-bit systems std::function func; - uint8_t retry_countdown; - uint32_t current_interval; Component *component; - std::string name; // Keep as std::string since retry uses it dynamically - float backoff_increase_factor; Scheduler *scheduler; + const char *name; // Points to static string or owned copy + uint32_t current_interval; + float backoff_increase_factor; + uint8_t retry_countdown; + bool name_is_dynamic; // True if name needs delete[] + + ~RetryArgs() { + if (this->name_is_dynamic && this->name) { + delete[] this->name; + } + } }; void retry_handler(const std::shared_ptr &args) { @@ -218,8 +226,10 @@ void retry_handler(const std::shared_ptr &args) { if (retry_result == RetryResult::DONE || args->retry_countdown <= 0) return; // second execution of `func` happens after `initial_wait_time` + // Pass is_static_string=true because args->name is owned by the shared_ptr + // which is captured in the lambda and outlives the SchedulerItem args->scheduler->set_timer_common_( - args->component, Scheduler::SchedulerItem::TIMEOUT, false, &args->name, args->current_interval, + args->component, Scheduler::SchedulerItem::TIMEOUT, true, args->name, args->current_interval, [args]() { retry_handler(args); }, /* is_retry= */ true); // backoff_increase_factor applied to third & later executions args->current_interval *= args->backoff_increase_factor; @@ -246,16 +256,35 @@ void HOT Scheduler::set_retry_common_(Component *component, bool is_static_strin auto args = std::make_shared(); args->func = std::move(func); - args->retry_countdown = max_attempts; - args->current_interval = initial_wait_time; args->component = component; - args->name = name_cstr ? name_cstr : ""; // Convert to std::string for RetryArgs - args->backoff_increase_factor = backoff_increase_factor; args->scheduler = this; + args->current_interval = initial_wait_time; + args->backoff_increase_factor = backoff_increase_factor; + args->retry_countdown = max_attempts; + + // Store name - either as static pointer or owned copy + if (name_cstr == nullptr || name_cstr[0] == '\0') { + // Empty or null name - use empty string literal + args->name = ""; + args->name_is_dynamic = false; + } else if (is_static_string) { + // Static string - just store the pointer + args->name = name_cstr; + args->name_is_dynamic = false; + } else { + // Dynamic string - make a copy + size_t len = strlen(name_cstr); + char *copy = new char[len + 1]; + memcpy(copy, name_cstr, len + 1); + args->name = copy; + args->name_is_dynamic = true; + } // First execution of `func` immediately - use set_timer_common_ with is_retry=true + // Pass is_static_string=true because args->name is owned by the shared_ptr + // which is captured in the lambda and outlives the SchedulerItem this->set_timer_common_( - component, SchedulerItem::TIMEOUT, false, &args->name, 0, [args]() { retry_handler(args); }, + component, SchedulerItem::TIMEOUT, true, args->name, 0, [args]() { retry_handler(args); }, /* is_retry= */ true); }