[core] Clamp underflowed blocking_time in warn_blocking cold path

When millis() < started_ (e.g. scheduler passes a now value slightly
ahead of real millis()), the uint32_t subtraction in finish() wraps to
~4 billion. This caused warn_blocking to fire on every call since the
underflowed value always exceeds the uint16_t threshold max (65535).

Fix by clamping blocking_time to uint16_t max in the cold warn_blocking
path. After one warning, should_warn_of_blocking() saturates the
threshold to 65535 and subsequent clamped values (65535) don't exceed it.
Zero cost on the hot path — the clamp is in the noinline cold function.
This commit is contained in:
J. Nick Koston
2026-03-17 01:11:48 -10:00
parent 885d7c3938
commit 2785edaac8
2 changed files with 10 additions and 8 deletions
+7
View File
@@ -512,6 +512,13 @@ void PollingComponent::set_update_interval(uint32_t update_interval) { this->upd
void __attribute__((noinline, cold))
WarnIfComponentBlockingGuard::warn_blocking(Component *component, uint32_t blocking_time) {
// Clamp underflowed values: if millis() < started_ (e.g. scheduler passes
// a `now` slightly ahead of real millis()), the subtraction wraps to ~4 billion.
// Clamping to uint16_t max lets should_warn_of_blocking() saturate the
// threshold and suppress further warnings.
if (blocking_time > std::numeric_limits<uint16_t>::max()) {
blocking_time = std::numeric_limits<uint16_t>::max();
}
bool should_warn;
if (component != nullptr) {
should_warn = component->should_warn_of_blocking(blocking_time);
+3 -8
View File
@@ -594,17 +594,12 @@ class WarnIfComponentBlockingGuard {
// Inlined: the fast path is just millis() + subtract + compare
inline uint32_t HOT finish() {
uint32_t curr_time = millis();
uint32_t blocking_time = curr_time - this->started_;
#ifdef USE_RUNTIME_STATS
this->record_runtime_stats_();
#endif
// Guard against underflow: if curr_time < started_, the subtraction wraps
// to a huge value. This can happen when the scheduler passes a `now` value
// slightly ahead of real millis() (e.g. from execute_item_ return values).
if (curr_time >= this->started_) [[likely]] {
uint32_t blocking_time = curr_time - this->started_;
if (blocking_time > WARN_IF_BLOCKING_OVER_MS) [[unlikely]] {
warn_blocking(this->component_, blocking_time);
}
if (blocking_time > WARN_IF_BLOCKING_OVER_MS) [[unlikely]] {
warn_blocking(this->component_, blocking_time);
}
return curr_time;
}