From 2a6e20dd32415811b0da6acaf7660a09cf501cfb Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 7 Feb 2026 14:26:13 -0600 Subject: [PATCH] [core] Deprecate set_retry, cancel_retry, and RetryResult set_retry does a std::make_shared() heap allocation on every invocation. No core component needs this pattern - all callers have been migrated to set_timeout or set_interval in prior PRs. The feature wastes flash and RAM on every firmware for a pattern that set_interval covers better, and the hidden heap allocation is a footgun for component authors. Deprecated in 2026.2.0, removal in 2026.8.0. Depends on: - #13841 [lps22] Replace set_retry with set_interval - #13842 [ms8607] Replace set_retry with set_timeout chain - #13843 [speaker] Replace set_retry with set_interval - #13844 [esp32_hosted] Replace set_retry with set_interval --- esphome/core/component.cpp | 19 +++++++++- esphome/core/component.h | 71 ++++++++++++-------------------------- esphome/core/scheduler.cpp | 7 ++++ esphome/core/scheduler.h | 23 +++++++++--- 4 files changed, 66 insertions(+), 54 deletions(-) diff --git a/esphome/core/component.cpp b/esphome/core/component.cpp index f09a39d2bb..6d8d1c57af 100644 --- a/esphome/core/component.cpp +++ b/esphome/core/component.cpp @@ -152,7 +152,10 @@ void Component::set_retry(const std::string &name, uint32_t initial_wait_time, u void Component::set_retry(const char *name, uint32_t initial_wait_time, uint8_t max_attempts, std::function &&f, float backoff_increase_factor) { // NOLINT +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" App.scheduler.set_retry(this, name, initial_wait_time, max_attempts, std::move(f), backoff_increase_factor); +#pragma GCC diagnostic pop } bool Component::cancel_retry(const std::string &name) { // NOLINT @@ -163,7 +166,10 @@ bool Component::cancel_retry(const std::string &name) { // NOLINT } bool Component::cancel_retry(const char *name) { // NOLINT +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" return App.scheduler.cancel_retry(this, name); +#pragma GCC diagnostic pop } void Component::set_timeout(const std::string &name, uint32_t timeout, std::function &&f) { // NOLINT @@ -203,10 +209,18 @@ bool Component::cancel_interval(uint32_t id) { return App.scheduler.cancel_inter void Component::set_retry(uint32_t id, uint32_t initial_wait_time, uint8_t max_attempts, std::function &&f, float backoff_increase_factor) { // NOLINT +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" App.scheduler.set_retry(this, id, initial_wait_time, max_attempts, std::move(f), backoff_increase_factor); +#pragma GCC diagnostic pop } -bool Component::cancel_retry(uint32_t id) { return App.scheduler.cancel_retry(this, id); } +bool Component::cancel_retry(uint32_t id) { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + return App.scheduler.cancel_retry(this, id); +#pragma GCC diagnostic pop +} void Component::call_loop() { this->loop(); } void Component::call_setup() { this->setup(); } @@ -371,7 +385,10 @@ void Component::set_interval(uint32_t interval, std::function &&f) { // } void Component::set_retry(uint32_t initial_wait_time, uint8_t max_attempts, std::function &&f, float backoff_increase_factor) { // NOLINT +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" App.scheduler.set_retry(this, "", initial_wait_time, max_attempts, std::move(f), backoff_increase_factor); +#pragma GCC diagnostic pop } bool Component::is_failed() const { return (this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_FAILED; } bool Component::is_ready() const { diff --git a/esphome/core/component.h b/esphome/core/component.h index 97f2afe1a4..c3582e23b1 100644 --- a/esphome/core/component.h +++ b/esphome/core/component.h @@ -68,6 +68,7 @@ extern const uint8_t STATUS_LED_OK; extern const uint8_t STATUS_LED_WARNING; extern const uint8_t STATUS_LED_ERROR; +// Remove before 2026.8.0 enum class RetryResult { DONE, RETRY }; extern const uint16_t WARN_IF_BLOCKING_OVER_MS; @@ -347,68 +348,40 @@ class Component { bool cancel_interval(const char *name); // NOLINT bool cancel_interval(uint32_t id); // NOLINT - /** Set an retry function with a unique name. Empty name means no cancelling possible. - * - * This will call the retry function f on the next scheduler loop. f should return RetryResult::DONE if - * it is successful and no repeat is required. Otherwise, returning RetryResult::RETRY will call f - * again in the future. - * - * The first retry of f happens after `initial_wait_time` milliseconds. The delay between retries is - * increased by multiplying by `backoff_increase_factor` each time. If no backoff_increase_factor is - * supplied (default = 1.0), the wait time will stay constant. - * - * The retry function f needs to accept a single argument: the number of attempts remaining. On the - * final retry of f, this value will be 0. - * - * This retry function can also be cancelled by name via cancel_retry(). - * - * IMPORTANT: Do not rely on this having correct timing. This is only called from - * loop() and therefore can be significantly delayed. - * - * REMARK: It is an error to supply a negative or zero `backoff_increase_factor`, and 1.0 will be used instead. - * - * REMARK: The interval between retries is stored into a `uint32_t`, so this doesn't behave correctly - * if `initial_wait_time * (backoff_increase_factor ** (max_attempts - 2))` overflows. - * - * @param name The identifier for this retry function. - * @param initial_wait_time The time in ms before f is called again - * @param max_attempts The maximum number of executions - * @param f The function (or lambda) that should be called - * @param backoff_increase_factor time between retries is multiplied by this factor on every retry after the first - * @see cancel_retry() - */ - // Remove before 2026.7.0 - ESPDEPRECATED("Use const char* or uint32_t overload instead. Removed in 2026.7.0", "2026.1.0") + /// @deprecated set_retry is deprecated. Use set_timeout or set_interval instead. Removed in 2026.8.0. + // Remove before 2026.8.0 + ESPDEPRECATED("set_retry is deprecated and will be removed in 2026.8.0. Use set_timeout or set_interval instead.", + "2026.2.0") 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 + // Remove before 2026.8.0 + ESPDEPRECATED("set_retry is deprecated and will be removed in 2026.8.0. Use set_timeout or set_interval instead.", + "2026.2.0") 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 - /** Set a retry function with a numeric ID (zero heap allocation). - * - * @param id The numeric identifier for this retry function - * @param initial_wait_time The wait time after the first execution - * @param max_attempts The max number of attempts - * @param f The function to call - * @param backoff_increase_factor The factor to increase the retry interval by - */ + // Remove before 2026.8.0 + ESPDEPRECATED("set_retry is deprecated and will be removed in 2026.8.0. Use set_timeout or set_interval instead.", + "2026.2.0") void set_retry(uint32_t id, uint32_t initial_wait_time, uint8_t max_attempts, // NOLINT std::function &&f, float backoff_increase_factor = 1.0f); // NOLINT + // Remove before 2026.8.0 + ESPDEPRECATED("set_retry is deprecated and will be removed in 2026.8.0. Use set_timeout or set_interval instead.", + "2026.2.0") void set_retry(uint32_t initial_wait_time, uint8_t max_attempts, std::function &&f, // NOLINT float backoff_increase_factor = 1.0f); // NOLINT - /** Cancel a retry function. - * - * @param name The identifier for this retry function. - * @return Whether a retry function was deleted. - */ - // Remove before 2026.7.0 - ESPDEPRECATED("Use const char* or uint32_t overload instead. Removed in 2026.7.0", "2026.1.0") + // Remove before 2026.8.0 + ESPDEPRECATED("cancel_retry is deprecated and will be removed in 2026.8.0.", "2026.2.0") bool cancel_retry(const std::string &name); // NOLINT - bool cancel_retry(const char *name); // NOLINT - bool cancel_retry(uint32_t id); // NOLINT + // Remove before 2026.8.0 + ESPDEPRECATED("cancel_retry is deprecated and will be removed in 2026.8.0.", "2026.2.0") + bool cancel_retry(const char *name); // NOLINT + // Remove before 2026.8.0 + ESPDEPRECATED("cancel_retry is deprecated and will be removed in 2026.8.0.", "2026.2.0") + bool cancel_retry(uint32_t id); // NOLINT /** Set a timeout function with a unique name. * diff --git a/esphome/core/scheduler.cpp b/esphome/core/scheduler.cpp index 047bf4ef17..6e898c2869 100644 --- a/esphome/core/scheduler.cpp +++ b/esphome/core/scheduler.cpp @@ -252,6 +252,11 @@ bool HOT Scheduler::cancel_interval(Component *component, uint32_t id) { return this->cancel_item_(component, NameType::NUMERIC_ID, nullptr, id, SchedulerItem::INTERVAL); } +// Suppress deprecation warnings for RetryResult usage in the still-present (but deprecated) retry implementation. +// Remove before 2026.8.0 along with all retry code. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + struct RetryArgs { // Ordered to minimize padding on 32-bit systems std::function func; @@ -364,6 +369,8 @@ bool HOT Scheduler::cancel_retry(Component *component, uint32_t id) { return this->cancel_retry_(component, NameType::NUMERIC_ID, nullptr, id); } +#pragma GCC diagnostic pop // End suppression of deprecated RetryResult warnings + optional HOT Scheduler::next_schedule_in(uint32_t now) { // IMPORTANT: This method should only be called from the main thread (loop task). // It performs cleanup and accesses items_[0] without holding a lock, which is only diff --git a/esphome/core/scheduler.h b/esphome/core/scheduler.h index 7de1023e6d..20b069f3f0 100644 --- a/esphome/core/scheduler.h +++ b/esphome/core/scheduler.h @@ -72,18 +72,30 @@ class Scheduler { bool cancel_interval(Component *component, const char *name); bool cancel_interval(Component *component, uint32_t id); - ESPDEPRECATED("Use const char* or uint32_t overload instead. Removed in 2026.7.0", "2026.1.0") + // Remove before 2026.8.0 + ESPDEPRECATED("set_retry is deprecated and will be removed in 2026.8.0. Use set_timeout or set_interval instead.", + "2026.2.0") void set_retry(Component *component, const std::string &name, uint32_t initial_wait_time, uint8_t max_attempts, std::function func, float backoff_increase_factor = 1.0f); + // Remove before 2026.8.0 + ESPDEPRECATED("set_retry is deprecated and will be removed in 2026.8.0. Use set_timeout or set_interval instead.", + "2026.2.0") void set_retry(Component *component, const char *name, uint32_t initial_wait_time, uint8_t max_attempts, std::function func, float backoff_increase_factor = 1.0f); - /// Set a retry with a numeric ID (zero heap allocation) + // Remove before 2026.8.0 + ESPDEPRECATED("set_retry is deprecated and will be removed in 2026.8.0. Use set_timeout or set_interval instead.", + "2026.2.0") void set_retry(Component *component, uint32_t id, uint32_t initial_wait_time, uint8_t max_attempts, std::function func, float backoff_increase_factor = 1.0f); - ESPDEPRECATED("Use const char* or uint32_t overload instead. Removed in 2026.7.0", "2026.1.0") + // Remove before 2026.8.0 + ESPDEPRECATED("cancel_retry is deprecated and will be removed in 2026.8.0.", "2026.2.0") bool cancel_retry(Component *component, const std::string &name); + // Remove before 2026.8.0 + ESPDEPRECATED("cancel_retry is deprecated and will be removed in 2026.8.0.", "2026.2.0") bool cancel_retry(Component *component, const char *name); + // Remove before 2026.8.0 + ESPDEPRECATED("cancel_retry is deprecated and will be removed in 2026.8.0.", "2026.2.0") bool cancel_retry(Component *component, uint32_t id); // Calculate when the next scheduled item should run @@ -231,11 +243,14 @@ class Scheduler { uint32_t hash_or_id, uint32_t delay, std::function func, bool is_retry = false, bool skip_cancel = false); - // Common implementation for retry + // Common implementation for retry - Remove before 2026.8.0 // name_type determines storage type: STATIC_STRING uses static_name, others use hash_or_id +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" void set_retry_common_(Component *component, NameType name_type, const char *static_name, uint32_t hash_or_id, uint32_t initial_wait_time, uint8_t max_attempts, std::function func, float backoff_increase_factor); +#pragma GCC diagnostic pop // Common implementation for cancel_retry bool cancel_retry_(Component *component, NameType name_type, const char *static_name, uint32_t hash_or_id);