15static const char *
const TAG =
"scheduler";
19static constexpr uint32_t MAX_LOGICALLY_DELETED_ITEMS = 5;
21static constexpr uint32_t MAX_INTERVAL_DELAY = 5000;
23#if defined(ESPHOME_LOG_HAS_VERBOSE) || defined(ESPHOME_DEBUG_SCHEDULER)
27struct SchedulerNameLog {
34 const char *
format(Scheduler::NameType name_type,
const char *static_name,
uint32_t hash_or_id) {
35 using NameType = Scheduler::NameType;
36 if (name_type == NameType::STATIC_STRING) {
40 ESPHOME_strncpy_P(buffer, ESPHOME_PSTR(
"(null)"),
sizeof(buffer));
42 }
else if (name_type == NameType::HASHED_STRING) {
43 ESPHOME_snprintf_P(buffer,
sizeof(buffer), ESPHOME_PSTR(
"hash:0x%08" PRIX32), hash_or_id);
45 }
else if (name_type == NameType::NUMERIC_ID) {
46 ESPHOME_snprintf_P(buffer,
sizeof(buffer), ESPHOME_PSTR(
"id:%" PRIu32), hash_or_id);
48 }
else if (name_type == NameType::NUMERIC_ID_INTERNAL) {
49 ESPHOME_snprintf_P(buffer,
sizeof(buffer), ESPHOME_PSTR(
"iid:%" PRIu32), hash_or_id);
54 ESPHOME_snprintf_P(buffer,
sizeof(buffer), ESPHOME_PSTR(
"self:%p"),
55 const_cast<void *
>(
static_cast<const void *
>(static_name)));
65#ifdef ESPHOME_DEBUG_SCHEDULER
67static void validate_static_string(
const char *name) {
73 uintptr_t addr =
reinterpret_cast<uintptr_t
>(name);
77 uintptr_t stack_addr =
reinterpret_cast<uintptr_t
>(&stack_var);
81 if (addr > (stack_addr - 0x2000) && addr < (stack_addr + 0x2000)) {
83 "WARNING: Scheduler name '%s' at %p appears to be on the stack - this is unsafe!\n"
84 " Stack reference at %p",
85 name, name, &stack_var);
90 static const char *static_str =
"test";
91 uintptr_t static_addr =
reinterpret_cast<uintptr_t
>(static_str);
94 if (addr > static_addr + 0x100000 || (static_addr > 0x100000 && addr < static_addr - 0x100000)) {
95 ESP_LOGW(TAG,
"WARNING: Scheduler name '%s' at %p might be on heap (static ref at %p)", name, name, static_str);
108 uint32_t max_offset = std::min(
delay / 2, MAX_INTERVAL_DELAY);
116bool Scheduler::is_retry_cancelled_locked_(Component *
component, NameType name_type,
const char *static_name,
118 for (
auto *container : {&this->items_, &this->to_add_}) {
119 for (
auto *item : *container) {
120 if (item !=
nullptr && this->is_item_removed_locked_(item) &&
121 this->matches_item_locked_(item,
component, name_type, static_name, hash_or_id, SchedulerItem::TIMEOUT,
132void HOT Scheduler::set_timer_common_(Component *
component, SchedulerItem::Type
type, NameType name_type,
134 std::function<
void()> &&func,
bool is_retry,
bool skip_cancel) {
138 LockGuard guard{this->lock_};
139 this->cancel_item_locked_(
component, name_type, static_name, hash_or_id,
type,
false,
152 if (
type == SchedulerItem::INTERVAL &&
delay == 0) [[unlikely]] {
153 ESP_LOGE(TAG,
"[%s] set_interval(0) would spin main loop - coercing to 1ms (use HighFrequencyLoopRequester)",
159 LockGuard guard{this->lock_};
164 if (is_retry &&
delay != 0 && (name_type != NameType::STATIC_STRING || static_name !=
nullptr) &&
165 type == SchedulerItem::TIMEOUT &&
166 this->is_retry_cancelled_locked_(
component, name_type, static_name, hash_or_id)) {
167#ifdef ESPHOME_DEBUG_SCHEDULER
168 SchedulerNameLog skip_name_log;
169 ESP_LOGD(TAG,
"Skipping retry '%s' - found cancelled item",
170 skip_name_log.format(name_type, static_name, hash_or_id));
176 SchedulerItem *item = this->get_item_from_pool_locked_();
178 item->set_name(name_type, static_name, hash_or_id);
185 item->callback.~function();
186 new (&item->callback) std::function<
void()>(std::move(func));
188 this->set_item_removed_(item,
false);
189 item->is_retry = is_retry;
193 auto *target = &this->to_add_;
195#ifndef ESPHOME_THREAD_SINGLE
198 if (
delay == 0 &&
type == SchedulerItem::TIMEOUT) {
200 target = &this->defer_queue_;
208 if (
type == SchedulerItem::INTERVAL) {
209 item->interval =
delay;
212 item->set_next_execution(now_64 + offset);
213#ifdef ESPHOME_LOG_HAS_VERBOSE
214 SchedulerNameLog name_log;
215 ESP_LOGV(TAG,
"Scheduler interval for %s is %" PRIu32
"ms, offset %" PRIu32
"ms",
216 name_log.format(name_type, static_name, hash_or_id),
delay, offset);
220 item->set_next_execution(now_64 +
delay);
223#ifdef ESPHOME_DEBUG_SCHEDULER
224 this->debug_log_timer_(item, name_type, static_name, hash_or_id,
type,
delay, now_64);
230 if (!skip_cancel && (name_type != NameType::STATIC_STRING || static_name !=
nullptr)) {
231 this->cancel_item_locked_(
component, name_type, static_name, hash_or_id,
type,
false,
234 target->push_back(item);
235 if (target == &this->to_add_) {
236 this->to_add_count_increment_locked_();
238#ifndef ESPHOME_THREAD_SINGLE
240 this->defer_count_increment_locked_();
245void HOT Scheduler::set_timeout(Component *
component,
const char *name,
uint32_t timeout,
246 std::function<
void()> &&func) {
247 this->set_timer_common_(
component, SchedulerItem::TIMEOUT, NameType::STATIC_STRING, name, 0, timeout,
251void HOT Scheduler::set_timeout(Component *
component,
const std::string &name,
uint32_t timeout,
252 std::function<
void()> &&func) {
253 this->set_timer_common_(
component, SchedulerItem::TIMEOUT, NameType::HASHED_STRING,
nullptr,
fnv1a_hash(name),
254 timeout, std::move(func));
257 this->set_timer_common_(
component, SchedulerItem::TIMEOUT, NameType::NUMERIC_ID,
nullptr,
id, timeout,
260bool HOT Scheduler::cancel_timeout(Component *
component,
const std::string &name) {
261 return this->cancel_item_(
component, NameType::HASHED_STRING,
nullptr,
fnv1a_hash(name), SchedulerItem::TIMEOUT);
263bool HOT Scheduler::cancel_timeout(Component *
component,
const char *name) {
264 return this->cancel_item_(
component, NameType::STATIC_STRING, name, 0, SchedulerItem::TIMEOUT);
267 return this->cancel_item_(
component, NameType::NUMERIC_ID,
nullptr,
id, SchedulerItem::TIMEOUT);
269void HOT Scheduler::set_interval(Component *
component,
const std::string &name,
uint32_t interval,
270 std::function<
void()> &&func) {
271 this->set_timer_common_(
component, SchedulerItem::INTERVAL, NameType::HASHED_STRING,
nullptr,
fnv1a_hash(name),
272 interval, std::move(func));
275void HOT Scheduler::set_interval(Component *
component,
const char *name,
uint32_t interval,
276 std::function<
void()> &&func) {
277 this->set_timer_common_(
component, SchedulerItem::INTERVAL, NameType::STATIC_STRING, name, 0, interval,
281 this->set_timer_common_(
component, SchedulerItem::INTERVAL, NameType::NUMERIC_ID,
nullptr,
id, interval,
284bool HOT Scheduler::cancel_interval(Component *
component,
const std::string &name) {
285 return this->cancel_item_(
component, NameType::HASHED_STRING,
nullptr,
fnv1a_hash(name), SchedulerItem::INTERVAL);
287bool HOT Scheduler::cancel_interval(Component *
component,
const char *name) {
288 return this->cancel_item_(
component, NameType::STATIC_STRING, name, 0, SchedulerItem::INTERVAL);
291 return this->cancel_item_(
component, NameType::NUMERIC_ID,
nullptr,
id, SchedulerItem::INTERVAL);
298void HOT Scheduler::set_timeout(
const void *self,
uint32_t timeout, std::function<
void()> &&func) {
299 this->set_timer_common_(
nullptr, SchedulerItem::TIMEOUT, NameType::SELF_POINTER,
static_cast<const char *
>(self), 0,
300 timeout, std::move(func));
302void HOT Scheduler::set_interval(
const void *self,
uint32_t interval, std::function<
void()> &&func) {
303 this->set_timer_common_(
nullptr, SchedulerItem::INTERVAL, NameType::SELF_POINTER,
static_cast<const char *
>(self), 0,
304 interval, std::move(func));
306bool HOT Scheduler::cancel_timeout(
const void *self) {
307 return this->cancel_item_(
nullptr, NameType::SELF_POINTER,
static_cast<const char *
>(self), 0,
308 SchedulerItem::TIMEOUT);
310bool HOT Scheduler::cancel_interval(
const void *self) {
311 return this->cancel_item_(
nullptr, NameType::SELF_POINTER,
static_cast<const char *
>(self), 0,
312 SchedulerItem::INTERVAL);
317#pragma GCC diagnostic push
318#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
324 Scheduler *scheduler;
327 const char *static_name;
331 float backoff_increase_factor;
332 Scheduler::NameType name_type;
333 uint8_t retry_countdown;
343 const char *static_name = (
args->name_type == Scheduler::NameType::STATIC_STRING) ?
args->name_.static_name :
nullptr;
344 uint32_t hash_or_id = (
args->name_type != Scheduler::NameType::STATIC_STRING) ?
args->name_.hash_or_id : 0;
345 args->scheduler->set_timer_common_(
346 args->component, Scheduler::SchedulerItem::TIMEOUT,
args->name_type, static_name, hash_or_id,
347 args->current_interval, [
args]() { retry_handler(args); },
350 args->current_interval *=
args->backoff_increase_factor;
353void HOT Scheduler::set_retry_common_(Component *
component, NameType name_type,
const char *static_name,
355 std::function<
RetryResult(uint8_t)> func,
float backoff_increase_factor) {
356 this->cancel_retry_(
component, name_type, static_name, hash_or_id);
361#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
363 SchedulerNameLog name_log;
364 ESP_LOGVV(TAG,
"set_retry(name='%s', initial_wait_time=%" PRIu32
", max_attempts=%u, backoff_factor=%0.1f)",
365 name_log.format(name_type, static_name, hash_or_id), initial_wait_time, max_attempts,
366 backoff_increase_factor);
370 if (backoff_increase_factor < 0.0001) {
371 ESP_LOGE(TAG,
"set_retry: backoff_factor %0.1f too small, using 1.0: %s", backoff_increase_factor,
372 (name_type == NameType::STATIC_STRING && static_name) ? static_name :
"");
373 backoff_increase_factor = 1;
376 auto args = std::make_shared<RetryArgs>();
377 args->func = std::move(func);
379 args->scheduler =
this;
380 args->name_type = name_type;
381 if (name_type == NameType::STATIC_STRING) {
382 args->name_.static_name = static_name;
384 args->name_.hash_or_id = hash_or_id;
386 args->current_interval = initial_wait_time;
388 args->retry_countdown = max_attempts;
391 this->set_timer_common_(
396void HOT Scheduler::set_retry(Component *
component,
const char *name,
uint32_t initial_wait_time, uint8_t max_attempts,
397 std::function<
RetryResult(uint8_t)> func,
float backoff_increase_factor) {
398 this->set_retry_common_(
component, NameType::STATIC_STRING, name, 0, initial_wait_time, max_attempts, std::move(func),
399 backoff_increase_factor);
402bool HOT Scheduler::cancel_retry_(Component *
component, NameType name_type,
const char *static_name,
404 return this->cancel_item_(
component, name_type, static_name, hash_or_id, SchedulerItem::TIMEOUT,
407bool HOT Scheduler::cancel_retry(Component *
component,
const char *name) {
408 return this->cancel_retry_(
component, NameType::STATIC_STRING, name, 0);
411void HOT Scheduler::set_retry(Component *
component,
const std::string &name,
uint32_t initial_wait_time,
412 uint8_t max_attempts, std::function<
RetryResult(uint8_t)> func,
413 float backoff_increase_factor) {
414 this->set_retry_common_(
component, NameType::HASHED_STRING,
nullptr,
fnv1a_hash(name), initial_wait_time,
415 max_attempts, std::move(func), backoff_increase_factor);
418bool HOT Scheduler::cancel_retry(Component *
component,
const std::string &name) {
423 std::function<
RetryResult(uint8_t)> func,
float backoff_increase_factor) {
424 this->set_retry_common_(
component, NameType::NUMERIC_ID,
nullptr,
id, initial_wait_time, max_attempts,
425 std::move(func), backoff_increase_factor);
429 return this->cancel_retry_(
component, NameType::NUMERIC_ID,
nullptr,
id);
432#pragma GCC diagnostic pop
434optional<uint32_t> HOT Scheduler::next_schedule_in(
uint32_t now) {
444#ifndef ESPHOME_THREAD_SINGLE
448 if (!this->defer_empty_())
454 if (!this->to_add_empty_())
459 if (!this->cleanup_())
462 SchedulerItem *item = this->items_[0];
463 const auto now_64 = this->millis_64_from_(now);
464 const uint64_t next_exec = item->get_next_execution();
465 if (next_exec < now_64)
467 return next_exec - now_64;
470void Scheduler::full_cleanup_removed_items_() {
476 LockGuard guard{this->lock_};
480 for (
size_t read = 0; read < this->items_.size(); ++read) {
481 if (!is_item_removed_locked_(this->items_[read])) {
483 this->items_[write] = this->items_[read];
487 this->recycle_item_main_loop_(this->items_[read]);
490 this->items_.erase(this->items_.begin() + write, this->items_.end());
492 std::make_heap(this->items_.begin(), this->items_.end(), SchedulerItem::cmp);
493 this->to_remove_clear_locked_();
496#ifndef ESPHOME_THREAD_SINGLE
497void Scheduler::compact_defer_queue_locked_() {
509 size_t remaining = this->defer_queue_.size() - this->defer_queue_front_;
510 for (
size_t i = 0; i < remaining; i++) {
511 this->defer_queue_[i] = this->defer_queue_[this->defer_queue_front_ + i];
515 this->defer_queue_.erase(this->defer_queue_.begin() + remaining, this->defer_queue_.end());
517void HOT Scheduler::process_defer_queue_slow_path_(
uint32_t &now) {
542 this->defer_count_clear_locked_();
543 size_t defer_queue_end = this->defer_queue_.size();
544 if (this->defer_queue_front_ >= defer_queue_end) {
545 this->lock_.unlock();
548 while (this->defer_queue_front_ < defer_queue_end) {
554 item = this->defer_queue_[this->defer_queue_front_];
555 this->defer_queue_[this->defer_queue_front_] =
nullptr;
556 this->defer_queue_front_++;
557 this->lock_.unlock();
561 if (!this->should_skip_item_(item)) {
562 now = this->execute_item_(item, now);
566 this->recycle_item_main_loop_(item);
569 this->cleanup_defer_queue_locked_();
570 this->lock_.unlock();
575#ifndef ESPHOME_THREAD_SINGLE
576 this->process_defer_queue_(now);
580 const auto now_64 = this->millis_64_from_(now);
581 this->process_to_add();
584 bool has_added_items =
false;
586#ifdef ESPHOME_DEBUG_SCHEDULER
587 static uint64_t last_print = 0;
589 if (now_64 - last_print > 2000) {
591 std::vector<SchedulerItem *> old_items;
592 ESP_LOGD(TAG,
"Items: count=%zu, pool=%zu, now=%" PRIu64, this->items_.size(), this->scheduler_item_pool_size_,
596 while (!this->items_.empty()) {
599 LockGuard guard{this->lock_};
600 item = this->pop_raw_locked_();
603 SchedulerNameLog name_log;
604 bool is_cancelled = is_item_removed_(item);
605 ESP_LOGD(TAG,
" %s '%s/%s' interval=%" PRIu32
" next_execution in %" PRIu64
"ms at %" PRIu64
"%s",
606 item->get_type_str(), LOG_STR_ARG(item->get_source()),
607 name_log.format(item->get_name_type(), item->get_name(), item->get_name_hash_or_id()), item->interval,
608 item->get_next_execution() - now_64, item->get_next_execution(), is_cancelled ?
" [CANCELLED]" :
"");
610 old_items.push_back(item);
615 LockGuard guard{this->lock_};
616 this->items_ = std::move(old_items);
618 std::make_heap(this->items_.begin(), this->items_.end(), SchedulerItem::cmp);
629 if (this->to_remove_count_() >= MAX_LOGICALLY_DELETED_ITEMS) {
630 this->full_cleanup_removed_items_();
638 while (!this->items_.empty()) {
640 SchedulerItem *item = this->items_[0];
641 if (item->get_next_execution() > now_64) {
646 if (item->component !=
nullptr && item->component->is_failed()) {
647 LockGuard guard{this->lock_};
648 this->recycle_item_main_loop_(this->pop_raw_locked_());
656#ifdef ESPHOME_THREAD_MULTI_NO_ATOMICS
659 LockGuard guard{this->lock_};
660 if (is_item_removed_locked_(item)) {
661 this->recycle_item_main_loop_(this->pop_raw_locked_());
662 this->to_remove_decrement_locked_();
668 if (is_item_removed_(item)) {
669 LockGuard guard{this->lock_};
670 this->recycle_item_main_loop_(this->pop_raw_locked_());
671 this->to_remove_decrement_locked_();
676#ifdef ESPHOME_DEBUG_SCHEDULER
678 SchedulerNameLog name_log;
679 ESP_LOGV(TAG,
"Running %s '%s/%s' with interval=%" PRIu32
" next_execution=%" PRIu64
" (now=%" PRIu64
")",
680 item->get_type_str(), LOG_STR_ARG(item->get_source()),
681 name_log.format(item->get_name_type(), item->get_name(), item->get_name_hash_or_id()), item->interval,
682 item->get_next_execution(), now_64);
689 now = this->execute_item_(item, now);
691 LockGuard guard{this->lock_};
695 SchedulerItem *executed_item = this->pop_raw_locked_();
697 if (this->is_item_removed_locked_(executed_item)) {
699 this->to_remove_decrement_locked_();
700 this->recycle_item_main_loop_(executed_item);
704 if (executed_item->type == SchedulerItem::INTERVAL) {
705 executed_item->set_next_execution(now_64 + executed_item->interval);
716 this->items_.push_back(executed_item);
717 std::push_heap(this->items_.begin(), this->items_.end(), SchedulerItem::cmp);
720 this->recycle_item_main_loop_(executed_item);
723 has_added_items |= !this->to_add_.empty();
726 if (has_added_items) {
727 this->process_to_add();
730#ifdef ESPHOME_DEBUG_SCHEDULER
740 LockGuard guard{this->lock_};
741 this->debug_verify_no_leak_();
748void HOT Scheduler::process_to_add_slow_path_() {
749 LockGuard guard{this->lock_};
750 for (
auto *&it : this->to_add_) {
751 if (is_item_removed_locked_(it)) {
753 this->recycle_item_main_loop_(it);
758 this->items_.push_back(it);
759 std::push_heap(this->items_.begin(), this->items_.end(), SchedulerItem::cmp);
761 this->to_add_.clear();
762 this->to_add_count_clear_locked_();
764bool HOT Scheduler::cleanup_slow_path_() {
773 LockGuard guard{this->lock_};
774 while (!this->items_.empty()) {
775 SchedulerItem *item = this->items_[0];
776 if (!this->is_item_removed_locked_(item))
778 this->to_remove_decrement_locked_();
779 this->recycle_item_main_loop_(this->pop_raw_locked_());
781 return !this->items_.empty();
783Scheduler::SchedulerItem *HOT Scheduler::pop_raw_locked_() {
784 std::pop_heap(this->items_.begin(), this->items_.end(), SchedulerItem::cmp);
786 SchedulerItem *item = this->items_.back();
787 this->items_.pop_back();
796 WarnIfComponentBlockingGuard guard{item->component, now};
808bool HOT Scheduler::cancel_item_(Component *
component, NameType name_type,
const char *static_name,
uint32_t hash_or_id,
809 SchedulerItem::Type
type,
bool match_retry) {
810 LockGuard guard{this->lock_};
813 return this->cancel_item_locked_(
component, name_type, static_name, hash_or_id,
type, match_retry);
822size_t Scheduler::mark_matching_items_removed_slow_locked_(std::vector<SchedulerItem *> &container,
823 Component *
component, NameType name_type,
824 const char *static_name,
uint32_t hash_or_id,
825 SchedulerItem::Type
type,
bool match_retry,
828 for (
auto *item : container) {
829 if (this->matches_item_locked_(item,
component, name_type, static_name, hash_or_id,
type, match_retry)) {
830 this->set_item_removed_(item,
true);
839bool HOT Scheduler::cancel_item_locked_(Component *
component, NameType name_type,
const char *static_name,
840 uint32_t hash_or_id, SchedulerItem::Type
type,
bool match_retry,
843 if (name_type == NameType::STATIC_STRING && static_name ==
nullptr) {
847 size_t total_cancelled = 0;
849#ifndef ESPHOME_THREAD_SINGLE
851 if (
type == SchedulerItem::TIMEOUT) {
852 total_cancelled += this->mark_matching_items_removed_locked_(this->defer_queue_,
component, name_type, static_name,
853 hash_or_id,
type, match_retry, find_first);
854 if (find_first && total_cancelled > 0)
865 size_t heap_cancelled = this->mark_matching_items_removed_locked_(this->items_,
component, name_type, static_name,
866 hash_or_id,
type, match_retry, find_first);
867 total_cancelled += heap_cancelled;
868 this->to_remove_add_locked_(heap_cancelled);
869 if (find_first && total_cancelled > 0)
874 total_cancelled += this->mark_matching_items_removed_locked_(this->to_add_,
component, name_type, static_name,
875 hash_or_id,
type, match_retry, find_first);
877 return total_cancelled > 0;
880bool HOT Scheduler::SchedulerItem::cmp(SchedulerItem *a, SchedulerItem *b) {
883 return (a->next_execution_high_ ==
b->next_execution_high_) ? (a->next_execution_low_ >
b->next_execution_low_)
884 : (a->next_execution_high_ >
b->next_execution_high_);
889void Scheduler::recycle_item_main_loop_(SchedulerItem *item) {
893 item->callback =
nullptr;
894 item->next_free = this->scheduler_item_pool_head_;
895 this->scheduler_item_pool_head_ = item;
896 this->scheduler_item_pool_size_++;
897#ifdef ESPHOME_DEBUG_SCHEDULER
898 ESP_LOGD(TAG,
"Recycled item to pool (pool size now: %zu)", this->scheduler_item_pool_size_);
910void __attribute__((noinline)) Scheduler::shrink_scheduler_vector_(std::vector<SchedulerItem *> *v) {
911 if (v->capacity() == v->size())
913 std::vector<SchedulerItem *> tmp;
914 tmp.reserve(v->size());
915 for (SchedulerItem *p : *v)
920void Scheduler::trim_freelist() {
921 LockGuard guard{this->lock_};
922 SchedulerItem *item = this->scheduler_item_pool_head_;
924 while (item !=
nullptr) {
925 SchedulerItem *next = item->next_free;
927#ifdef ESPHOME_DEBUG_SCHEDULER
928 this->debug_live_items_--;
933 this->scheduler_item_pool_head_ =
nullptr;
934 this->scheduler_item_pool_size_ = 0;
938 shrink_scheduler_vector_(&this->items_);
939 shrink_scheduler_vector_(&this->to_add_);
940#ifndef ESPHOME_THREAD_SINGLE
941 shrink_scheduler_vector_(&this->defer_queue_);
944#ifdef ESPHOME_DEBUG_SCHEDULER
945 ESP_LOGD(TAG,
"Freelist trimmed (%zu items freed)", freed);
951#ifdef ESPHOME_DEBUG_SCHEDULER
952void Scheduler::debug_log_timer_(
const SchedulerItem *item, NameType name_type,
const char *static_name,
955 if (name_type == NameType::STATIC_STRING && static_name !=
nullptr) {
956 validate_static_string(static_name);
960 SchedulerNameLog name_log;
961 const char *type_str = (
type == SchedulerItem::TIMEOUT) ?
"timeout" :
"interval";
962 if (
type == SchedulerItem::TIMEOUT) {
963 ESP_LOGD(TAG,
"set_%s(name='%s/%s', %s=%" PRIu32
")", type_str, LOG_STR_ARG(item->get_source()),
964 name_log.format(name_type, static_name, hash_or_id), type_str,
delay);
966 ESP_LOGD(TAG,
"set_%s(name='%s/%s', %s=%" PRIu32
", offset=%" PRIu32
")", type_str, LOG_STR_ARG(item->get_source()),
967 name_log.format(name_type, static_name, hash_or_id), type_str,
delay,
968 static_cast<uint32_t>(item->get_next_execution() - now));
975Scheduler::SchedulerItem *Scheduler::get_item_from_pool_locked_() {
976 if (this->scheduler_item_pool_head_ !=
nullptr) {
977 SchedulerItem *item = this->scheduler_item_pool_head_;
978 this->scheduler_item_pool_head_ = item->next_free;
979 this->scheduler_item_pool_size_--;
980#ifdef ESPHOME_DEBUG_SCHEDULER
981 ESP_LOGD(TAG,
"Reused item from pool (pool size now: %zu)", this->scheduler_item_pool_size_);
985#ifdef ESPHOME_DEBUG_SCHEDULER
986 ESP_LOGD(TAG,
"Allocated new item (pool empty)");
988 auto *item =
new SchedulerItem();
989#ifdef ESPHOME_DEBUG_SCHEDULER
990 this->debug_live_items_++;
995#ifdef ESPHOME_DEBUG_SCHEDULER
996bool Scheduler::debug_verify_no_leak_()
const {
999 size_t accounted = this->items_.size() + this->to_add_.size() + this->scheduler_item_pool_size_;
1000#ifndef ESPHOME_THREAD_SINGLE
1001 accounted += this->defer_queue_.size();
1003 if (accounted != this->debug_live_items_) {
1005 "SCHEDULER LEAK DETECTED: live=%" PRIu32
" but accounted=%" PRIu32
" (items=%" PRIu32
" to_add=%" PRIu32
1007#ifndef ESPHOME_THREAD_SINGLE
1011 static_cast<uint32_t>(this->debug_live_items_),
static_cast<uint32_t>(accounted),
1012 static_cast<uint32_t>(this->items_.size()),
static_cast<uint32_t>(this->to_add_.size()),
1013 static_cast<uint32_t>(this->scheduler_item_pool_size_)
1014#ifndef ESPHOME_THREAD_SINGLE
1016 static_cast<uint32_t>(this->defer_queue_.size())
void set_loop_component_start_time_(uint32_t now)
Freshen the cached loop component start time. Called by Scheduler before each dispatch.
void ESPHOME_ALWAYS_INLINE feed_wdt_with_time(uint32_t time)
Feed the task watchdog, hot entry.
void set_current_component(Component *component)
const LogString * get_component_log_str() const ESPHOME_ALWAYS_INLINE
Get the integration where this component was declared as a LogString for logging.
ESPDEPRECATED("set_retry is deprecated and will be removed in 2026.8.0. Use set_timeout or set_interval instead.", "2026.2.0") void set_retry(const std uint32_t uint8_t std::function< RetryResult(uint8_t)> float backoff_increase_factor
struct @65::@66 __attribute__
Wake the main loop task from an ISR. ISR-safe.
const Component * component
const char int const __FlashStringHelper * format
void retry_handler(const std::shared_ptr< RetryArgs > &args)
const char int const __FlashStringHelper va_list args
uint32_t random_uint32()
Return a random 32-bit unsigned integer.
void HOT delay(uint32_t ms)
Application App
Global storage of Application pointer - only one Application can exist.
constexpr uint32_t fnv1a_hash(const char *str)
Calculate a FNV-1a hash of str.
constexpr uint32_t SCHEDULER_DONT_RUN