mirror of
https://github.com/esphome/esphome.git
synced 2026-09-21 12:08:38 +00:00
[core] Wake main loop from ISR in enable_loop_soon_any_context() (#14383)
This commit is contained in:
@@ -189,9 +189,10 @@ async def to_code(config):
|
||||
cg.add_define("USE_SOCKET_IMPL_BSD_SOCKETS")
|
||||
cg.add_define("USE_SOCKET_SELECT_SUPPORT")
|
||||
# ESP32 and LibreTiny both have LwIP >= 2.1.3 with lwip_socket_dbg_get_socket()
|
||||
# and FreeRTOS task notifications — enable fast select to bypass lwip_select()
|
||||
if CORE.is_esp32 or CORE.is_libretiny:
|
||||
cg.add_define("USE_LWIP_FAST_SELECT")
|
||||
# and FreeRTOS task notifications — enable fast select to bypass lwip_select().
|
||||
# Only when not using lwip_tcp, which does not provide select() support.
|
||||
if (CORE.is_esp32 or CORE.is_libretiny) and impl != IMPLEMENTATION_LWIP_TCP:
|
||||
cg.add_build_flag("-DUSE_LWIP_FAST_SELECT")
|
||||
|
||||
|
||||
def FILTER_SOURCE_FILES() -> list[str]:
|
||||
|
||||
@@ -501,7 +501,7 @@ class Application {
|
||||
void wake_loop_threadsafe();
|
||||
#endif
|
||||
|
||||
#if defined(USE_WAKE_LOOP_THREADSAFE) && defined(USE_LWIP_FAST_SELECT)
|
||||
#ifdef USE_LWIP_FAST_SELECT
|
||||
/// Wake the main event loop from an ISR.
|
||||
/// Uses vTaskNotifyGiveFromISR() — <1 us, ISR-safe.
|
||||
/// Only available on platforms with fast select (ESP32, LibreTiny).
|
||||
@@ -509,6 +509,12 @@ class Application {
|
||||
static void IRAM_ATTR wake_loop_isrsafe(int *px_higher_priority_task_woken) {
|
||||
esphome_lwip_wake_main_loop_from_isr(px_higher_priority_task_woken);
|
||||
}
|
||||
|
||||
#ifdef USE_ESP32
|
||||
/// Wake the main event loop from any context (ISR, thread, or main loop).
|
||||
/// Detects the calling context and uses the appropriate FreeRTOS API.
|
||||
static void IRAM_ATTR wake_loop_any_context() { esphome_lwip_wake_main_loop_any_context(); }
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
@@ -315,7 +315,7 @@ void IRAM_ATTR HOT Component::enable_loop_soon_any_context() {
|
||||
// This method is thread and ISR-safe because:
|
||||
// 1. Only performs simple assignments to volatile variables (atomic on all platforms)
|
||||
// 2. No read-modify-write operations that could be interrupted
|
||||
// 3. No memory allocation, object construction, or function calls
|
||||
// 3. No memory allocation or object construction; on ESP32 the only call (wake_loop_any_context) is ISR-safe
|
||||
// 4. IRAM_ATTR ensures code is in IRAM, not flash (required for ISR execution)
|
||||
// 5. Components are never destroyed, so no use-after-free concerns
|
||||
// 6. App is guaranteed to be initialized before any ISR could fire
|
||||
@@ -323,6 +323,12 @@ void IRAM_ATTR HOT Component::enable_loop_soon_any_context() {
|
||||
// 8. Race condition with main loop is handled by clearing flag before processing
|
||||
this->pending_enable_loop_ = true;
|
||||
App.has_pending_enable_loop_requests_ = true;
|
||||
#if defined(USE_LWIP_FAST_SELECT) && defined(USE_ESP32)
|
||||
// Wake the main loop if sleeping in ulTaskNotifyTake(). Without this,
|
||||
// the main loop would not wake until the select timeout expires (~16ms).
|
||||
// Uses xPortInIsrContext() to choose the correct FreeRTOS notify API.
|
||||
Application::wake_loop_any_context();
|
||||
#endif
|
||||
}
|
||||
void Component::reset_to_construction_state() {
|
||||
if ((this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_FAILED) {
|
||||
|
||||
@@ -105,10 +105,9 @@
|
||||
// critical sections). Multiple concurrent xTaskNotifyGive calls are safe —
|
||||
// the notification count simply increments.
|
||||
|
||||
// USE_ESP32 and USE_LIBRETINY are compiler -D flags, so they are always visible in this .c file.
|
||||
// Feature macros like USE_LWIP_FAST_SELECT may come from generated headers that are not included here,
|
||||
// so this implementation is enabled based on platform flags instead of USE_LWIP_FAST_SELECT.
|
||||
#if defined(USE_ESP32) || defined(USE_LIBRETINY)
|
||||
// USE_LWIP_FAST_SELECT is set via -D build flag (not cg.add_define) so it is
|
||||
// visible in both .c and .cpp translation units.
|
||||
#ifdef USE_LWIP_FAST_SELECT
|
||||
|
||||
// LwIP headers must come first — they define netconn_callback, struct lwip_sock, etc.
|
||||
#include <lwip/api.h>
|
||||
@@ -239,4 +238,19 @@ void IRAM_ATTR esphome_lwip_wake_main_loop_from_isr(int *px_higher_priority_task
|
||||
}
|
||||
}
|
||||
|
||||
#endif // defined(USE_ESP32) || defined(USE_LIBRETINY)
|
||||
// Wake the main loop from any context (ISR, thread, or main loop).
|
||||
// ESP32-only: uses xPortInIsrContext() to detect ISR context.
|
||||
// LibreTiny is excluded because it lacks IRAM_ATTR support needed for ISR-safe paths.
|
||||
#ifdef USE_ESP32
|
||||
void IRAM_ATTR esphome_lwip_wake_main_loop_any_context(void) {
|
||||
if (xPortInIsrContext()) {
|
||||
int px_higher_priority_task_woken = 0;
|
||||
esphome_lwip_wake_main_loop_from_isr(&px_higher_priority_task_woken);
|
||||
portYIELD_FROM_ISR(px_higher_priority_task_woken);
|
||||
} else {
|
||||
esphome_lwip_wake_main_loop();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // USE_LWIP_FAST_SELECT
|
||||
|
||||
@@ -33,6 +33,13 @@ void esphome_lwip_wake_main_loop(void);
|
||||
/// @param px_higher_priority_task_woken Set to pdTRUE if a context switch is needed.
|
||||
void esphome_lwip_wake_main_loop_from_isr(int *px_higher_priority_task_woken);
|
||||
|
||||
/// Wake the main loop task from any context (ISR, thread, or main loop).
|
||||
/// ESP32-only: uses xPortInIsrContext() to detect ISR context.
|
||||
/// LibreTiny lacks IRAM_ATTR support needed for ISR-safe paths.
|
||||
#ifdef USE_ESP32
|
||||
void esphome_lwip_wake_main_loop_any_context(void);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user