mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 07:03:55 +00:00 
			
		
		
		
	Merge branch 'loop_done_enable_isr' into binary_sensor_gpio_polling
This commit is contained in:
		| @@ -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; | ||||||
|   | |||||||
| @@ -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. | ||||||
|   | |||||||
| @@ -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 | ||||||
|   | |||||||
| @@ -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; } | ||||||
|   | |||||||
| @@ -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" | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user