From f278250740330b6b4e990ae19b0bc91d846ec924 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 2 Mar 2026 06:57:59 -1000 Subject: [PATCH] [core] Deduplicate ControllerRegistry notify dispatch loop (#14394) --- esphome/core/controller_registry.cpp | 21 +++++++++++++-------- esphome/core/controller_registry.h | 15 +++++++++++++++ 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/esphome/core/controller_registry.cpp b/esphome/core/controller_registry.cpp index 13b505e8e9..255efa86ba 100644 --- a/esphome/core/controller_registry.cpp +++ b/esphome/core/controller_registry.cpp @@ -10,21 +10,26 @@ StaticVector ControllerRegistry::controll void ControllerRegistry::register_controller(Controller *controller) { controllers.push_back(controller); } +void ControllerRegistry::notify(void *obj, DispatchFunc dispatch) { + for (auto *controller : controllers) { + dispatch(controller, obj); + } +} + // Macro for standard registry notification dispatch - calls on__update() +// Each wrapper passes a small trampoline lambda that calls the correct virtual method. +// NOLINTBEGIN(bugprone-macro-parentheses) #define CONTROLLER_REGISTRY_NOTIFY(entity_type, entity_name) \ - void ControllerRegistry::notify_##entity_name##_update(entity_type *obj) { /* NOLINT(bugprone-macro-parentheses) */ \ - for (auto *controller : controllers) { \ - controller->on_##entity_name##_update(obj); \ - } \ + void ControllerRegistry::notify_##entity_name##_update(entity_type *obj) { \ + notify(obj, [](Controller *c, void *o) { c->on_##entity_name##_update(static_cast(o)); }); \ } // Macro for entities where controller method has no "_update" suffix (Event, Update) #define CONTROLLER_REGISTRY_NOTIFY_NO_UPDATE_SUFFIX(entity_type, entity_name) \ - void ControllerRegistry::notify_##entity_name(entity_type *obj) { /* NOLINT(bugprone-macro-parentheses) */ \ - for (auto *controller : controllers) { \ - controller->on_##entity_name(obj); \ - } \ + void ControllerRegistry::notify_##entity_name(entity_type *obj) { \ + notify(obj, [](Controller *c, void *o) { c->on_##entity_name(static_cast(o)); }); \ } +// NOLINTEND(bugprone-macro-parentheses) #ifdef USE_BINARY_SENSOR CONTROLLER_REGISTRY_NOTIFY(binary_sensor::BinarySensor, binary_sensor) diff --git a/esphome/core/controller_registry.h b/esphome/core/controller_registry.h index d6452d8827..15e3b4ba83 100644 --- a/esphome/core/controller_registry.h +++ b/esphome/core/controller_registry.h @@ -247,6 +247,21 @@ class ControllerRegistry { #endif protected: + /** Type-erased dispatch function pointer. + * + * Each notify method passes a small trampoline that calls the + * correct virtual method on Controller. The shared notify() loop + * iterates controllers once, calling the trampoline for each. + */ + using DispatchFunc = void (*)(Controller *, void *); + + /** Shared dispatch loop - iterates controllers and calls dispatch for each. + * + * Marked noinline to ensure only one copy of the loop exists in flash, + * rather than being duplicated into each notify_*_update wrapper. + */ + static void __attribute__((noinline)) notify(void *obj, DispatchFunc dispatch); + static StaticVector controllers; };