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,
135 const LogString *source) {
139 LockGuard guard{this->lock_};
140 this->cancel_item_locked_(
component, name_type, static_name, hash_or_id,
type,
false,
153 if (
type == SchedulerItem::INTERVAL &&
delay == 0) [[unlikely]] {
154 ESP_LOGE(TAG,
"[%s] set_interval(0) would spin main loop - coercing to 1ms (use HighFrequencyLoopRequester)",
160 LockGuard guard{this->lock_};
165 if (is_retry &&
delay != 0 && (name_type != NameType::STATIC_STRING || static_name !=
nullptr) &&
166 type == SchedulerItem::TIMEOUT &&
167 this->is_retry_cancelled_locked_(
component, name_type, static_name, hash_or_id)) {
168#ifdef ESPHOME_DEBUG_SCHEDULER
169 SchedulerNameLog skip_name_log;
170 ESP_LOGD(TAG,
"Skipping retry '%s' - found cancelled item",
171 skip_name_log.format(name_type, static_name, hash_or_id));
177 SchedulerItem *item = this->get_item_from_pool_locked_();
179 if (name_type == NameType::SELF_POINTER) {
180 item->source_name = source;
184 item->set_name(name_type, static_name, hash_or_id);
191 item->callback.~function();
192 new (&item->callback) std::function<
void()>(std::move(func));
194 this->set_item_removed_(item,
false);
195 item->is_retry = is_retry;
199 auto *target = &this->to_add_;
201#ifndef ESPHOME_THREAD_SINGLE
204 if (
delay == 0 &&
type == SchedulerItem::TIMEOUT) {
206 target = &this->defer_queue_;
214 if (
type == SchedulerItem::INTERVAL) {
215 item->interval =
delay;
218 item->set_next_execution(now_64 + offset);
219#ifdef ESPHOME_LOG_HAS_VERBOSE
220 SchedulerNameLog name_log;
221 ESP_LOGV(TAG,
"Scheduler interval for %s is %" PRIu32
"ms, offset %" PRIu32
"ms",
222 name_log.format(name_type, static_name, hash_or_id),
delay, offset);
226 item->set_next_execution(now_64 +
delay);
229#ifdef ESPHOME_DEBUG_SCHEDULER
230 this->debug_log_timer_(item, name_type, static_name, hash_or_id,
type,
delay, now_64);
236 if (!skip_cancel && (name_type != NameType::STATIC_STRING || static_name !=
nullptr)) {
237 this->cancel_item_locked_(
component, name_type, static_name, hash_or_id,
type,
false,
240 target->push_back(item);
241 if (target == &this->to_add_) {
242 this->to_add_count_increment_locked_();
244#ifndef ESPHOME_THREAD_SINGLE
246 this->defer_count_increment_locked_();
251void HOT Scheduler::set_timeout(Component *
component,
const char *name,
uint32_t timeout,
252 std::function<
void()> &&func) {
253 this->set_timer_common_(
component, SchedulerItem::TIMEOUT, NameType::STATIC_STRING, name, 0, timeout,
257void HOT Scheduler::set_timeout(Component *
component,
const std::string &name,
uint32_t timeout,
258 std::function<
void()> &&func) {
259 this->set_timer_common_(
component, SchedulerItem::TIMEOUT, NameType::HASHED_STRING,
nullptr,
fnv1a_hash(name),
260 timeout, std::move(func));
263 this->set_timer_common_(
component, SchedulerItem::TIMEOUT, NameType::NUMERIC_ID,
nullptr,
id, timeout,
266bool HOT Scheduler::cancel_timeout(Component *
component,
const std::string &name) {
267 return this->cancel_item_(
component, NameType::HASHED_STRING,
nullptr,
fnv1a_hash(name), SchedulerItem::TIMEOUT);
269bool HOT Scheduler::cancel_timeout(Component *
component,
const char *name) {
270 return this->cancel_item_(
component, NameType::STATIC_STRING, name, 0, SchedulerItem::TIMEOUT);
273 return this->cancel_item_(
component, NameType::NUMERIC_ID,
nullptr,
id, SchedulerItem::TIMEOUT);
275void HOT Scheduler::set_interval(Component *
component,
const std::string &name,
uint32_t interval,
276 std::function<
void()> &&func) {
277 this->set_timer_common_(
component, SchedulerItem::INTERVAL, NameType::HASHED_STRING,
nullptr,
fnv1a_hash(name),
278 interval, std::move(func));
281void HOT Scheduler::set_interval(Component *
component,
const char *name,
uint32_t interval,
282 std::function<
void()> &&func) {
283 this->set_timer_common_(
component, SchedulerItem::INTERVAL, NameType::STATIC_STRING, name, 0, interval,
287 this->set_timer_common_(
component, SchedulerItem::INTERVAL, NameType::NUMERIC_ID,
nullptr,
id, interval,
290bool HOT Scheduler::cancel_interval(Component *
component,
const std::string &name) {
291 return this->cancel_item_(
component, NameType::HASHED_STRING,
nullptr,
fnv1a_hash(name), SchedulerItem::INTERVAL);
293bool HOT Scheduler::cancel_interval(Component *
component,
const char *name) {
294 return this->cancel_item_(
component, NameType::STATIC_STRING, name, 0, SchedulerItem::INTERVAL);
297 return this->cancel_item_(
component, NameType::NUMERIC_ID,
nullptr,
id, SchedulerItem::INTERVAL);
304void HOT Scheduler::set_timeout(
const void *self,
uint32_t timeout, std::function<
void()> &&func) {
305 this->set_timer_common_(
nullptr, SchedulerItem::TIMEOUT, NameType::SELF_POINTER,
static_cast<const char *
>(self), 0,
306 timeout, std::move(func));
308void HOT Scheduler::set_interval(
const void *self,
uint32_t interval, std::function<
void()> &&func) {
309 this->set_timer_common_(
nullptr, SchedulerItem::INTERVAL, NameType::SELF_POINTER,
static_cast<const char *
>(self), 0,
310 interval, std::move(func));
312bool HOT Scheduler::cancel_timeout(
const void *self) {
313 return this->cancel_item_(
nullptr, NameType::SELF_POINTER,
static_cast<const char *
>(self), 0,
314 SchedulerItem::TIMEOUT);
316bool HOT Scheduler::cancel_interval(
const void *self) {
317 return this->cancel_item_(
nullptr, NameType::SELF_POINTER,
static_cast<const char *
>(self), 0,
318 SchedulerItem::INTERVAL);
323#pragma GCC diagnostic push
324#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
330 Scheduler *scheduler;
333 const char *static_name;
337 float backoff_increase_factor;
338 Scheduler::NameType name_type;
339 uint8_t retry_countdown;
349 const char *static_name = (
args->name_type == Scheduler::NameType::STATIC_STRING) ?
args->name_.static_name :
nullptr;
350 uint32_t hash_or_id = (
args->name_type != Scheduler::NameType::STATIC_STRING) ?
args->name_.hash_or_id : 0;
351 args->scheduler->set_timer_common_(
352 args->component, Scheduler::SchedulerItem::TIMEOUT,
args->name_type, static_name, hash_or_id,
353 args->current_interval, [
args]() { retry_handler(args); },
356 args->current_interval *=
args->backoff_increase_factor;
359void HOT Scheduler::set_retry_common_(Component *
component, NameType name_type,
const char *static_name,
361 std::function<
RetryResult(uint8_t)> func,
float backoff_increase_factor) {
362 this->cancel_retry_(
component, name_type, static_name, hash_or_id);
367#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
369 SchedulerNameLog name_log;
370 ESP_LOGVV(TAG,
"set_retry(name='%s', initial_wait_time=%" PRIu32
", max_attempts=%u, backoff_factor=%0.1f)",
371 name_log.format(name_type, static_name, hash_or_id), initial_wait_time, max_attempts,
372 backoff_increase_factor);
376 if (backoff_increase_factor < 0.0001) {
377 ESP_LOGE(TAG,
"set_retry: backoff_factor %0.1f too small, using 1.0: %s", backoff_increase_factor,
378 (name_type == NameType::STATIC_STRING && static_name) ? static_name :
"");
379 backoff_increase_factor = 1;
382 auto args = std::make_shared<RetryArgs>();
383 args->func = std::move(func);
385 args->scheduler =
this;
386 args->name_type = name_type;
387 if (name_type == NameType::STATIC_STRING) {
388 args->name_.static_name = static_name;
390 args->name_.hash_or_id = hash_or_id;
392 args->current_interval = initial_wait_time;
394 args->retry_countdown = max_attempts;
397 this->set_timer_common_(
402void HOT Scheduler::set_retry(Component *
component,
const char *name,
uint32_t initial_wait_time, uint8_t max_attempts,
403 std::function<
RetryResult(uint8_t)> func,
float backoff_increase_factor) {
404 this->set_retry_common_(
component, NameType::STATIC_STRING, name, 0, initial_wait_time, max_attempts, std::move(func),
405 backoff_increase_factor);
408bool HOT Scheduler::cancel_retry_(Component *
component, NameType name_type,
const char *static_name,
410 return this->cancel_item_(
component, name_type, static_name, hash_or_id, SchedulerItem::TIMEOUT,
413bool HOT Scheduler::cancel_retry(Component *
component,
const char *name) {
414 return this->cancel_retry_(
component, NameType::STATIC_STRING, name, 0);
417void HOT Scheduler::set_retry(Component *
component,
const std::string &name,
uint32_t initial_wait_time,
418 uint8_t max_attempts, std::function<
RetryResult(uint8_t)> func,
419 float backoff_increase_factor) {
420 this->set_retry_common_(
component, NameType::HASHED_STRING,
nullptr,
fnv1a_hash(name), initial_wait_time,
421 max_attempts, std::move(func), backoff_increase_factor);
424bool HOT Scheduler::cancel_retry(Component *
component,
const std::string &name) {
429 std::function<
RetryResult(uint8_t)> func,
float backoff_increase_factor) {
430 this->set_retry_common_(
component, NameType::NUMERIC_ID,
nullptr,
id, initial_wait_time, max_attempts,
431 std::move(func), backoff_increase_factor);
435 return this->cancel_retry_(
component, NameType::NUMERIC_ID,
nullptr,
id);
438#pragma GCC diagnostic pop
440optional<uint32_t> HOT Scheduler::next_schedule_in(
uint32_t now) {
450#ifndef ESPHOME_THREAD_SINGLE
454 if (!this->defer_empty_())
460 if (!this->to_add_empty_())
465 if (!this->cleanup_())
468 SchedulerItem *item = this->items_[0];
469 const auto now_64 = this->millis_64_from_(now);
470 const uint64_t next_exec = item->get_next_execution();
471 if (next_exec < now_64)
473 return next_exec - now_64;
476void Scheduler::full_cleanup_removed_items_() {
482 LockGuard guard{this->lock_};
486 for (
size_t read = 0; read < this->items_.size(); ++read) {
487 if (!is_item_removed_locked_(this->items_[read])) {
489 this->items_[write] = this->items_[read];
493 this->recycle_item_main_loop_(this->items_[read]);
496 this->items_.erase(this->items_.begin() + write, this->items_.end());
498 std::make_heap(this->items_.begin(), this->items_.end(), SchedulerItem::cmp);
499 this->to_remove_clear_locked_();
502#ifndef ESPHOME_THREAD_SINGLE
503void Scheduler::compact_defer_queue_locked_() {
515 size_t remaining = this->defer_queue_.size() - this->defer_queue_front_;
516 for (
size_t i = 0; i < remaining; i++) {
517 this->defer_queue_[i] = this->defer_queue_[this->defer_queue_front_ + i];
521 this->defer_queue_.erase(this->defer_queue_.begin() + remaining, this->defer_queue_.end());
523void HOT Scheduler::process_defer_queue_slow_path_(
uint32_t &now) {
548 this->defer_count_clear_locked_();
549 size_t defer_queue_end = this->defer_queue_.size();
550 if (this->defer_queue_front_ >= defer_queue_end) {
551 this->lock_.unlock();
554 while (this->defer_queue_front_ < defer_queue_end) {
560 item = this->defer_queue_[this->defer_queue_front_];
561 this->defer_queue_[this->defer_queue_front_] =
nullptr;
562 this->defer_queue_front_++;
563 this->lock_.unlock();
567 if (!this->should_skip_item_(item)) {
568 now = this->execute_item_(item, now);
572 this->recycle_item_main_loop_(item);
575 this->cleanup_defer_queue_locked_();
576 this->lock_.unlock();
581#ifndef ESPHOME_THREAD_SINGLE
582 this->process_defer_queue_(now);
586 const auto now_64 = this->millis_64_from_(now);
587 this->process_to_add();
590 bool has_added_items =
false;
592#ifdef ESPHOME_DEBUG_SCHEDULER
593 static uint64_t last_print = 0;
595 if (now_64 - last_print > 2000) {
597 std::vector<SchedulerItem *> old_items;
598 ESP_LOGD(TAG,
"Items: count=%zu, pool=%zu, now=%" PRIu64, this->items_.size(), this->scheduler_item_pool_size_,
602 while (!this->items_.empty()) {
605 LockGuard guard{this->lock_};
606 item = this->pop_raw_locked_();
609 SchedulerNameLog name_log;
610 bool is_cancelled = is_item_removed_(item);
611 ESP_LOGD(TAG,
" %s '%s/%s' interval=%" PRIu32
" next_execution in %" PRIu64
"ms at %" PRIu64
"%s",
612 item->get_type_str(), LOG_STR_ARG(item->get_source()),
613 name_log.format(item->get_name_type(), item->get_name(), item->get_name_hash_or_id()), item->interval,
614 item->get_next_execution() - now_64, item->get_next_execution(), is_cancelled ?
" [CANCELLED]" :
"");
616 old_items.push_back(item);
621 LockGuard guard{this->lock_};
622 this->items_ = std::move(old_items);
624 std::make_heap(this->items_.begin(), this->items_.end(), SchedulerItem::cmp);
635 if (this->to_remove_count_() >= MAX_LOGICALLY_DELETED_ITEMS) {
636 this->full_cleanup_removed_items_();
644 while (!this->items_.empty()) {
646 SchedulerItem *item = this->items_[0];
647 if (item->get_next_execution() > now_64) {
652 if (this->is_item_failed_(item)) {
653 LockGuard guard{this->lock_};
654 this->recycle_item_main_loop_(this->pop_raw_locked_());
662#ifdef ESPHOME_THREAD_MULTI_NO_ATOMICS
665 LockGuard guard{this->lock_};
666 if (is_item_removed_locked_(item)) {
667 this->recycle_item_main_loop_(this->pop_raw_locked_());
668 this->to_remove_decrement_locked_();
674 if (is_item_removed_(item)) {
675 LockGuard guard{this->lock_};
676 this->recycle_item_main_loop_(this->pop_raw_locked_());
677 this->to_remove_decrement_locked_();
682#ifdef ESPHOME_DEBUG_SCHEDULER
684 SchedulerNameLog name_log;
685 ESP_LOGV(TAG,
"Running %s '%s/%s' with interval=%" PRIu32
" next_execution=%" PRIu64
" (now=%" PRIu64
")",
686 item->get_type_str(), LOG_STR_ARG(item->get_source()),
687 name_log.format(item->get_name_type(), item->get_name(), item->get_name_hash_or_id()), item->interval,
688 item->get_next_execution(), now_64);
695 now = this->execute_item_(item, now);
697 LockGuard guard{this->lock_};
701 SchedulerItem *executed_item = this->pop_raw_locked_();
703 if (this->is_item_removed_locked_(executed_item)) {
705 this->to_remove_decrement_locked_();
706 this->recycle_item_main_loop_(executed_item);
710 if (executed_item->type == SchedulerItem::INTERVAL) {
711 executed_item->set_next_execution(now_64 + executed_item->interval);
722 this->items_.push_back(executed_item);
723 std::push_heap(this->items_.begin(), this->items_.end(), SchedulerItem::cmp);
726 this->recycle_item_main_loop_(executed_item);
729 has_added_items |= !this->to_add_.empty();
732 if (has_added_items) {
733 this->process_to_add();
736#ifdef ESPHOME_DEBUG_SCHEDULER
746 LockGuard guard{this->lock_};
747 this->debug_verify_no_leak_();
754void HOT Scheduler::process_to_add_slow_path_() {
755 LockGuard guard{this->lock_};
756 for (
auto *&it : this->to_add_) {
757 if (is_item_removed_locked_(it)) {
759 this->recycle_item_main_loop_(it);
764 this->items_.push_back(it);
765 std::push_heap(this->items_.begin(), this->items_.end(), SchedulerItem::cmp);
767 this->to_add_.clear();
768 this->to_add_count_clear_locked_();
770bool HOT Scheduler::cleanup_slow_path_() {
779 LockGuard guard{this->lock_};
780 while (!this->items_.empty()) {
781 SchedulerItem *item = this->items_[0];
782 if (!this->is_item_removed_locked_(item))
784 this->to_remove_decrement_locked_();
785 this->recycle_item_main_loop_(this->pop_raw_locked_());
787 return !this->items_.empty();
789Scheduler::SchedulerItem *HOT Scheduler::pop_raw_locked_() {
790 std::pop_heap(this->items_.begin(), this->items_.end(), SchedulerItem::cmp);
792 SchedulerItem *item = this->items_.back();
793 this->items_.pop_back();
804 const LogString *source;
805 if (item->get_name_type() == NameType::SELF_POINTER) {
807 source = item->source_name;
813 LoopBlockingGuard guard{
component, source, now};
825bool HOT Scheduler::cancel_item_(Component *
component, NameType name_type,
const char *static_name,
uint32_t hash_or_id,
826 SchedulerItem::Type
type,
bool match_retry) {
827 LockGuard guard{this->lock_};
830 return this->cancel_item_locked_(
component, name_type, static_name, hash_or_id,
type, match_retry);
839size_t Scheduler::mark_matching_items_removed_slow_locked_(std::vector<SchedulerItem *> &container,
840 Component *
component, NameType name_type,
841 const char *static_name,
uint32_t hash_or_id,
842 SchedulerItem::Type
type,
bool match_retry,
845 for (
auto *item : container) {
846 if (this->matches_item_locked_(item,
component, name_type, static_name, hash_or_id,
type, match_retry)) {
847 this->set_item_removed_(item,
true);
856bool HOT Scheduler::cancel_item_locked_(Component *
component, NameType name_type,
const char *static_name,
857 uint32_t hash_or_id, SchedulerItem::Type
type,
bool match_retry,
860 if (name_type == NameType::STATIC_STRING && static_name ==
nullptr) {
864 size_t total_cancelled = 0;
866#ifndef ESPHOME_THREAD_SINGLE
868 if (
type == SchedulerItem::TIMEOUT) {
869 total_cancelled += this->mark_matching_items_removed_locked_(this->defer_queue_,
component, name_type, static_name,
870 hash_or_id,
type, match_retry, find_first);
871 if (find_first && total_cancelled > 0)
882 size_t heap_cancelled = this->mark_matching_items_removed_locked_(this->items_,
component, name_type, static_name,
883 hash_or_id,
type, match_retry, find_first);
884 total_cancelled += heap_cancelled;
885 this->to_remove_add_locked_(heap_cancelled);
886 if (find_first && total_cancelled > 0)
891 total_cancelled += this->mark_matching_items_removed_locked_(this->to_add_,
component, name_type, static_name,
892 hash_or_id,
type, match_retry, find_first);
894 return total_cancelled > 0;
897bool HOT Scheduler::SchedulerItem::cmp(SchedulerItem *a, SchedulerItem *b) {
900 return (a->next_execution_high_ ==
b->next_execution_high_) ? (a->next_execution_low_ >
b->next_execution_low_)
901 : (a->next_execution_high_ >
b->next_execution_high_);
906void Scheduler::recycle_item_main_loop_(SchedulerItem *item) {
910 item->callback =
nullptr;
911 item->next_free = this->scheduler_item_pool_head_;
912 this->scheduler_item_pool_head_ = item;
913 this->scheduler_item_pool_size_++;
914#ifdef ESPHOME_DEBUG_SCHEDULER
915 ESP_LOGD(TAG,
"Recycled item to pool (pool size now: %zu)", this->scheduler_item_pool_size_);
927void __attribute__((noinline)) Scheduler::shrink_scheduler_vector_(std::vector<SchedulerItem *> *v) {
928 if (v->capacity() == v->size())
930 std::vector<SchedulerItem *> tmp;
931 tmp.reserve(v->size());
932 for (SchedulerItem *p : *v)
937void Scheduler::trim_freelist() {
938 LockGuard guard{this->lock_};
939 SchedulerItem *item = this->scheduler_item_pool_head_;
941 while (item !=
nullptr) {
942 SchedulerItem *next = item->next_free;
944#ifdef ESPHOME_DEBUG_SCHEDULER
945 this->debug_live_items_--;
950 this->scheduler_item_pool_head_ =
nullptr;
951 this->scheduler_item_pool_size_ = 0;
955 shrink_scheduler_vector_(&this->items_);
956 shrink_scheduler_vector_(&this->to_add_);
957#ifndef ESPHOME_THREAD_SINGLE
958 shrink_scheduler_vector_(&this->defer_queue_);
961#ifdef ESPHOME_DEBUG_SCHEDULER
962 ESP_LOGD(TAG,
"Freelist trimmed (%zu items freed)", freed);
968#ifdef ESPHOME_DEBUG_SCHEDULER
969void Scheduler::debug_log_timer_(
const SchedulerItem *item, NameType name_type,
const char *static_name,
972 if (name_type == NameType::STATIC_STRING && static_name !=
nullptr) {
973 validate_static_string(static_name);
977 SchedulerNameLog name_log;
978 const char *type_str = (
type == SchedulerItem::TIMEOUT) ?
"timeout" :
"interval";
979 if (
type == SchedulerItem::TIMEOUT) {
980 ESP_LOGD(TAG,
"set_%s(name='%s/%s', %s=%" PRIu32
")", type_str, LOG_STR_ARG(item->get_source()),
981 name_log.format(name_type, static_name, hash_or_id), type_str,
delay);
983 ESP_LOGD(TAG,
"set_%s(name='%s/%s', %s=%" PRIu32
", offset=%" PRIu32
")", type_str, LOG_STR_ARG(item->get_source()),
984 name_log.format(name_type, static_name, hash_or_id), type_str,
delay,
985 static_cast<uint32_t>(item->get_next_execution() - now));
992Scheduler::SchedulerItem *Scheduler::get_item_from_pool_locked_() {
993 if (this->scheduler_item_pool_head_ !=
nullptr) {
994 SchedulerItem *item = this->scheduler_item_pool_head_;
995 this->scheduler_item_pool_head_ = item->next_free;
996 this->scheduler_item_pool_size_--;
997#ifdef ESPHOME_DEBUG_SCHEDULER
998 ESP_LOGD(TAG,
"Reused item from pool (pool size now: %zu)", this->scheduler_item_pool_size_);
1002#ifdef ESPHOME_DEBUG_SCHEDULER
1003 ESP_LOGD(TAG,
"Allocated new item (pool empty)");
1005 auto *item =
new SchedulerItem();
1006#ifdef ESPHOME_DEBUG_SCHEDULER
1007 this->debug_live_items_++;
1012#ifdef ESPHOME_DEBUG_SCHEDULER
1013bool Scheduler::debug_verify_no_leak_()
const {
1016 size_t accounted = this->items_.size() + this->to_add_.size() + this->scheduler_item_pool_size_;
1017#ifndef ESPHOME_THREAD_SINGLE
1018 accounted += this->defer_queue_.size();
1020 if (accounted != this->debug_live_items_) {
1022 "SCHEDULER LEAK DETECTED: live=%" PRIu32
" but accounted=%" PRIu32
" (items=%" PRIu32
" to_add=%" PRIu32
1024#ifndef ESPHOME_THREAD_SINGLE
1028 static_cast<uint32_t>(this->debug_live_items_),
static_cast<uint32_t>(accounted),
1029 static_cast<uint32_t>(this->items_.size()),
static_cast<uint32_t>(this->to_add_.size()),
1030 static_cast<uint32_t>(this->scheduler_item_pool_size_)
1031#ifndef ESPHOME_THREAD_SINGLE
1033 static_cast<uint32_t>(this->defer_queue_.size())
void ESPHOME_ALWAYS_INLINE feed_wdt_with_time(uint32_t time)
Feed the task watchdog, hot entry.
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