diff --git a/esphome/core/application.cpp b/esphome/core/application.cpp index 1d01d6ff0b..cd75859880 100644 --- a/esphome/core/application.cpp +++ b/esphome/core/application.cpp @@ -115,12 +115,8 @@ void Application::setup() { #endif #if defined(USE_ESP32) || defined(USE_LIBRETINY) - // Save main loop task handle for wake_loop_*() FreeRTOS notifications. - g_main_task_handle = xTaskGetCurrentTaskHandle(); -#endif -#ifdef USE_LWIP_FAST_SELECT - // Initialize fast select: hooks socket monitoring for direct rcvevent reads. - esphome_lwip_fast_select_init(); + // Save main loop task handle for wake_loop_*() / fast select FreeRTOS notifications. + esphome_main_task_handle = xTaskGetCurrentTaskHandle(); #endif #ifdef USE_HOST // Set up wake socket for waking main loop from tasks (platforms without fast select only) diff --git a/esphome/core/lwip_fast_select.c b/esphome/core/lwip_fast_select.c index a695fa396b..c850e88e20 100644 --- a/esphome/core/lwip_fast_select.c +++ b/esphome/core/lwip_fast_select.c @@ -63,11 +63,11 @@ // // Shared state and safety rationale: // -// s_main_loop_task (TaskHandle_t, 4 bytes): +// esphome_main_task_handle (TaskHandle_t, 4 bytes): // Written once by main loop in init(). Read by TCP/IP thread (in callback) // and background tasks (in wake). // Safe: write-once-then-read pattern. Socket hooks may run before init(), -// but the NULL check on s_main_loop_task in the callback provides correct +// but the NULL check on esphome_main_task_handle in the callback provides correct // degraded behavior — notifications are simply skipped until init() completes. // // s_original_callback (netconn_callback, 4-byte function pointer): @@ -123,6 +123,7 @@ #endif #include "esphome/core/lwip_fast_select.h" +#include "esphome/core/main_task.h" #include @@ -157,8 +158,7 @@ _Static_assert(offsetof(struct lwip_sock, rcvevent) % sizeof(((struct lwip_sock _Static_assert(offsetof(struct lwip_sock, rcvevent) == ESPHOME_LWIP_SOCK_RCVEVENT_OFFSET, "lwip_sock.rcvevent offset changed — update ESPHOME_LWIP_SOCK_RCVEVENT_OFFSET in lwip_fast_select.h"); -// Task handle for the main loop — written once in init(), read from TCP/IP and background tasks. -static TaskHandle_t s_main_loop_task = NULL; +// Task handle is in main_task.c (esphome_main_task_handle) — shared with wake.h. // Saved original event_callback pointer — written once in first hook_socket(), read from TCP/IP task. static netconn_callback s_original_callback = NULL; @@ -177,15 +177,13 @@ static void esphome_socket_event_callback(struct netconn *conn, enum netconn_evt // (rcvevent++ with a NULL pbuf or error in recvmbox), so error conditions // already wake the main loop through the RCVPLUS path. if (evt == NETCONN_EVT_RCVPLUS) { - TaskHandle_t task = s_main_loop_task; + TaskHandle_t task = esphome_main_task_handle; if (task != NULL) { xTaskNotifyGive(task); } } } -void esphome_lwip_fast_select_init(void) { s_main_loop_task = xTaskGetCurrentTaskHandle(); } - // lwip_socket_dbg_get_socket() is a thin wrapper around the static // tryget_socket_unconn_nouse() — a direct array lookup without the refcount // that get_socket()/done_socket() uses. This is safe because: @@ -234,7 +232,7 @@ bool esphome_lwip_set_nodelay(struct lwip_sock *sock, bool enable) { // 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; + TaskHandle_t task = esphome_main_task_handle; if (task != NULL) { xTaskNotifyGive(task); } @@ -242,7 +240,7 @@ void esphome_lwip_wake_main_loop(void) { // Wake the main loop from an ISR. ISR-safe variant. void IRAM_ATTR esphome_lwip_wake_main_loop_from_isr(int *px_higher_priority_task_woken) { - TaskHandle_t task = s_main_loop_task; + TaskHandle_t task = esphome_main_task_handle; if (task != NULL) { vTaskNotifyGiveFromISR(task, (BaseType_t *) px_higher_priority_task_woken); } diff --git a/esphome/core/lwip_fast_select.h b/esphome/core/lwip_fast_select.h index 50706ba9f6..96337a1dcd 100644 --- a/esphome/core/lwip_fast_select.h +++ b/esphome/core/lwip_fast_select.h @@ -20,10 +20,6 @@ enum { ESPHOME_LWIP_SOCK_RCVEVENT_OFFSET = 8 }; extern "C" { #endif -/// Initialize fast select — must be called from the main loop task during setup(). -/// Saves the current task handle for xTaskNotifyGive() wake notifications. -void esphome_lwip_fast_select_init(void); - /// Look up a LwIP socket struct from a file descriptor. /// Returns NULL if fd is invalid or the socket/netconn is not initialized. /// Use this at registration time to cache the pointer for esphome_lwip_socket_has_data(). diff --git a/esphome/core/main_task.c b/esphome/core/main_task.c new file mode 100644 index 0000000000..52d9c2951a --- /dev/null +++ b/esphome/core/main_task.c @@ -0,0 +1,5 @@ +#include "esphome/core/main_task.h" + +#if defined(USE_ESP32) || defined(USE_LIBRETINY) +TaskHandle_t esphome_main_task_handle = NULL; +#endif diff --git a/esphome/core/main_task.h b/esphome/core/main_task.h new file mode 100644 index 0000000000..d46a54cb19 --- /dev/null +++ b/esphome/core/main_task.h @@ -0,0 +1,22 @@ +#pragma once + +/// Main loop task handle — shared between wake.h (C++) and lwip_fast_select.c (C). +/// Set once during Application::setup() via xTaskGetCurrentTaskHandle(). + +#ifdef USE_ESP32 +#include +#include +#elif defined(USE_LIBRETINY) +#include +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +extern TaskHandle_t esphome_main_task_handle; + +#ifdef __cplusplus +} +#endif diff --git a/esphome/core/wake.cpp b/esphome/core/wake.cpp index 18fbbfd4f4..723988509b 100644 --- a/esphome/core/wake.cpp +++ b/esphome/core/wake.cpp @@ -12,12 +12,6 @@ namespace esphome { -// === ESP32/LibreTiny — FreeRTOS task handle === -#if defined(USE_ESP32) || defined(USE_LIBRETINY) -// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) -TaskHandle_t g_main_task_handle = nullptr; -#endif - // === ESP32 — IRAM_ATTR entry points === #ifdef USE_ESP32 void IRAM_ATTR wake_loop_isrsafe(int *px_higher_priority_task_woken) { diff --git a/esphome/core/wake.h b/esphome/core/wake.h index 6c3343e135..79037c8264 100644 --- a/esphome/core/wake.h +++ b/esphome/core/wake.h @@ -8,13 +8,7 @@ #include "esphome/core/hal.h" #if defined(USE_ESP32) || defined(USE_LIBRETINY) -#ifdef USE_ESP32 -#include -#include -#else -#include -#include -#endif +#include "esphome/core/main_task.h" #endif #ifdef USE_ESP8266 #include @@ -31,41 +25,35 @@ namespace esphome { extern volatile bool g_main_loop_woke; #endif -// === ESP32/LibreTiny — FreeRTOS task handle for wake notifications === -#if defined(USE_ESP32) || defined(USE_LIBRETINY) -// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) -extern TaskHandle_t g_main_task_handle; -#endif - // === ESP32 === #if defined(USE_ESP32) /// Inline impl — ISR callers inline this into IRAM. inline void ESPHOME_ALWAYS_INLINE wake_loop_isrsafe_inline_(int *px_higher_priority_task_woken) { - if (g_main_task_handle != nullptr) - vTaskNotifyGiveFromISR(g_main_task_handle, (BaseType_t *) px_higher_priority_task_woken); + if (esphome_main_task_handle != nullptr) + vTaskNotifyGiveFromISR(esphome_main_task_handle, (BaseType_t *) px_higher_priority_task_woken); } /// IRAM_ATTR entry point — defined in wake.cpp. void wake_loop_isrsafe(int *px_higher_priority_task_woken); /// Inline impl — ISR callers inline this into IRAM. Uses xPortInIsrContext() to pick safe API. inline void ESPHOME_ALWAYS_INLINE wake_loop_any_context_inline_() { - if (g_main_task_handle == nullptr) + if (esphome_main_task_handle == nullptr) return; if (xPortInIsrContext()) { BaseType_t xHigherPriorityTaskWoken = pdFALSE; - vTaskNotifyGiveFromISR(g_main_task_handle, &xHigherPriorityTaskWoken); + vTaskNotifyGiveFromISR(esphome_main_task_handle, &xHigherPriorityTaskWoken); portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } else { - xTaskNotifyGive(g_main_task_handle); + xTaskNotifyGive(esphome_main_task_handle); } } /// IRAM_ATTR entry point — defined in wake.cpp. void wake_loop_any_context(); inline void wake_loop_threadsafe() { - if (g_main_task_handle != nullptr) - xTaskNotifyGive(g_main_task_handle); + if (esphome_main_task_handle != nullptr) + xTaskNotifyGive(esphome_main_task_handle); } namespace internal { @@ -82,13 +70,13 @@ inline void wakeable_delay(uint32_t ms) { #elif defined(USE_LIBRETINY) inline void wake_loop_any_context() { - if (g_main_task_handle != nullptr) - xTaskNotifyGive(g_main_task_handle); + if (esphome_main_task_handle != nullptr) + xTaskNotifyGive(esphome_main_task_handle); } inline void wake_loop_threadsafe() { - if (g_main_task_handle != nullptr) - xTaskNotifyGive(g_main_task_handle); + if (esphome_main_task_handle != nullptr) + xTaskNotifyGive(esphome_main_task_handle); } namespace internal {