1
0
mirror of https://github.com/esphome/esphome.git synced 2025-09-18 19:22:22 +01:00
This commit is contained in:
J. Nick Koston
2025-06-18 14:17:45 +02:00
parent 7b9bd70729
commit ec186e6324
5 changed files with 15 additions and 15 deletions

View File

@@ -163,15 +163,15 @@ void Component::enable_loop() {
App.enable_component_loop_(this); App.enable_component_loop_(this);
} }
} }
void IRAM_ATTR HOT Component::enable_loop_soon_from_isr() { void IRAM_ATTR HOT Component::enable_loop_soon_any_context() {
// This method is ISR-safe because: // This method is thread and ISR-safe because:
// 1. Only performs simple assignments to volatile variables (atomic on all platforms) // 1. Only performs simple assignments to volatile variables (atomic on all platforms)
// 2. No read-modify-write operations that could be interrupted // 2. No read-modify-write operations that could be interrupted
// 3. No memory allocation, object construction, or function calls // 3. No memory allocation, object construction, or function calls
// 4. IRAM_ATTR ensures code is in IRAM, not flash (required for ISR execution) // 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 // 5. Components are never destroyed, so no use-after-free concerns
// 6. App is guaranteed to be initialized before any ISR could fire // 6. App is guaranteed to be initialized before any ISR could fire
// 7. Multiple ISR calls are safe - just sets the same flags to true // 7. Multiple ISR/thread calls are safe - just sets the same flags to true
// 8. Race condition with main loop is handled by clearing flag before processing // 8. Race condition with main loop is handled by clearing flag before processing
this->pending_enable_loop_ = true; this->pending_enable_loop_ = true;
App.has_pending_enable_loop_requests_ = true; App.has_pending_enable_loop_requests_ = true;

View File

@@ -171,15 +171,15 @@ class Component {
*/ */
void enable_loop(); void enable_loop();
/** ISR-safe version of enable_loop() that can be called from interrupt context. /** Thread and ISR-safe version of enable_loop() that can be called from any context.
* *
* This method defers the actual enable via enable_pending_loops_ to the main loop, * This method defers the actual enable via enable_pending_loops_ to the main loop,
* making it safe to call from ISR handlers, timer callbacks, or other * making it safe to call from ISR handlers, timer callbacks, other threads,
* interrupt contexts. * or any interrupt context.
* *
* @note The actual loop enabling will happen on the next main loop iteration. * @note The actual loop enabling will happen on the next main loop iteration.
* @note Only one pending enable request is tracked per component. * @note Only one pending enable request is tracked per component.
* @note There is no disable_loop_soon_from_isr() on purpose - it would race * @note There is no disable_loop_soon_any_context() on purpose - it would race
* against enable calls and synchronization would get too complex * against enable calls and synchronization would get too complex
* to provide a safe version that would work for each component. * to provide a safe version that would work for each component.
* *
@@ -190,7 +190,7 @@ class Component {
* disable_loop() in its next ::loop() iteration. Implementations * disable_loop() in its next ::loop() iteration. Implementations
* will need to carefully consider all possible race conditions. * will need to carefully consider all possible race conditions.
*/ */
void enable_loop_soon_from_isr(); void enable_loop_soon_any_context();
bool is_failed() const; bool is_failed() const;
@@ -363,7 +363,7 @@ class Component {
/// Bit 3: STATUS_LED_ERROR /// Bit 3: STATUS_LED_ERROR
/// Bits 4-7: Unused - reserved for future expansion (50% of the bits are free) /// Bits 4-7: Unused - reserved for future expansion (50% of the bits are free)
uint8_t component_state_{0x00}; uint8_t component_state_{0x00};
volatile bool pending_enable_loop_{false}; ///< ISR-safe flag for enable_loop_soon_from_isr volatile bool pending_enable_loop_{false}; ///< ISR-safe flag for enable_loop_soon_any_context
}; };
/** This class simplifies creating components that periodically check a state. /** This class simplifies creating components that periodically check a state.

View File

@@ -67,10 +67,10 @@ void IRAM_ATTR LoopTestISRComponent::simulate_isr_enable() {
this->isr_call_count_++; this->isr_call_count_++;
// Call enable_loop_soon_from_isr multiple times to test that it's safe // Call enable_loop_soon_any_context multiple times to test that it's safe
this->enable_loop_soon_from_isr(); this->enable_loop_soon_any_context();
this->enable_loop_soon_from_isr(); // Test multiple calls this->enable_loop_soon_any_context(); // Test multiple calls
this->enable_loop_soon_from_isr(); // Should be idempotent this->enable_loop_soon_any_context(); // Should be idempotent
// Note: In a real ISR, we cannot use ESP_LOG* macros as they're not ISR-safe // Note: In a real ISR, we cannot use ESP_LOG* macros as they're not ISR-safe
// For testing, we'll track the call count and log it from the main loop // For testing, we'll track the call count and log it from the main loop

View File

@@ -14,7 +14,7 @@ class LoopTestISRComponent : public Component {
void setup() override; void setup() override;
void loop() override; void loop() override;
// Simulates an ISR calling enable_loop_soon_from_isr // Simulates an ISR calling enable_loop_soon_any_context
void simulate_isr_enable(); void simulate_isr_enable();
float get_setup_priority() const override { return setup_priority::DATA; } float get_setup_priority() const override { return setup_priority::DATA; }

View File

@@ -35,7 +35,7 @@ loop_test_component:
test_redundant_operations: true test_redundant_operations: true
disable_after: 10 disable_after: 10
# ISR test component that uses enable_loop_soon_from_isr # ISR test component that uses enable_loop_soon_any_context
isr_components: isr_components:
- id: isr_test - id: isr_test
name: "isr_test" name: "isr_test"