mirror of
https://github.com/esphome/esphome.git
synced 2026-09-18 18:48:39 +00:00
[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:
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user