diff --git a/esphome/core/lock_free_queue.h b/esphome/core/lock_free_queue.h index 7e29080f8e..e7c9ddb11f 100644 --- a/esphome/core/lock_free_queue.h +++ b/esphome/core/lock_free_queue.h @@ -118,9 +118,26 @@ template class NotifyingLockFreeQueue : public LockFreeQu bool was_empty; bool result = this->push_internal_(element, was_empty); - // Notify if push succeeded and queue was empty - if (result && task_to_notify_ != nullptr && was_empty) { - xTaskNotifyGive(task_to_notify_); + // Notify optimization: only notify if we need to + if (result && task_to_notify_ != nullptr) { + if (was_empty) { + // Queue was empty - consumer might be going to sleep, must notify + xTaskNotifyGive(task_to_notify_); + } else { + // Queue wasn't empty - check if consumer has caught up to previous tail + uint8_t current_tail = this->tail_.load(std::memory_order_relaxed); + uint8_t head_after = this->head_.load(std::memory_order_acquire); + // We just pushed, so go back one position to get the old tail + uint8_t previous_tail = (current_tail + SIZE - 1) % SIZE; + if (head_after == previous_tail) { + // Consumer just caught up to where tail was - might go to sleep, must notify + // Note: There's a benign race here - between reading head_after and calling + // xTaskNotifyGive(), the consumer could advance further. This would result + // in an unnecessary wake-up, but is harmless and extremely rare in practice. + xTaskNotifyGive(task_to_notify_); + } + // Otherwise: consumer is still behind, no need to notify + } } return result;