From 641289915f76cc51b955cb3fad537f0d89bcb5c4 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 29 Mar 2026 19:09:03 -1000 Subject: [PATCH] [wifi] Skip xQueueReceive when no WiFi events pending on ESP-IDF Add an atomic counter incremented when events are enqueued and decremented when dequeued. wifi_loop_() checks this counter before calling xQueueReceive, avoiding the FreeRTOS kernel call on every loop iteration when no events are pending (the common case). This matches the fast-path pattern used by the scheduler to avoid lock overhead when there is nothing to process. --- esphome/components/wifi/wifi_component_esp_idf.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/esphome/components/wifi/wifi_component_esp_idf.cpp b/esphome/components/wifi/wifi_component_esp_idf.cpp index d8b3db9667..cc8af38f75 100644 --- a/esphome/components/wifi/wifi_component_esp_idf.cpp +++ b/esphome/components/wifi/wifi_component_esp_idf.cpp @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -48,7 +49,10 @@ static const char *const TAG = "wifi_esp32"; static EventGroupHandle_t s_wifi_event_group; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) static QueueHandle_t s_event_queue; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) -static esp_netif_t *s_sta_netif = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +// Fast-path counter to skip xQueueReceive kernel call when queue is empty. +// Incremented from event handler (ISR-safe context), read from main loop. +static std::atomic s_event_count{0}; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +static esp_netif_t *s_sta_netif = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) #ifdef USE_WIFI_AP static esp_netif_t *s_ap_netif = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) #endif // USE_WIFI_AP @@ -138,6 +142,8 @@ void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, voi // don't block, we may miss events but the core can handle that if (xQueueSend(s_event_queue, &to_send, 0L) != pdPASS) { delete to_send; // NOLINT(cppcoreguidelines-owning-memory) + } else { + s_event_count.fetch_add(1, std::memory_order_release); } } @@ -724,12 +730,18 @@ const char *get_disconnect_reason_str(uint8_t reason) { } void WiFiComponent::wifi_loop_() { + // Fast path: skip xQueueReceive kernel call when no events are pending. + // The atomic load is much cheaper than entering the FreeRTOS kernel. + if (s_event_count.load(std::memory_order_acquire) == 0) + return; + while (true) { IDFWiFiEvent *data; if (xQueueReceive(s_event_queue, &data, 0L) != pdTRUE) { // no event ready break; } + s_event_count.fetch_sub(1, std::memory_order_relaxed); // process event wifi_process_event_(data);