mirror of
https://github.com/esphome/esphome.git
synced 2026-09-23 21:14:03 +00:00
[core] Inline yield_with_select_ fast path for LWIP_FAST_SELECT
When USE_LWIP_FAST_SELECT is defined (ESP32/LibreTiny), move the yield_with_select_ implementation to application.h as always_inline. This eliminates another stack frame from the hot loop path on these platforms. The fallback select()/delay() paths for other platforms remain in application.cpp.
This commit is contained in:
@@ -12,13 +12,6 @@
|
||||
#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"
|
||||
@@ -557,34 +550,11 @@ void Application::unregister_socket_fd(int fd) {
|
||||
|
||||
#endif
|
||||
|
||||
// When USE_LWIP_FAST_SELECT is defined, yield_with_select_ is inlined in application.h
|
||||
#if !defined(USE_SOCKET_SELECT_SUPPORT) || !defined(USE_LWIP_FAST_SELECT)
|
||||
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_LWIP_FAST_SELECT)
|
||||
// Fast path (ESP32/LibreTiny): reads rcvevent directly from cached lwip_sock pointers.
|
||||
// Safe because this runs on the main loop which owns socket lifetime (create, read, close).
|
||||
if (delay_ms == 0) [[unlikely]] {
|
||||
yield();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if any socket already has pending data before sleeping.
|
||||
// If a socket still has unread data (rcvevent > 0) but the task notification was already
|
||||
// consumed, ulTaskNotifyTake would block until timeout — adding up to delay_ms latency.
|
||||
// This scan preserves select() semantics: return immediately when any fd is ready.
|
||||
for (struct lwip_sock *sock : this->monitored_sockets_) {
|
||||
if (esphome_lwip_socket_has_data(sock)) {
|
||||
yield();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Sleep with instant wake via FreeRTOS task notification.
|
||||
// Woken by: callback wrapper (socket data arrives), wake_loop_threadsafe() (other tasks), or timeout.
|
||||
// Without USE_WAKE_LOOP_THREADSAFE, only hooked socket callbacks wake the task —
|
||||
// background tasks won't call wake, so this degrades to a pure timeout (same as old select path).
|
||||
ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(delay_ms));
|
||||
|
||||
#elif defined(USE_SOCKET_SELECT_SUPPORT)
|
||||
#if defined(USE_SOCKET_SELECT_SUPPORT)
|
||||
// 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]] {
|
||||
@@ -643,6 +613,7 @@ void Application::yield_with_select_(uint32_t delay_ms) {
|
||||
delay(delay_ms);
|
||||
#endif
|
||||
}
|
||||
#endif // !defined(USE_SOCKET_SELECT_SUPPORT) || !defined(USE_LWIP_FAST_SELECT)
|
||||
|
||||
// App storage — asm label shares the linker symbol with "extern Application App".
|
||||
// char[] is trivially destructible, so no __cxa_atexit or destructor chain is emitted.
|
||||
|
||||
@@ -27,6 +27,13 @@
|
||||
#ifdef USE_SOCKET_SELECT_SUPPORT
|
||||
#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
|
||||
#else
|
||||
#include <sys/select.h>
|
||||
#ifdef USE_WAKE_LOOP_THREADSAFE
|
||||
@@ -628,7 +635,11 @@ class Application {
|
||||
void feed_wdt_arch_();
|
||||
|
||||
/// Perform a delay while also monitoring socket file descriptors for readiness
|
||||
#if defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_LWIP_FAST_SELECT)
|
||||
inline void ESPHOME_ALWAYS_INLINE yield_with_select_(uint32_t delay_ms);
|
||||
#else
|
||||
void yield_with_select_(uint32_t delay_ms);
|
||||
#endif
|
||||
|
||||
#if defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_WAKE_LOOP_THREADSAFE) && !defined(USE_LWIP_FAST_SELECT)
|
||||
void setup_wake_loop_threadsafe_(); // Create wake notification socket
|
||||
@@ -883,4 +894,32 @@ inline void ESPHOME_ALWAYS_INLINE Application::loop() {
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_LWIP_FAST_SELECT)
|
||||
inline void ESPHOME_ALWAYS_INLINE Application::yield_with_select_(uint32_t delay_ms) {
|
||||
// Fast path (ESP32/LibreTiny): reads rcvevent directly from cached lwip_sock pointers.
|
||||
// Safe because this runs on the main loop which owns socket lifetime (create, read, close).
|
||||
if (delay_ms == 0) [[unlikely]] {
|
||||
yield();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if any socket already has pending data before sleeping.
|
||||
// If a socket still has unread data (rcvevent > 0) but the task notification was already
|
||||
// consumed, ulTaskNotifyTake would block until timeout — adding up to delay_ms latency.
|
||||
// This scan preserves select() semantics: return immediately when any fd is ready.
|
||||
for (struct lwip_sock *sock : this->monitored_sockets_) {
|
||||
if (esphome_lwip_socket_has_data(sock)) {
|
||||
yield();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Sleep with instant wake via FreeRTOS task notification.
|
||||
// Woken by: callback wrapper (socket data arrives), wake_loop_threadsafe() (other tasks), or timeout.
|
||||
// Without USE_WAKE_LOOP_THREADSAFE, only hooked socket callbacks wake the task —
|
||||
// background tasks won't call wake, so this degrades to a pure timeout (same as old select path).
|
||||
ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(delay_ms));
|
||||
}
|
||||
#endif // defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_LWIP_FAST_SELECT)
|
||||
|
||||
} // namespace esphome
|
||||
|
||||
Reference in New Issue
Block a user