mirror of
https://github.com/esphome/esphome.git
synced 2025-09-22 13:12:22 +01: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
|
// 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.
|
// 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,
|
void HOT Scheduler::set_timeout(Component *component, const std::string &name, uint32_t timeout,
|
||||||
std::function<void()> func) {
|
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_();
|
const auto now = this->millis_();
|
||||||
|
|
||||||
if (!name.empty())
|
if (!name.empty())
|
||||||
@@ -34,7 +43,7 @@ void HOT Scheduler::set_timeout(Component *component, const std::string &name, u
|
|||||||
|
|
||||||
auto item = make_unique<SchedulerItem>();
|
auto item = make_unique<SchedulerItem>();
|
||||||
item->component = component;
|
item->component = component;
|
||||||
item->name = name;
|
item->set_name(name.c_str(), make_copy);
|
||||||
item->type = SchedulerItem::TIMEOUT;
|
item->type = SchedulerItem::TIMEOUT;
|
||||||
item->next_execution_ = now + timeout;
|
item->next_execution_ = now + timeout;
|
||||||
item->callback = std::move(func);
|
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,
|
void HOT Scheduler::set_interval(Component *component, const std::string &name, uint32_t interval,
|
||||||
std::function<void()> func) {
|
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_();
|
const auto now = this->millis_();
|
||||||
|
|
||||||
if (!name.empty())
|
if (!name.empty())
|
||||||
@@ -64,7 +81,7 @@ void HOT Scheduler::set_interval(Component *component, const std::string &name,
|
|||||||
|
|
||||||
auto item = make_unique<SchedulerItem>();
|
auto item = make_unique<SchedulerItem>();
|
||||||
item->component = component;
|
item->component = component;
|
||||||
item->name = name;
|
item->set_name(name.c_str(), make_copy);
|
||||||
item->type = SchedulerItem::INTERVAL;
|
item->type = SchedulerItem::INTERVAL;
|
||||||
item->interval = interval;
|
item->interval = interval;
|
||||||
item->next_execution_ = now + offset;
|
item->next_execution_ = now + offset;
|
||||||
@@ -85,7 +102,7 @@ struct RetryArgs {
|
|||||||
uint8_t retry_countdown;
|
uint8_t retry_countdown;
|
||||||
uint32_t current_interval;
|
uint32_t current_interval;
|
||||||
Component *component;
|
Component *component;
|
||||||
std::string name;
|
std::string name; // Keep as std::string since retry uses it dynamically
|
||||||
float backoff_increase_factor;
|
float backoff_increase_factor;
|
||||||
Scheduler *scheduler;
|
Scheduler *scheduler;
|
||||||
};
|
};
|
||||||
@@ -303,14 +320,16 @@ bool HOT Scheduler::cancel_item_(Component *component, const std::string &name,
|
|||||||
LockGuard guard{this->lock_};
|
LockGuard guard{this->lock_};
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
for (auto &it : this->items_) {
|
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_++;
|
to_remove_++;
|
||||||
it->remove = true;
|
it->remove = true;
|
||||||
ret = true;
|
ret = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (auto &it : this->to_add_) {
|
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;
|
it->remove = true;
|
||||||
ret = true;
|
ret = true;
|
||||||
}
|
}
|
||||||
|
@@ -12,11 +12,19 @@ class Component;
|
|||||||
|
|
||||||
class Scheduler {
|
class Scheduler {
|
||||||
public:
|
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 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);
|
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);
|
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,
|
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);
|
std::function<RetryResult(uint8_t)> func, float backoff_increase_factor = 1.0f);
|
||||||
bool cancel_retry(Component *component, const std::string &name);
|
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
|
// 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.
|
// billions of years. This ensures correct scheduling even when devices run for months.
|
||||||
uint64_t next_execution_;
|
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;
|
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);
|
static bool cmp(const std::unique_ptr<SchedulerItem> &a, const std::unique_ptr<SchedulerItem> &b);
|
||||||
const char *get_type_str() {
|
const char *get_type_str() {
|
||||||
|
Reference in New Issue
Block a user