[core] Extend fast select optimization to LibreTiny platforms (#14254)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
J. Nick KostonandClaude Opus 4.6 authored and GitHub committed 2026-02-27 06:03:37 -10:00
1 parent bd3f8e006c
commit 0f7ac1726d
7 files changed
+100 -62

No files matched your search

+25 -18
View File
@@ -9,10 +9,17 @@
#endif
#ifdef USE_ESP32
#include <esp_chip_info.h>
#endif
#ifdef USE_LWIP_FAST_SELECT
#include "esphome/core/lwip_fast_select.h"
#ifdef USE_ESP32
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#else
#include <FreeRTOS.h>
#include <task.h>
#endif
#endif // USE_LWIP_FAST_SELECT
#include "esphome/core/version.h"
#include "esphome/core/hal.h"
#include <algorithm>
@@ -147,14 +154,14 @@ void Application::setup() {
clear_setup_priority_overrides();
#endif
#if defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_ESP32)
#if defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_LWIP_FAST_SELECT)
// Initialize fast select: saves main loop task handle for xTaskNotifyGive wake.
// Always init on ESP32 — the fast path (rcvevent reads + ulTaskNotifyTake) is used
// unconditionally when USE_SOCKET_SELECT_SUPPORT is enabled.
// The fast path (rcvevent reads + ulTaskNotifyTake) is used unconditionally
// when USE_LWIP_FAST_SELECT is enabled (ESP32 and LibreTiny).
esphome_lwip_fast_select_init();
#endif
#if defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_WAKE_LOOP_THREADSAFE) && !defined(USE_ESP32)
// Set up wake socket for waking main loop from tasks (non-ESP32 only)
#if defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_WAKE_LOOP_THREADSAFE) && !defined(USE_LWIP_FAST_SELECT)
// Set up wake socket for waking main loop from tasks (platforms without fast select only)
this->setup_wake_loop_threadsafe_();
#endif
@@ -532,7 +539,7 @@ void Application::enable_pending_loops_() {
}
void Application::before_loop_tasks_(uint32_t loop_start_time) {
#if defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_WAKE_LOOP_THREADSAFE) && !defined(USE_ESP32)
#if defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_WAKE_LOOP_THREADSAFE) && !defined(USE_LWIP_FAST_SELECT)
// Drain wake notifications first to clear socket for next wake
this->drain_wake_notifications_();
#endif
@@ -585,7 +592,7 @@ bool Application::register_socket_fd(int fd) {
#endif
this->socket_fds_.push_back(fd);
#ifdef USE_ESP32
#ifdef USE_LWIP_FAST_SELECT
// Hook the socket's netconn callback for instant wake on receive events
esphome_lwip_hook_socket(fd);
#else
@@ -609,12 +616,13 @@ void Application::unregister_socket_fd(int fd) {
continue;
// Swap with last element and pop - O(1) removal since order doesn't matter.
// No need to unhook the netconn callback on ESP32 — all LwIP sockets share
// the same static event_callback, and the socket will be closed by the caller.
// No need to unhook the netconn callback on fast select platforms — all LwIP
// sockets share the same static event_callback, and the socket will be closed
// by the caller.
if (i < this->socket_fds_.size() - 1)
this->socket_fds_[i] = this->socket_fds_.back();
this->socket_fds_.pop_back();
#ifndef USE_ESP32
#ifndef USE_LWIP_FAST_SELECT
this->socket_fds_changed_ = true;
// Only recalculate max_fd if we removed the current max
if (fd == this->max_fd_) {
@@ -633,8 +641,8 @@ void Application::unregister_socket_fd(int fd) {
void Application::yield_with_select_(uint32_t delay_ms) {
// Delay while monitoring sockets. When delay_ms is 0, always yield() to ensure other tasks run.
#if defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_ESP32)
// ESP32 fast path: reads rcvevent directly via lwip_socket_dbg_get_socket() (~215 ns per socket).
#if defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_LWIP_FAST_SELECT)
// Fast path (ESP32/LibreTiny): reads rcvevent directly via lwip_socket_dbg_get_socket().
// Safe because this runs on the main loop which owns socket lifetime (create, read, close).
if (delay_ms == 0) [[unlikely]] {
yield();
@@ -659,9 +667,8 @@ void Application::yield_with_select_(uint32_t delay_ms) {
ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(delay_ms));
#elif defined(USE_SOCKET_SELECT_SUPPORT)
// Non-ESP32 select() path (LibreTiny bk72xx/rtl87xx, host platform).
// ESP32 is excluded by the #if above — both BSD_SOCKETS and LWIP_SOCKETS on ESP32
// use LwIP under the hood, so the fast path handles all ESP32 socket implementations.
// Fallback select() path (host platform and any future platforms without fast select).
// ESP32 and LibreTiny are excluded by the #if above — they use the fast path.
if (!this->socket_fds_.empty()) [[likely]] {
// Update fd_set if socket list has changed
if (this->socket_fds_changed_) [[unlikely]] {
@@ -725,12 +732,12 @@ alignas(Application) char app_storage[sizeof(Application)] asm("_ZN7esphome3AppE
#if defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_WAKE_LOOP_THREADSAFE)
#ifdef USE_ESP32
#ifdef USE_LWIP_FAST_SELECT
void Application::wake_loop_threadsafe() {
// Direct FreeRTOS task notification — <1 us, task context only (NOT ISR-safe)
esphome_lwip_wake_main_loop();
}
#else // !USE_ESP32
#else // !USE_LWIP_FAST_SELECT
void Application::setup_wake_loop_threadsafe_() {
// Create UDP socket for wake notifications
@@ -798,7 +805,7 @@ void Application::wake_loop_threadsafe() {
lwip_send(this->wake_socket_fd_, &dummy, 1, 0);
}
}
#endif // USE_ESP32
#endif // USE_LWIP_FAST_SELECT
#endif // defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_WAKE_LOOP_THREADSAFE)