[core][scheduler] add missing process_to_add call (#20961)

Co-authored-by: J. Nick Koston <nick@home-assistant.io>
This commit is contained in:
luar123
2026-10-17 01:33:29 +00:00
committed by GitHub
co-authored by J. Nick Koston
parent 107bebebad
commit 9031df757b
2 changed files with 47 additions and 2 deletions
+3 -2
View File
@@ -442,10 +442,10 @@ uint32_t HOT Scheduler::call(uint32_t now) {
SchedulerNameLog name_log;
bool is_cancelled = is_item_removed_(item);
ESP_LOGD(TAG, " %s '%s/%s' interval=%" PRIu32 " next_execution in %" PRIu64 "ms at %" PRIu64 "%s",
ESP_LOGD(TAG, " %s '%s/%s' interval=%" PRIu32 " next_execution in %" PRId64 "ms at %" PRIu64 "%s",
LOG_STR_ARG(item->get_type_str()), LOG_STR_ARG(item->get_source()),
name_log.format(item->get_name_type(), item->get_name(), item->get_name_hash_or_id()), item->interval,
item->get_next_execution() - now_64, item->get_next_execution(),
(int64_t) (item->get_next_execution() - now_64), item->get_next_execution(),
is_cancelled ? LOG_STR_LITERAL(" [CANCELLED]") : LOG_STR_LITERAL(""));
old_items.push_back(item);
@@ -539,6 +539,7 @@ uint32_t HOT Scheduler::call(uint32_t now) {
// We were removed/cancelled in the function call, recycle and continue
this->to_remove_decrement_locked_();
this->recycle_item_main_loop_(executed_item);
has_added_items |= !this->to_add_.empty();
continue;
}
@@ -0,0 +1,44 @@
#include "esphome/core/component.h"
#include "esphome/core/hal.h"
#include "esphome/core/scheduler.h"
#include <gtest/gtest.h>
#include <chrono>
#include <thread>
namespace esphome::core::testing {
class SchedulerTestComponent : public Component {};
// On a multi-threaded build next_schedule_in() only sees items_, so a timeout a callback leaves in
// to_add_ would let a loop that sleeps on next_schedule_in() oversleep. The case that used to skip
// the merge is a heap item (a zero timeout would run from the defer queue instead) whose callback
// cancels its own item.
TEST(Scheduler, TimeoutAddedByACancelledCallbackIsVisibleAfterCall) {
Scheduler scheduler;
SchedulerTestComponent component;
bool fired = false;
scheduler.set_timeout(&component, "first", 1, [&]() {
scheduler.cancel_timeout(&component, "first");
scheduler.set_timeout(&component, "second", 100, [&fired]() { fired = true; });
});
std::this_thread::sleep_for(std::chrono::milliseconds(5));
scheduler.call(millis());
auto next = scheduler.next_schedule_in(millis());
ASSERT_TRUE(next.has_value());
const uint32_t next_ms = next.has_value() ? *next : 0;
EXPECT_GT(next_ms, 50u);
EXPECT_LE(next_ms, 100u);
EXPECT_FALSE(fired);
std::this_thread::sleep_for(std::chrono::milliseconds(120));
scheduler.call(millis());
EXPECT_TRUE(fired);
EXPECT_FALSE(scheduler.next_schedule_in(millis()).has_value());
scheduler.trim_freelist();
}
} // namespace esphome::core::testing