[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.
This commit is contained in:
J. Nick Koston
2026-03-29 19:09:03 -10:00
parent 18168ad7fd
commit 641289915f
@@ -13,6 +13,7 @@
#include <freertos/task.h>
#include <algorithm>
#include <atomic>
#include <cinttypes>
#include <memory>
#include <utility>
@@ -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<uint32_t> 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);