diff --git a/esphome/components/socket/__init__.py b/esphome/components/socket/__init__.py index 4f6634bd7a..5f4d04eb44 100644 --- a/esphome/components/socket/__init__.py +++ b/esphome/components/socket/__init__.py @@ -126,13 +126,15 @@ def require_wake_loop_threadsafe() -> None: """Mark that wake_loop_threadsafe support is required by a component. Call this from components that need to wake the main event loop from background threads. - - On ESP32: Uses FreeRTOS task notifications (<1 us, no socket needed). - On other platforms: Uses a shared UDP loopback socket mechanism (~208 bytes RAM). + This enables the shared UDP loopback socket mechanism (~208 bytes RAM). + The socket is shared across all components that use this feature. This call is a no-op if networking is not enabled in the configuration. - IMPORTANT: This is for background task context only, NOT ISR context. + IMPORTANT: This is for background thread context only, NOT ISR context. + Socket operations are not safe to call from ISR handlers. + + On ESP32, FreeRTOS task notifications are used instead (no socket needed). Example: from esphome.components import socket diff --git a/esphome/core/application.cpp b/esphome/core/application.cpp index 8950e6029e..29d3c9dac9 100644 --- a/esphome/core/application.cpp +++ b/esphome/core/application.cpp @@ -611,10 +611,7 @@ void Application::unregister_socket_fd(int fd) { if (i < this->socket_fds_.size() - 1) this->socket_fds_[i] = this->socket_fds_.back(); this->socket_fds_.pop_back(); -#ifdef USE_ESP32 - // Unhook the socket's netconn callback - esphome_lwip_unhook_socket(fd); -#else +#ifndef USE_ESP32 this->socket_fds_changed_ = true; // Only recalculate max_fd if we removed the current max if (fd == this->max_fd_) { diff --git a/esphome/core/lwip_fast_select.c b/esphome/core/lwip_fast_select.c index b2cf43b914..69703470f1 100644 --- a/esphome/core/lwip_fast_select.c +++ b/esphome/core/lwip_fast_select.c @@ -10,7 +10,7 @@ // Thread safety analysis // ====================== // Three threads interact with this code: -// 1. Main loop task — calls init, has_data, hook, unhook +// 1. Main loop task — calls init, has_data, hook // 2. LwIP TCP/IP task — calls event_callback (which reads s_original_callback, writes rcvevent) // 3. Background tasks — call wake_main_loop // @@ -31,7 +31,8 @@ // the write), so it always sees the initialized value. // // sock->conn->callback (netconn_callback, 4-byte function pointer): -// Written by main loop in hook_socket() and unhook_socket(). +// Written by main loop in hook_socket(). Never restored — all LwIP sockets share +// the same static event_callback, so the wrapper stays in place permanently. // Read by TCP/IP thread when invoking the callback. // Safe: 32-bit aligned pointer writes are atomic on Xtensa and RISC-V (ESP32). // The TCP/IP thread will see either the old or new pointer atomically — never a @@ -133,17 +134,6 @@ void esphome_lwip_hook_socket(int fd) { sock->conn->callback = esphome_socket_event_callback; } -void esphome_lwip_unhook_socket(int fd) { - struct lwip_sock *sock = lwip_socket_dbg_get_socket(fd); - if (sock == NULL || sock->conn == NULL) - return; - - // Restore original callback. Atomic on ESP32 (32-bit aligned pointer write). - if (s_original_callback != NULL) { - sock->conn->callback = s_original_callback; - } -} - // Wake the main loop from another FreeRTOS task. NOT ISR-safe. void esphome_lwip_wake_main_loop(void) { TaskHandle_t task = s_main_loop_task; diff --git a/esphome/core/lwip_fast_select.h b/esphome/core/lwip_fast_select.h index 4c47c13f20..7d16042c03 100644 --- a/esphome/core/lwip_fast_select.h +++ b/esphome/core/lwip_fast_select.h @@ -23,10 +23,6 @@ bool esphome_lwip_socket_has_data(int fd); /// Must be called from the main loop after socket creation. void esphome_lwip_hook_socket(int fd); -/// Unhook a socket's netconn callback, restoring the original event_callback. -/// Must be called from the main loop before closing the socket. -void esphome_lwip_unhook_socket(int fd); - /// Wake the main loop task from another FreeRTOS task — costs <1 us. /// NOT ISR-safe — must only be called from task context. void esphome_lwip_wake_main_loop(void);