[wifi] Replace FreeRTOS queue with LockFreeQueue on ESP-IDF (#15306)

This commit is contained in:
J. Nick Koston
2026-03-30 08:24:34 -10:00
committed by GitHub
parent 8969eb76e9
commit 46ea61666e
2 changed files with 19 additions and 18 deletions
+11
View File
@@ -6,6 +6,9 @@
#include "esphome/core/automation.h"
#include "esphome/core/component.h"
#include "esphome/core/helpers.h"
#ifdef USE_ESP32
#include "esphome/core/lock_free_queue.h"
#endif
#include "esphome/core/string_ref.h"
#include <span>
@@ -727,6 +730,7 @@ class WiFiComponent final : public Component {
#ifdef USE_ESP32
void wifi_process_event_(IDFWiFiEvent *data);
friend void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data);
#endif
#ifdef USE_RP2040
@@ -871,6 +875,13 @@ class WiFiComponent final : public Component {
bool is_high_performance_mode_{false};
#endif
#ifdef USE_ESP32
// Lock-free SPSC queue for WiFi events from ESP-IDF event handler.
// 17 slots = 16 usable (ring buffer reserves one slot). WiFi events are rare.
// Placed at end of class to avoid padding between smaller fields.
LockFreeQueue<IDFWiFiEvent, 17> event_queue_;
#endif
private:
// Stores a pointer to a string literal (static storage duration).
// ONLY set from Python-generated code with string literals - never dynamic strings.
@@ -47,7 +47,6 @@ namespace esphome::wifi {
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)
#ifdef USE_WIFI_AP
static esp_netif_t *s_ap_netif = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
@@ -132,11 +131,10 @@ void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, voi
return;
}
// copy to heap to keep queue object small
// copy to heap — WiFi events are rare so heap alloc is fine
auto *to_send = new IDFWiFiEvent; // NOLINT(cppcoreguidelines-owning-memory)
memcpy(to_send, &event, sizeof(IDFWiFiEvent));
// don't block, we may miss events but the core can handle that
if (xQueueSend(s_event_queue, &to_send, 0L) != pdPASS) {
if (!global_wifi_component->event_queue_.push(to_send)) {
delete to_send; // NOLINT(cppcoreguidelines-owning-memory)
}
}
@@ -157,12 +155,6 @@ void WiFiComponent::wifi_pre_setup_() {
ESP_LOGE(TAG, "xEventGroupCreate failed");
return;
}
// NOLINTNEXTLINE(bugprone-sizeof-expression)
s_event_queue = xQueueCreate(64, sizeof(IDFWiFiEvent *));
if (s_event_queue == nullptr) {
ESP_LOGE(TAG, "xQueueCreate failed");
return;
}
err = esp_event_loop_create_default();
if (err != ERR_OK) {
ESP_LOGE(TAG, "esp_event_loop_create_default failed: %s", esp_err_to_name(err));
@@ -724,16 +716,14 @@ const char *get_disconnect_reason_str(uint8_t reason) {
}
void WiFiComponent::wifi_loop_() {
while (true) {
IDFWiFiEvent *data;
if (xQueueReceive(s_event_queue, &data, 0L) != pdTRUE) {
// no event ready
break;
}
uint16_t dropped = this->event_queue_.get_and_reset_dropped_count();
if (dropped > 0) {
ESP_LOGW(TAG, "Dropped %u WiFi events due to buffer overflow", dropped);
}
// process event
IDFWiFiEvent *data;
while ((data = this->event_queue_.pop()) != nullptr) {
wifi_process_event_(data);
delete data; // NOLINT(cppcoreguidelines-owning-memory)
}
}