mirror of
https://github.com/esphome/esphome.git
synced 2026-09-15 17:18:40 +00:00
[wifi] Replace FreeRTOS queue with LockFreeQueue on ESP-IDF
Replace the FreeRTOS xQueue (xQueueCreate/xQueueSend/xQueueReceive) used for WiFi event passing with a lock-free SPSC queue. This avoids the FreeRTOS kernel spinlock overhead on every loop iteration when checking for events — the common case is an empty queue. The LockFreeQueue::pop() fast path is just two atomic loads and a comparison, vs xQueueReceive which takes a spinlock, checks the queue, and releases the spinlock even when empty. WiFi events are rare (connect/disconnect/scan) so heap allocation for event data is retained — no EventPool needed unlike the BLE path which processes hundreds of events per second. This matches the lock-free pattern already used by esp32_ble.
This commit is contained in:
@@ -39,6 +39,7 @@
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/lock_free_queue.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/util.h"
|
||||
|
||||
@@ -47,8 +48,11 @@ 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)
|
||||
// Lock-free SPSC queue replaces FreeRTOS xQueue to avoid kernel spinlock
|
||||
// overhead on every loop iteration. WiFi events are rare so 64 slots is plenty.
|
||||
// 65 slots = 64 usable (ring buffer reserves one slot to distinguish full from empty)
|
||||
static LockFreeQueue<IDFWiFiEvent, 65> 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)
|
||||
#endif // USE_WIFI_AP
|
||||
@@ -132,11 +136,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 (!s_event_queue.push(to_send)) {
|
||||
delete to_send; // NOLINT(cppcoreguidelines-owning-memory)
|
||||
}
|
||||
}
|
||||
@@ -157,12 +160,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 +721,9 @@ 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;
|
||||
}
|
||||
|
||||
// process event
|
||||
IDFWiFiEvent *data;
|
||||
while ((data = s_event_queue.pop()) != nullptr) {
|
||||
wifi_process_event_(data);
|
||||
|
||||
delete data; // NOLINT(cppcoreguidelines-owning-memory)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user