mirror of
https://github.com/esphome/esphome.git
synced 2025-08-10 12:27:46 +00:00
avoid string copy in scheduler for const strings
This commit is contained in:
@@ -22,8 +22,17 @@ static const uint32_t MAX_LOGICALLY_DELETED_ITEMS = 10;
|
||||
// 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.
|
||||
|
||||
void HOT Scheduler::set_timeout(Component *component, const char *name, uint32_t timeout, std::function<void()> func) {
|
||||
return this->set_timeout_(component, name, timeout, func, false);
|
||||
}
|
||||
|
||||
void HOT Scheduler::set_timeout(Component *component, const std::string &name, uint32_t timeout,
|
||||
std::function<void()> func) {
|
||||
return this->set_timeout_(component, name, timeout, func, true);
|
||||
}
|
||||
|
||||
void HOT Scheduler::set_timeout_(Component *component, const std::string &name, uint32_t timeout,
|
||||
std::function<void()> func, bool make_copy) {
|
||||
const auto now = this->millis_();
|
||||
|
||||
if (!name.empty())
|
||||
@@ -34,7 +43,7 @@ void HOT Scheduler::set_timeout(Component *component, const std::string &name, u
|
||||
|
||||
auto item = make_unique<SchedulerItem>();
|
||||
item->component = component;
|
||||
item->name = name;
|
||||
item->set_name(name.c_str(), make_copy);
|
||||
item->type = SchedulerItem::TIMEOUT;
|
||||
item->next_execution_ = now + timeout;
|
||||
item->callback = std::move(func);
|
||||
@@ -49,6 +58,14 @@ bool HOT Scheduler::cancel_timeout(Component *component, const std::string &name
|
||||
}
|
||||
void HOT Scheduler::set_interval(Component *component, const std::string &name, uint32_t interval,
|
||||
std::function<void()> func) {
|
||||
this->set_interval_(component, name, interval, func, true);
|
||||
}
|
||||
void HOT Scheduler::set_interval(Component *component, const char *name, uint32_t interval,
|
||||
std::function<void()> func) {
|
||||
this->set_interval_(component, name, interval, func, false);
|
||||
}
|
||||
void HOT Scheduler::set_interval_(Component *component, const std::string &name, uint32_t interval,
|
||||
std::function<void()> func, bool make_copy) {
|
||||
const auto now = this->millis_();
|
||||
|
||||
if (!name.empty())
|
||||
@@ -64,7 +81,7 @@ void HOT Scheduler::set_interval(Component *component, const std::string &name,
|
||||
|
||||
auto item = make_unique<SchedulerItem>();
|
||||
item->component = component;
|
||||
item->name = name;
|
||||
item->set_name(name.c_str(), make_copy);
|
||||
item->type = SchedulerItem::INTERVAL;
|
||||
item->interval = interval;
|
||||
item->next_execution_ = now + offset;
|
||||
@@ -85,7 +102,7 @@ struct RetryArgs {
|
||||
uint8_t retry_countdown;
|
||||
uint32_t current_interval;
|
||||
Component *component;
|
||||
std::string name;
|
||||
std::string name; // Keep as std::string since retry uses it dynamically
|
||||
float backoff_increase_factor;
|
||||
Scheduler *scheduler;
|
||||
};
|
||||
@@ -303,14 +320,16 @@ bool HOT Scheduler::cancel_item_(Component *component, const std::string &name,
|
||||
LockGuard guard{this->lock_};
|
||||
bool ret = false;
|
||||
for (auto &it : this->items_) {
|
||||
if (it->component == component && it->name == name && it->type == type && !it->remove) {
|
||||
const char *item_name = it->get_name();
|
||||
if (it->component == component && item_name != nullptr && name == item_name && it->type == type && !it->remove) {
|
||||
to_remove_++;
|
||||
it->remove = true;
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
for (auto &it : this->to_add_) {
|
||||
if (it->component == component && it->name == name && it->type == type) {
|
||||
const char *item_name = it->get_name();
|
||||
if (it->component == component && item_name != nullptr && name == item_name && it->type == type) {
|
||||
it->remove = true;
|
||||
ret = true;
|
||||
}
|
||||
|
@@ -12,11 +12,19 @@ class Component;
|
||||
|
||||
class Scheduler {
|
||||
public:
|
||||
// Public API - accepts std::string for backward compatibility
|
||||
void set_timeout(Component *component, const std::string &name, uint32_t timeout, std::function<void()> func);
|
||||
void set_timeout(Component *component, const char *name, uint32_t timeout, std::function<void()> func);
|
||||
void set_timeout_(Component *component, const std::string &name, uint32_t timeout, std::function<void()> func,
|
||||
bool make_copy);
|
||||
|
||||
bool cancel_timeout(Component *component, const std::string &name);
|
||||
void set_interval(Component *component, const std::string &name, uint32_t interval, std::function<void()> func);
|
||||
bool cancel_interval(Component *component, const std::string &name);
|
||||
void set_interval(Component *component, const char *name, uint32_t interval, std::function<void()> func);
|
||||
void set_interval_(Component *component, const std::string &name, uint32_t interval, std::function<void()> func,
|
||||
bool make_copy);
|
||||
|
||||
bool cancel_interval(Component *component, const std::string &name);
|
||||
void set_retry(Component *component, const std::string &name, uint32_t initial_wait_time, uint8_t max_attempts,
|
||||
std::function<RetryResult(uint8_t)> func, float backoff_increase_factor = 1.0f);
|
||||
bool cancel_retry(Component *component, const std::string &name);
|
||||
@@ -36,10 +44,65 @@ class Scheduler {
|
||||
// with a 16-bit rollover counter to create a 64-bit time that won't roll over for
|
||||
// billions of years. This ensures correct scheduling even when devices run for months.
|
||||
uint64_t next_execution_;
|
||||
std::string name;
|
||||
|
||||
// Optimized name storage using tagged union
|
||||
union {
|
||||
const char *static_name; // For string literals (no allocation)
|
||||
char *dynamic_name; // For allocated strings
|
||||
} name_;
|
||||
|
||||
std::function<void()> callback;
|
||||
enum Type : uint8_t { TIMEOUT, INTERVAL } type;
|
||||
bool remove;
|
||||
|
||||
// Bit-packed fields to minimize padding
|
||||
enum Type : uint8_t { TIMEOUT, INTERVAL } type : 1;
|
||||
bool remove : 1;
|
||||
bool owns_name : 1; // True if name_.dynamic_name needs to be freed
|
||||
// 5 bits padding
|
||||
|
||||
// Constructor
|
||||
SchedulerItem()
|
||||
: component(nullptr),
|
||||
interval(0),
|
||||
next_execution_(0),
|
||||
callback(nullptr),
|
||||
type(TIMEOUT),
|
||||
remove(false),
|
||||
owns_name(false) {
|
||||
name_.static_name = nullptr;
|
||||
}
|
||||
|
||||
// Destructor to clean up dynamic names
|
||||
~SchedulerItem() {
|
||||
if (owns_name && name_.dynamic_name) {
|
||||
delete[] name_.dynamic_name;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to get the name regardless of storage type
|
||||
const char *get_name() const { return owns_name ? name_.dynamic_name : name_.static_name; }
|
||||
|
||||
// Helper to set name with proper ownership
|
||||
void set_name(const char *name, bool make_copy = false) {
|
||||
// Clean up old dynamic name if any
|
||||
if (owns_name && name_.dynamic_name) {
|
||||
delete[] name_.dynamic_name;
|
||||
}
|
||||
|
||||
if (name == nullptr || name[0] == '\0') {
|
||||
name_.static_name = nullptr;
|
||||
owns_name = false;
|
||||
} else if (make_copy) {
|
||||
// Make a copy for dynamic strings
|
||||
size_t len = strlen(name);
|
||||
name_.dynamic_name = new char[len + 1];
|
||||
strcpy(name_.dynamic_name, name);
|
||||
owns_name = true;
|
||||
} else {
|
||||
// Use static string directly
|
||||
name_.static_name = name;
|
||||
owns_name = false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool cmp(const std::unique_ptr<SchedulerItem> &a, const std::unique_ptr<SchedulerItem> &b);
|
||||
const char *get_type_str() {
|
||||
|
Reference in New Issue
Block a user