[core] Wake main loop from ISR 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 Application::wake_loop_isrsafe(nullptr) to immediately wake the
main loop when called from ISR context on platforms with fast select.

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:
J. Nick Koston
2026-02-28 10:54:00 -10:00
parent 3c5d877d96
commit 46bff3f3eb
2 changed files with 8 additions and 1 deletions
+1 -1
View File
@@ -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).
+7
View File
@@ -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).
// vTaskNotifyGiveFromISR(NULL) is safe here — it skips the yield flag
// and the context switch happens at the next scheduler tick (<1ms).
Application::wake_loop_isrsafe(nullptr);
#endif
}
void Component::reset_to_construction_state() {
if ((this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_FAILED) {