Compare commits

..

2 Commits

Author SHA1 Message Date
J. Nick Koston
6eeaca2020 bot 2026-01-19 22:36:28 -10:00
J. Nick Koston
7bc142ad02 [core] Simplify LazyCallbackManager memory management 2026-01-19 22:20:43 -10:00
4 changed files with 31 additions and 24 deletions

View File

@@ -1,5 +1,8 @@
#include "logger.h"
#include <cinttypes>
#ifdef USE_ESPHOME_TASK_LOG_BUFFER
#include <memory> // For unique_ptr
#endif
#include "esphome/core/application.h"
#include "esphome/core/hal.h"
@@ -196,8 +199,7 @@ inline uint8_t Logger::level_for(const char *tag) {
Logger::Logger(uint32_t baud_rate, size_t tx_buffer_size) : baud_rate_(baud_rate), tx_buffer_size_(tx_buffer_size) {
// add 1 to buffer size for null terminator
// NOLINTNEXTLINE(cppcoreguidelines-owning-memory) - allocated once, never freed
this->tx_buffer_ = new char[this->tx_buffer_size_ + 1];
this->tx_buffer_ = new char[this->tx_buffer_size_ + 1]; // NOLINT
#if defined(USE_ESP32) || defined(USE_LIBRETINY)
this->main_task_ = xTaskGetCurrentTaskHandle();
#elif defined(USE_ZEPHYR)
@@ -210,14 +212,11 @@ Logger::Logger(uint32_t baud_rate, size_t tx_buffer_size) : baud_rate_(baud_rate
void Logger::init_log_buffer(size_t total_buffer_size) {
#ifdef USE_HOST
// Host uses slot count instead of byte size
// NOLINTNEXTLINE(cppcoreguidelines-owning-memory) - allocated once, never freed
this->log_buffer_ = new logger::TaskLogBufferHost(total_buffer_size);
this->log_buffer_ = esphome::make_unique<logger::TaskLogBufferHost>(total_buffer_size);
#elif defined(USE_ESP32)
// NOLINTNEXTLINE(cppcoreguidelines-owning-memory) - allocated once, never freed
this->log_buffer_ = new logger::TaskLogBuffer(total_buffer_size);
this->log_buffer_ = esphome::make_unique<logger::TaskLogBuffer>(total_buffer_size);
#elif defined(USE_LIBRETINY)
// NOLINTNEXTLINE(cppcoreguidelines-owning-memory) - allocated once, never freed
this->log_buffer_ = new logger::TaskLogBufferLibreTiny(total_buffer_size);
this->log_buffer_ = esphome::make_unique<logger::TaskLogBufferLibreTiny>(total_buffer_size);
#endif
#if defined(USE_ESP32) || defined(USE_LIBRETINY)

View File

@@ -412,11 +412,11 @@ class Logger : public Component {
#endif
#ifdef USE_ESPHOME_TASK_LOG_BUFFER
#ifdef USE_HOST
logger::TaskLogBufferHost *log_buffer_{nullptr}; // Allocated once, never freed
std::unique_ptr<logger::TaskLogBufferHost> log_buffer_; // Will be initialized with init_log_buffer
#elif defined(USE_ESP32)
logger::TaskLogBuffer *log_buffer_{nullptr}; // Allocated once, never freed
std::unique_ptr<logger::TaskLogBuffer> log_buffer_; // Will be initialized with init_log_buffer
#elif defined(USE_LIBRETINY)
logger::TaskLogBufferLibreTiny *log_buffer_{nullptr}; // Allocated once, never freed
std::unique_ptr<logger::TaskLogBufferLibreTiny> log_buffer_; // Will be initialized with init_log_buffer
#endif
#endif

View File

@@ -79,17 +79,13 @@ async def setup_conf(config, key):
async def to_code(config):
# Request specific WiFi listeners based on which sensors are configured
# Each sensor needs its own listener slot - call request for EACH sensor
# SSID and BSSID use WiFiConnectStateListener
for key in (CONF_SSID, CONF_BSSID):
if key in config:
wifi.request_wifi_connect_state_listener()
if CONF_SSID in config or CONF_BSSID in config:
wifi.request_wifi_connect_state_listener()
# IP address and DNS use WiFiIPStateListener
for key in (CONF_IP_ADDRESS, CONF_DNS_ADDRESS):
if key in config:
wifi.request_wifi_ip_state_listener()
if CONF_IP_ADDRESS in config or CONF_DNS_ADDRESS in config:
wifi.request_wifi_ip_state_listener()
# Scan results use WiFiScanResultsListener
if CONF_SCAN_RESULTS in config:

View File

@@ -348,8 +348,6 @@ template<typename T> class FixedVector {
size_t size() const { return size_; }
bool empty() const { return size_ == 0; }
size_t capacity() const { return capacity_; }
bool full() const { return size_ == capacity_; }
/// Access element without bounds checking (matches std::vector behavior)
/// Caller must ensure index is valid (i < size())
@@ -1345,16 +1343,30 @@ template<typename... X> class LazyCallbackManager;
*
* Memory overhead comparison (32-bit systems):
* - CallbackManager: 12 bytes (empty std::vector)
* - LazyCallbackManager: 4 bytes (nullptr unique_ptr)
* - LazyCallbackManager: 4 bytes (nullptr pointer)
*
* Uses plain pointer instead of unique_ptr to avoid template instantiation overhead.
* The class is explicitly non-copyable/non-movable for Rule of Five compliance.
*
* @tparam Ts The arguments for the callbacks, wrapped in void().
*/
template<typename... Ts> class LazyCallbackManager<void(Ts...)> {
public:
LazyCallbackManager() = default;
/// Destructor - clean up allocated CallbackManager if any.
/// In practice this never runs (entities live for device lifetime) but included for correctness.
~LazyCallbackManager() { delete this->callbacks_; }
// Non-copyable and non-movable (entities are never copied or moved)
LazyCallbackManager(const LazyCallbackManager &) = delete;
LazyCallbackManager &operator=(const LazyCallbackManager &) = delete;
LazyCallbackManager(LazyCallbackManager &&) = delete;
LazyCallbackManager &operator=(LazyCallbackManager &&) = delete;
/// Add a callback to the list. Allocates the underlying CallbackManager on first use.
void add(std::function<void(Ts...)> &&callback) {
if (!this->callbacks_) {
this->callbacks_ = make_unique<CallbackManager<void(Ts...)>>();
this->callbacks_ = new CallbackManager<void(Ts...)>();
}
this->callbacks_->add(std::move(callback));
}
@@ -1376,7 +1388,7 @@ template<typename... Ts> class LazyCallbackManager<void(Ts...)> {
void operator()(Ts... args) { this->call(args...); }
protected:
std::unique_ptr<CallbackManager<void(Ts...)>> callbacks_;
CallbackManager<void(Ts...)> *callbacks_{nullptr};
};
/// Helper class to deduplicate items in a series of values.