mirror of
https://github.com/esphome/esphome.git
synced 2026-09-20 19:48:39 +00:00
[core] Wake main loop from any context in enable_loop_soon_any_context()
enable_loop_soon_any_context() sets volatile flags but does not wake the main loop from ulTaskNotifyTake() sleep. This means components using ISR-driven state changes (e.g. GPIO binary sensors) wait up to ~16ms for the select timeout before their loop runs. Add wake_loop_any_context() that detects the calling context (ISR vs task) using xPortInIsrContext() (ESP32) or __get_IPSR() (LibreTiny) and calls the appropriate FreeRTOS API. This is safe from ISR, thread, and main loop contexts. Also relax the wake_loop_isrsafe() guard from requiring both USE_WAKE_LOOP_THREADSAFE and USE_LWIP_FAST_SELECT to just USE_LWIP_FAST_SELECT, since the ISR wake path uses vTaskNotifyGiveFromISR directly and does not depend on the UDP socket mechanism.
This commit is contained in:
@@ -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,10 @@ 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);
|
||||
}
|
||||
|
||||
/// 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
|
||||
|
||||
|
||||
@@ -323,6 +323,13 @@ 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;
|
||||
#ifdef USE_LWIP_FAST_SELECT
|
||||
// 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 pick xTaskNotifyGive (task) or
|
||||
// vTaskNotifyGiveFromISR (ISR) — safe from any calling context.
|
||||
Application::wake_loop_any_context();
|
||||
#endif
|
||||
}
|
||||
void Component::reset_to_construction_state() {
|
||||
if ((this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_FAILED) {
|
||||
|
||||
@@ -239,4 +239,20 @@ void IRAM_ATTR esphome_lwip_wake_main_loop_from_isr(int *px_higher_priority_task
|
||||
}
|
||||
}
|
||||
|
||||
// Wake the main loop from any context (ISR, thread, or main loop).
|
||||
// Detects ISR context and delegates to the appropriate variant:
|
||||
// ESP32 (Xtensa/RISC-V): xPortInIsrContext() — checks interrupt nesting counter
|
||||
// LibreTiny (ARM Cortex-M): __get_IPSR() — reads IPSR register (0 = task context)
|
||||
void IRAM_ATTR esphome_lwip_wake_main_loop_any_context(void) {
|
||||
#ifdef USE_ESP32
|
||||
if (xPortInIsrContext()) {
|
||||
#else
|
||||
if (__get_IPSR() != 0) {
|
||||
#endif
|
||||
esphome_lwip_wake_main_loop_from_isr(NULL);
|
||||
} else {
|
||||
esphome_lwip_wake_main_loop();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // defined(USE_ESP32) || defined(USE_LIBRETINY)
|
||||
|
||||
@@ -33,6 +33,10 @@ 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).
|
||||
/// Detects the calling context and uses the appropriate FreeRTOS API.
|
||||
void esphome_lwip_wake_main_loop_any_context(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user