diff --git a/esphome/core/application.cpp b/esphome/core/application.cpp index ad4880b3db..2967c41e5e 100644 --- a/esphome/core/application.cpp +++ b/esphome/core/application.cpp @@ -93,6 +93,9 @@ void Application::loop() { delay_time = this->loop_interval_ - (now - this->last_loop_); uint32_t next_schedule = this->scheduler.next_schedule_in().value_or(delay_time); + // next_schedule is max 0.5*delay_time + // otherwise interval=0 schedules result in constant looping with almost no sleep + next_schedule = std::max(next_schedule, delay_time / 2); delay_time = std::min(next_schedule, delay_time); delay(delay_time); } diff --git a/esphome/core/component.cpp b/esphome/core/component.cpp index 3745478ff3..2bd47333cd 100644 --- a/esphome/core/component.cpp +++ b/esphome/core/component.cpp @@ -42,16 +42,16 @@ void Component::setup() {} void Component::loop() {} -void Component::set_interval(const std::string &name, uint32_t interval, std::function f) { // NOLINT - App.scheduler.set_interval(this, name, interval, f); +void Component::set_interval(const std::string &name, uint32_t interval, std::function &&f) { // NOLINT + App.scheduler.set_interval(this, name, interval, std::move(f)); } bool Component::cancel_interval(const std::string &name) { // NOLINT return App.scheduler.cancel_interval(this, name); } -void Component::set_timeout(const std::string &name, uint32_t timeout, std::function f) { // NOLINT - return App.scheduler.set_timeout(this, name, timeout, f); +void Component::set_timeout(const std::string &name, uint32_t timeout, std::function &&f) { // NOLINT + return App.scheduler.set_timeout(this, name, timeout, std::move(f)); } bool Component::cancel_timeout(const std::string &name) { // NOLINT @@ -82,18 +82,20 @@ void Component::mark_failed() { this->component_state_ |= COMPONENT_STATE_FAILED; this->status_set_error(); } -void Component::defer(std::function f) { App.scheduler.set_timeout(this, "", 0, f); } // NOLINT -bool Component::cancel_defer(const std::string &name) { // NOLINT +void Component::defer(std::function &&f) { // NOLINT + App.scheduler.set_timeout(this, "", 0, std::move(f)); +} +bool Component::cancel_defer(const std::string &name) { // NOLINT return App.scheduler.cancel_timeout(this, name); } -void Component::defer(const std::string &name, std::function f) { // NOLINT - App.scheduler.set_timeout(this, name, 0, f); +void Component::defer(const std::string &name, std::function &&f) { // NOLINT + App.scheduler.set_timeout(this, name, 0, std::move(f)); } -void Component::set_timeout(uint32_t timeout, std::function f) { // NOLINT - App.scheduler.set_timeout(this, "", timeout, f); +void Component::set_timeout(uint32_t timeout, std::function &&f) { // NOLINT + App.scheduler.set_timeout(this, "", timeout, std::move(f)); } -void Component::set_interval(uint32_t interval, std::function f) { // NOLINT - App.scheduler.set_timeout(this, "", interval, f); +void Component::set_interval(uint32_t interval, std::function &&f) { // NOLINT + App.scheduler.set_timeout(this, "", interval, std::move(f)); } bool Component::is_failed() { return (this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_FAILED; } bool Component::can_proceed() { return true; } diff --git a/esphome/core/component.h b/esphome/core/component.h index 6791d4b7c5..6fb05cf784 100644 --- a/esphome/core/component.h +++ b/esphome/core/component.h @@ -153,9 +153,9 @@ class Component { * * @see cancel_interval() */ - void set_interval(const std::string &name, uint32_t interval, std::function f); // NOLINT + void set_interval(const std::string &name, uint32_t interval, std::function &&f); // NOLINT - void set_interval(uint32_t interval, std::function f); // NOLINT + void set_interval(uint32_t interval, std::function &&f); // NOLINT /** Cancel an interval function. * @@ -164,7 +164,7 @@ class Component { */ bool cancel_interval(const std::string &name); // NOLINT - void set_timeout(uint32_t timeout, std::function f); // NOLINT + void set_timeout(uint32_t timeout, std::function &&f); // NOLINT /** Set a timeout function with a unique name. * @@ -180,7 +180,7 @@ class Component { * * @see cancel_timeout() */ - void set_timeout(const std::string &name, uint32_t timeout, std::function f); // NOLINT + void set_timeout(const std::string &name, uint32_t timeout, std::function &&f); // NOLINT /** Cancel a timeout function. * @@ -196,10 +196,10 @@ class Component { * @param name The name of the defer function. * @param f The callback. */ - void defer(const std::string &name, std::function f); // NOLINT + void defer(const std::string &name, std::function &&f); // NOLINT /// Defer a callback to the next loop() call. - void defer(std::function f); // NOLINT + void defer(std::function &&f); // NOLINT /// Cancel a defer callback using the specified name, name must not be empty. bool cancel_defer(const std::string &name); // NOLINT diff --git a/esphome/core/scheduler.cpp b/esphome/core/scheduler.cpp index 7e9e8f96a9..bc36e14a84 100644 --- a/esphome/core/scheduler.cpp +++ b/esphome/core/scheduler.cpp @@ -7,7 +7,7 @@ namespace esphome { static const char *TAG = "scheduler"; -void HOT Scheduler::set_timeout(Component *component, const std::string &name, uint32_t timeout, std::function func) { +void HOT Scheduler::set_timeout(Component *component, const std::string &name, uint32_t timeout, std::function &&func) { const uint32_t now = millis(); if (!name.empty()) @@ -21,7 +21,7 @@ void HOT Scheduler::set_timeout(Component *component, const std::string &name, u item.type = SchedulerItem::TIMEOUT; item.timeout = timeout; item.last_execution = now; - item.f = func; + item.f = std::move(func); item.remove = false; this->push_(item); } @@ -31,7 +31,7 @@ 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 func) { + std::function &&func) { const uint32_t now = millis(); // only put offset in lower half @@ -50,7 +50,7 @@ void HOT Scheduler::set_interval(Component *component, item.type = SchedulerItem::INTERVAL; item.interval = interval; item.last_execution = now - offset; - item.f = func; + item.f = std::move(func); item.remove = false; this->push_(item); } @@ -58,11 +58,11 @@ bool HOT Scheduler::cancel_interval(Component *component, const std::string &nam return this->cancel_item_(component, name, SchedulerItem::INTERVAL); } optional HOT Scheduler::next_schedule_in() { - auto item = this->peek_(); - if (!item.has_value()) + if (!this->peek_()) return {}; + auto &item = this->items_[0]; const uint32_t now = millis(); - uint32_t next_time = item->last_execution + item->interval; + uint32_t next_time = item.last_execution + item.interval; if (next_time < now) return 0; return next_time - now; @@ -72,25 +72,36 @@ void ICACHE_RAM_ATTR HOT Scheduler::call() { this->process_to_add_(); while (true) { - auto item = this->peek_(); - if (!item.has_value() || (now - item->last_execution) < item->interval) + bool has_item = this->peek_(); + if (!has_item) break; + // Don't copy-by value yet + auto &item_ref = this->items_[0]; + if ((now - item_ref.last_execution) < item_ref.interval) + break; + + auto item = this->items_[0]; this->pop_(); + + // Don't run failed components + if (item.component->is_failed()) + continue; + #ifdef ESPHOME_LOG_HAS_VERY_VERBOSE const char *type = - item->type == SchedulerItem::INTERVAL ? "interval" : "timeout"; - ESP_LOGVV(TAG, "Running %s '%s' with interval=%u last_execution=%u (now=%u)", type, item->name.c_str(), - item->interval, item->last_execution, now); + item.type == SchedulerItem::INTERVAL ? "interval" : "timeout"; + ESP_LOGVV(TAG, "Running %s '%s' with interval=%u last_execution=%u (now=%u)", type, item.name.c_str(), + item.interval, item.last_execution, now); #endif - item->f(); - if (item->type == SchedulerItem::INTERVAL) { - if (item->interval != 0) { - const uint32_t amount = (now - item->last_execution) / item->interval; - item->last_execution += amount * item->interval; + item.f(); + if (item.type == SchedulerItem::INTERVAL) { + if (item.interval != 0) { + const uint32_t amount = (now - item.last_execution) / item.interval; + item.last_execution += amount * item.interval; } - this->push_(*item); + this->push_(item); } } @@ -114,23 +125,22 @@ void HOT Scheduler::cleanup_() { this->pop_raw_(); } } -optional HOT Scheduler::peek_() { +bool HOT Scheduler::peek_() { this->cleanup_(); if (this->items_.empty()) - return {}; - return this->items_[0]; + return false; + return true; } -optional HOT Scheduler::pop_() { +bool HOT Scheduler::pop_() { this->cleanup_(); if (this->items_.empty()) - return {}; - return this->pop_raw_(); + return false; + return true; } -Scheduler::SchedulerItem HOT Scheduler::pop_raw_() { +void HOT Scheduler::pop_raw_() { std::pop_heap(this->items_.begin(), this->items_.end()); auto item = this->items_.back(); this->items_.pop_back(); - return item; } void HOT Scheduler::push_(const Scheduler::SchedulerItem &item) { this->to_add_.push_back(item); @@ -141,15 +151,11 @@ bool HOT Scheduler::cancel_item_(Component *component, const std::string &name, if (it.component == component && it.name == name && it.type == type) { it.remove = true; ret = true; - if (!name.empty()) - return true; } for (auto &it : this->to_add_) if (it.component == component && it.name == name && it.type == type) { it.remove = true; ret = true; - if (!name.empty()) - return true; } return ret; diff --git a/esphome/core/scheduler.h b/esphome/core/scheduler.h index 351ffd6707..99aa1df240 100644 --- a/esphome/core/scheduler.h +++ b/esphome/core/scheduler.h @@ -9,9 +9,9 @@ class Component; class Scheduler { public: - void set_timeout(Component *component, const std::string &name, uint32_t timeout, std::function func); + void set_timeout(Component *component, const std::string &name, uint32_t timeout, std::function &&func); bool cancel_timeout(Component *component, const std::string &name); - void set_interval(Component *component, const std::string &name, uint32_t interval, std::function func); + void set_interval(Component *component, const std::string &name, uint32_t interval, std::function &&func); bool cancel_interval(Component *component, const std::string &name); optional next_schedule_in(); @@ -36,9 +36,9 @@ class Scheduler { void process_to_add_(); void cleanup_(); - optional peek_(); - optional pop_(); - SchedulerItem pop_raw_(); + bool peek_(); + bool pop_(); + void pop_raw_(); void push_(const SchedulerItem& item); bool cancel_item_(Component *component, const std::string &name, SchedulerItem::Type type);