7#ifdef ESPHOME_THREAD_MULTI_ATOMICS
26 friend void ::esphome::retry_handler(
const std::shared_ptr<RetryArgs> &args);
31 template<
typename... Ts>
friend class DelayAction;
42 void set_timeout(Component *
component,
const char *name,
uint32_t timeout, std::function<
void()> &&func);
46 void set_timeout(Component *
component, InternalSchedulerID
id,
uint32_t timeout, std::function<
void()> &&func) {
47 this->set_timer_common_(
component, SchedulerItem::TIMEOUT, NameType::NUMERIC_ID_INTERNAL,
nullptr,
48 static_cast<uint32_t>(
id), timeout, std::move(func));
51 bool cancel_timeout(Component *
component,
const char *name);
53 bool cancel_timeout(Component *
component, InternalSchedulerID
id) {
54 return this->cancel_item_(
component, NameType::NUMERIC_ID_INTERNAL,
nullptr,
static_cast<uint32_t>(
id),
55 SchedulerItem::TIMEOUT);
66 void set_interval(Component *
component,
const char *name,
uint32_t interval, std::function<
void()> &&func);
70 void set_interval(Component *
component, InternalSchedulerID
id,
uint32_t interval, std::function<
void()> &&func) {
71 this->set_timer_common_(
component, SchedulerItem::INTERVAL, NameType::NUMERIC_ID_INTERNAL,
nullptr,
72 static_cast<uint32_t>(
id), interval, std::move(func));
75 bool cancel_interval(Component *
component,
const char *name);
77 bool cancel_interval(Component *
component, InternalSchedulerID
id) {
78 return this->cancel_item_(
component, NameType::NUMERIC_ID_INTERNAL,
nullptr,
static_cast<uint32_t>(
id),
79 SchedulerItem::INTERVAL);
83 ESPDEPRECATED(
"set_retry is deprecated and will be removed in 2026.8.0. Use set_timeout or set_interval instead.",
85 void set_retry(Component *
component, const
std::
string &name,
uint32_t initial_wait_time, uint8_t max_attempts,
86 std::function<RetryResult(uint8_t)> func,
float backoff_increase_factor = 1.0f);
88 ESPDEPRECATED("set_retry is deprecated and will be removed in 2026.8.0. Use set_timeout or set_interval instead.",
90 void set_retry(Component *
component, const
char *name,
uint32_t initial_wait_time, uint8_t max_attempts,
91 std::function<RetryResult(uint8_t)> func,
float backoff_increase_factor = 1.0f);
93 ESPDEPRECATED("set_retry is deprecated and will be removed in 2026.8.0. Use set_timeout or set_interval instead.",
96 std::function<RetryResult(uint8_t)> func,
float backoff_increase_factor = 1.0f);
99 ESPDEPRECATED("cancel_retry is deprecated and will be removed in 2026.8.0.", "2026.2.0")
100 bool cancel_retry(Component *
component, const
std::
string &name);
102 ESPDEPRECATED("cancel_retry is deprecated and will be removed in 2026.8.0.", "2026.2.0")
103 bool cancel_retry(Component *
component, const
char *name);
105 ESPDEPRECATED("cancel_retry is deprecated and will be removed in 2026.8.0.", "2026.2.0")
116 optional<uint32_t> next_schedule_in(
uint32_t now);
127 void trim_freelist();
136 inline void ESPHOME_ALWAYS_INLINE HOT process_to_add() {
137 if (this->to_add_empty_())
139 this->process_to_add_slow_path_();
145 enum class NameType : uint8_t {
149 NUMERIC_ID_INTERNAL = 3,
166 void set_timeout(
const void *self,
uint32_t timeout, std::function<
void()> &&func);
168 void set_interval(
const void *self,
uint32_t interval, std::function<
void()> &&func);
169 bool cancel_timeout(
const void *self);
170 bool cancel_interval(
const void *self);
173 struct SchedulerItem {
179 SchedulerItem *next_free;
183 const char *static_name;
195 std::function<void()> callback;
196 uint16_t next_execution_high_;
198#ifdef ESPHOME_THREAD_MULTI_ATOMICS
203 std::atomic<uint8_t> remove{0};
207 NameType name_type_ : 3;
215 NameType name_type_ : 3;
224 next_execution_low_(0),
225 next_execution_high_(0),
226#ifdef ESPHOME_THREAD_MULTI_ATOMICS
229 name_type_(NameType::STATIC_STRING),
234 name_type_(NameType::STATIC_STRING),
237 name_.static_name =
nullptr;
241 ~SchedulerItem() =
default;
244 SchedulerItem(
const SchedulerItem &) =
delete;
245 SchedulerItem &operator=(
const SchedulerItem &) =
delete;
248 SchedulerItem(SchedulerItem &&) =
delete;
249 SchedulerItem &operator=(SchedulerItem &&) =
delete;
253 const char *get_name()
const {
254 return (name_type_ == NameType::STATIC_STRING || name_type_ == NameType::SELF_POINTER) ? name_.static_name
259 uint32_t get_name_hash_or_id()
const {
260 return (name_type_ != NameType::STATIC_STRING && name_type_ != NameType::SELF_POINTER) ? name_.hash_or_id : 0;
264 NameType get_name_type()
const {
return name_type_; }
269 void set_name(NameType
type,
const char *static_name,
uint32_t hash_or_id) {
270 if (
type == NameType::STATIC_STRING ||
type == NameType::SELF_POINTER) {
271 name_.static_name = static_name;
273 name_.hash_or_id = hash_or_id;
278 static bool cmp(SchedulerItem *a, SchedulerItem *b);
283 constexpr uint64_t get_next_execution()
const {
284 return (
static_cast<uint64_t
>(next_execution_high_) << 32) | next_execution_low_;
287 constexpr void set_next_execution(uint64_t value) {
288 next_execution_low_ =
static_cast<uint32_t>(value);
291 next_execution_high_ =
static_cast<uint16_t
>(value >> 32);
293 constexpr const char *get_type_str()
const {
return (
type == TIMEOUT) ?
"timeout" :
"interval"; }
296 Component *get_component()
const {
return name_type_ == NameType::SELF_POINTER ? nullptr :
component; }
297 const LogString *get_source()
const {
299 if (name_type_ == NameType::SELF_POINTER)
301 return component !=
nullptr ?
component->get_component_log_str() : LOG_STR(
"unknown");
308 void set_timer_common_(Component *
component, SchedulerItem::Type
type, NameType name_type,
const char *static_name,
310 bool skip_cancel =
false,
const LogString *source =
nullptr);
314#pragma GCC diagnostic push
315#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
316 void set_retry_common_(Component *
component, NameType name_type,
const char *static_name,
uint32_t hash_or_id,
317 uint32_t initial_wait_time, uint8_t max_attempts, std::function<
RetryResult(uint8_t)> func,
318 float backoff_increase_factor);
319#pragma GCC diagnostic pop
321 bool cancel_retry_(Component *
component, NameType name_type,
const char *static_name,
uint32_t hash_or_id);
332 uint64_t ESPHOME_ALWAYS_INLINE millis_64_from_(
uint32_t now) {
333#ifdef USE_NATIVE_64BIT_TIME
337 return Millis64Impl::compute(now);
348 inline bool ESPHOME_ALWAYS_INLINE HOT cleanup_() {
349 if (this->to_remove_empty_())
350 return !this->items_.empty();
351 return this->cleanup_slow_path_();
354 bool cleanup_slow_path_();
356 void process_to_add_slow_path_();
360 SchedulerItem *pop_raw_locked_();
363 SchedulerItem *get_item_from_pool_locked_();
368 void shrink_scheduler_vector_(std::vector<SchedulerItem *> *v);
376 bool cancel_item_locked_(Component *
component, NameType name_type,
const char *static_name,
uint32_t hash_or_id,
377 SchedulerItem::Type
type,
bool match_retry =
false,
bool find_first =
false);
380 bool cancel_item_(Component *
component, NameType name_type,
const char *static_name,
uint32_t hash_or_id,
381 SchedulerItem::Type
type,
bool match_retry =
false);
384 inline bool HOT names_match_static_(
const char *name1,
const char *name2)
const {
389 return (name1 !=
nullptr && name2 !=
nullptr) && ((name1 == name2) || (strcmp(name1, name2) == 0));
395 inline bool HOT matches_item_locked_(SchedulerItem *item, Component *
component, NameType name_type,
396 const char *static_name,
uint32_t hash_or_id, SchedulerItem::Type
type,
397 bool match_retry,
bool skip_removed =
true)
const {
405 if (item->get_component() !=
component || item->type !=
type ||
406 (skip_removed && this->is_item_removed_locked_(item)) || (match_retry && !item->is_retry)) {
410 if (item->get_name_type() != name_type)
414 if (name_type == NameType::STATIC_STRING) {
415 return this->names_match_static_(item->get_name(), static_name);
417 if (name_type == NameType::SELF_POINTER) {
418 return item->name_.static_name == static_name;
420 return item->get_name_hash_or_id() == hash_or_id;
428 bool is_item_failed_(SchedulerItem *item)
const {
429 Component *
component = item->get_component();
434 bool should_skip_item_(SchedulerItem *item)
const {
return is_item_removed_(item) || this->is_item_failed_(item); }
442 void recycle_item_main_loop_(SchedulerItem *item);
445 void full_cleanup_removed_items_();
456 is_retry_cancelled_locked_(Component *
component, NameType name_type,
const char *static_name,
uint32_t hash_or_id);
458#ifdef ESPHOME_DEBUG_SCHEDULER
460 void debug_log_timer_(
const SchedulerItem *item, NameType name_type,
const char *static_name,
uint32_t hash_or_id,
464#ifndef ESPHOME_THREAD_SINGLE
468 inline void ESPHOME_ALWAYS_INLINE HOT process_defer_queue_(
uint32_t &now) {
471 if (this->defer_empty_())
473 this->process_defer_queue_slow_path_(now);
477 void process_defer_queue_slow_path_(
uint32_t &now);
483 inline void cleanup_defer_queue_locked_() {
485 if (this->defer_queue_front_ >= this->defer_queue_.size()) {
487 this->defer_queue_.clear();
491 this->compact_defer_queue_locked_();
493 this->defer_queue_front_ = 0;
499 void __attribute__((noinline)) compact_defer_queue_locked_();
506 bool is_item_removed_(SchedulerItem *item)
const {
507#ifdef ESPHOME_THREAD_MULTI_ATOMICS
509 return item->remove.load(std::memory_order_acquire);
521 bool is_item_removed_locked_(SchedulerItem *item)
const {
522#ifdef ESPHOME_THREAD_MULTI_ATOMICS
524 return item->remove.load(std::memory_order_relaxed);
534 void set_item_removed_(SchedulerItem *item,
bool removed) {
535#ifdef ESPHOME_THREAD_MULTI_ATOMICS
539 item->remove.store(removed ? 1 : 0, removed ? std::memory_order_release : std::memory_order_relaxed);
544 item->remove = removed;
557 inline size_t HOT mark_matching_items_removed_locked_(std::vector<SchedulerItem *> &container, Component *
component,
558 NameType name_type,
const char *static_name,
559 uint32_t hash_or_id, SchedulerItem::Type
type,
bool match_retry,
560 bool find_first =
false) {
561 if (container.empty())
563 return this->mark_matching_items_removed_slow_locked_(container,
component, name_type, static_name, hash_or_id,
564 type, match_retry, find_first);
569 __attribute__((noinline))
size_t mark_matching_items_removed_slow_locked_(
570 std::vector<SchedulerItem *> &container, Component *
component, NameType name_type,
const char *static_name,
571 uint32_t hash_or_id, SchedulerItem::Type
type,
bool match_retry,
bool find_first);
574 std::vector<SchedulerItem *> items_;
575 std::vector<SchedulerItem *> to_add_;
577#ifndef ESPHOME_THREAD_SINGLE
585#ifdef ESPHOME_THREAD_MULTI_ATOMICS
586 std::atomic<uint32_t> to_add_count_{0};
593 bool to_add_empty_()
const {
594#ifdef ESPHOME_THREAD_SINGLE
595 return this->to_add_.empty();
596#elif defined(ESPHOME_THREAD_MULTI_ATOMICS)
597 return this->to_add_count_.load(std::memory_order_relaxed) == 0;
599 return __atomic_load_n(&this->to_add_count_, __ATOMIC_RELAXED) == 0;
608 void to_add_count_increment_locked_() {
609#if defined(ESPHOME_THREAD_SINGLE)
611#elif defined(ESPHOME_THREAD_MULTI_ATOMICS)
612 this->to_add_count_.fetch_add(1, std::memory_order_relaxed);
614 uint32_t v = __atomic_load_n(&this->to_add_count_, __ATOMIC_RELAXED);
615 __atomic_store_n(&this->to_add_count_, v + 1, __ATOMIC_RELAXED);
620 void to_add_count_clear_locked_() {
621#if defined(ESPHOME_THREAD_SINGLE)
623#elif defined(ESPHOME_THREAD_MULTI_ATOMICS)
624 this->to_add_count_.store(0, std::memory_order_relaxed);
626 __atomic_store_n(&this->to_add_count_, 0, __ATOMIC_RELAXED);
630#ifndef ESPHOME_THREAD_SINGLE
634 std::vector<SchedulerItem *> defer_queue_;
635 size_t defer_queue_front_{0};
639#ifdef ESPHOME_THREAD_MULTI_ATOMICS
640 std::atomic<uint32_t> defer_count_{0};
645 bool defer_empty_()
const {
647#ifdef ESPHOME_THREAD_MULTI_ATOMICS
648 return this->defer_count_.load(std::memory_order_relaxed) == 0;
650 return __atomic_load_n(&this->defer_count_, __ATOMIC_RELAXED) == 0;
654 void defer_count_increment_locked_() {
655#ifdef ESPHOME_THREAD_MULTI_ATOMICS
656 this->defer_count_.fetch_add(1, std::memory_order_relaxed);
658 uint32_t v = __atomic_load_n(&this->defer_count_, __ATOMIC_RELAXED);
659 __atomic_store_n(&this->defer_count_, v + 1, __ATOMIC_RELAXED);
663 void defer_count_clear_locked_() {
664#ifdef ESPHOME_THREAD_MULTI_ATOMICS
665 this->defer_count_.store(0, std::memory_order_relaxed);
667 __atomic_store_n(&this->defer_count_, 0, __ATOMIC_RELAXED);
676#ifdef ESPHOME_THREAD_MULTI_ATOMICS
677 std::atomic<uint32_t> to_remove_{0};
683 bool to_remove_empty_()
const {
684#if defined(ESPHOME_THREAD_MULTI_ATOMICS)
685 return this->to_remove_.load(std::memory_order_relaxed) == 0;
686#elif defined(ESPHOME_THREAD_MULTI_NO_ATOMICS)
687 return __atomic_load_n(&this->to_remove_, __ATOMIC_RELAXED) == 0;
689 return this->to_remove_ == 0;
693 void to_remove_add_locked_(
uint32_t count) {
694#if defined(ESPHOME_THREAD_MULTI_ATOMICS)
695 this->to_remove_.fetch_add(count, std::memory_order_relaxed);
696#elif defined(ESPHOME_THREAD_MULTI_NO_ATOMICS)
697 uint32_t v = __atomic_load_n(&this->to_remove_, __ATOMIC_RELAXED);
698 __atomic_store_n(&this->to_remove_, v + count, __ATOMIC_RELAXED);
700 this->to_remove_ += count;
704 void to_remove_decrement_locked_() {
705#if defined(ESPHOME_THREAD_MULTI_ATOMICS)
706 this->to_remove_.fetch_sub(1, std::memory_order_relaxed);
707#elif defined(ESPHOME_THREAD_MULTI_NO_ATOMICS)
708 uint32_t v = __atomic_load_n(&this->to_remove_, __ATOMIC_RELAXED);
709 __atomic_store_n(&this->to_remove_, v - 1, __ATOMIC_RELAXED);
715 void to_remove_clear_locked_() {
716#if defined(ESPHOME_THREAD_MULTI_ATOMICS)
717 this->to_remove_.store(0, std::memory_order_relaxed);
718#elif defined(ESPHOME_THREAD_MULTI_NO_ATOMICS)
719 __atomic_store_n(&this->to_remove_, 0, __ATOMIC_RELAXED);
721 this->to_remove_ = 0;
726#if defined(ESPHOME_THREAD_MULTI_ATOMICS)
727 return this->to_remove_.load(std::memory_order_relaxed);
728#elif defined(ESPHOME_THREAD_MULTI_NO_ATOMICS)
729 return __atomic_load_n(&this->to_remove_, __ATOMIC_RELAXED);
731 return this->to_remove_;
738 SchedulerItem *scheduler_item_pool_head_{
nullptr};
739 size_t scheduler_item_pool_size_{0};
741#ifdef ESPHOME_DEBUG_SCHEDULER
745 size_t debug_live_items_{0};
749 bool debug_verify_no_leak_()
const;
struct @65::@66 __attribute__
Wake the main loop task from an ISR. ISR-safe.
const Component * component
void delay(unsigned long ms)
void retry_handler(const std::shared_ptr< RetryArgs > &args)
const char int const __FlashStringHelper va_list args