mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 15:12:06 +00:00 
			
		
		
		
	Merge branch 'extract_helpers' into integration
This commit is contained in:
		| @@ -1,6 +1,6 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #ifdef USE_ESP32 | #if defined(USE_ESP32) || defined(USE_LIBRETINY) | ||||||
|  |  | ||||||
| #include <atomic> | #include <atomic> | ||||||
| #include <cstddef> | #include <cstddef> | ||||||
| @@ -11,12 +11,18 @@ namespace esphome { | |||||||
|  |  | ||||||
| // Event Pool - On-demand pool of objects to avoid heap fragmentation | // Event Pool - On-demand pool of objects to avoid heap fragmentation | ||||||
| // Events are allocated on first use and reused thereafter, growing to peak usage | // Events are allocated on first use and reused thereafter, growing to peak usage | ||||||
|  | // @tparam T The type of objects managed by the pool (must have a clear() method) | ||||||
|  | // @tparam SIZE The maximum number of objects in the pool (1-255, limited by uint8_t) | ||||||
| template<class T, uint8_t SIZE> class EventPool { | template<class T, uint8_t SIZE> class EventPool { | ||||||
|  public: |  public: | ||||||
|   EventPool() : total_created_(0) {} |   EventPool() : total_created_(0) {} | ||||||
|  |  | ||||||
|   ~EventPool() { |   ~EventPool() { | ||||||
|     // Clean up any remaining events in the free list |     // Clean up any remaining events in the free list | ||||||
|  |     // IMPORTANT: This destructor assumes no concurrent access. The EventPool must not | ||||||
|  |     // be destroyed while any thread might still call allocate() or release(). | ||||||
|  |     // In practice, this is typically ensured by destroying the pool only during | ||||||
|  |     // component shutdown when all producer/consumer threads have been stopped. | ||||||
|     T *event; |     T *event; | ||||||
|     RAMAllocator<T> allocator(RAMAllocator<T>::ALLOC_INTERNAL); |     RAMAllocator<T> allocator(RAMAllocator<T>::ALLOC_INTERNAL); | ||||||
|     while ((event = this->free_list_.pop()) != nullptr) { |     while ((event = this->free_list_.pop()) != nullptr) { | ||||||
| @@ -67,9 +73,9 @@ template<class T, uint8_t SIZE> class EventPool { | |||||||
|  |  | ||||||
|  private: |  private: | ||||||
|   LockFreeQueue<T, SIZE> free_list_;  // Free events ready for reuse |   LockFreeQueue<T, SIZE> free_list_;  // Free events ready for reuse | ||||||
|   uint8_t total_created_;             // Total events created (high water mark) |   uint8_t total_created_;             // Total events created (high water mark, max 255) | ||||||
| }; | }; | ||||||
|  |  | ||||||
| }  // namespace esphome | }  // namespace esphome | ||||||
|  |  | ||||||
| #endif | #endif  // defined(USE_ESP32) || defined(USE_LIBRETINY) | ||||||
|   | |||||||
| @@ -1,11 +1,17 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #ifdef USE_ESP32 | #if defined(USE_ESP32) || defined(USE_LIBRETINY) | ||||||
|  |  | ||||||
| #include <atomic> | #include <atomic> | ||||||
| #include <cstddef> | #include <cstddef> | ||||||
|  |  | ||||||
|  | #if defined(USE_ESP32) | ||||||
| #include <freertos/FreeRTOS.h> | #include <freertos/FreeRTOS.h> | ||||||
| #include <freertos/task.h> | #include <freertos/task.h> | ||||||
|  | #elif defined(USE_LIBRETINY) | ||||||
|  | #include <FreeRTOS.h> | ||||||
|  | #include <task.h> | ||||||
|  | #endif | ||||||
|  |  | ||||||
| /* | /* | ||||||
|  * Lock-free queue for single-producer single-consumer scenarios. |  * Lock-free queue for single-producer single-consumer scenarios. | ||||||
| @@ -13,9 +19,14 @@ | |||||||
|  * blocking each other. |  * blocking each other. | ||||||
|  * |  * | ||||||
|  * This is a Single-Producer Single-Consumer (SPSC) lock-free ring buffer. |  * This is a Single-Producer Single-Consumer (SPSC) lock-free ring buffer. | ||||||
|  |  * Available on platforms with FreeRTOS support (ESP32, LibreTiny). | ||||||
|  |  * | ||||||
|  * Common use cases: |  * Common use cases: | ||||||
|  * - BLE events: BLE task produces, main loop consumes |  * - BLE events: BLE task produces, main loop consumes | ||||||
|  * - MQTT messages: main task produces, MQTT thread consumes |  * - MQTT messages: main task produces, MQTT thread consumes | ||||||
|  |  * | ||||||
|  |  * @tparam T The type of elements stored in the queue (must be a pointer type) | ||||||
|  |  * @tparam SIZE The maximum number of elements (1-255, limited by uint8_t indices) | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| @@ -56,6 +67,9 @@ template<class T, uint8_t SIZE> class LockFreeQueue { | |||||||
|         uint8_t head_after = head_.load(std::memory_order_acquire); |         uint8_t head_after = head_.load(std::memory_order_acquire); | ||||||
|         if (head_after == current_tail) { |         if (head_after == current_tail) { | ||||||
|           // Consumer just caught up to where tail was - might go to sleep, must notify |           // Consumer just caught up to where tail was - might go to sleep, must notify | ||||||
|  |           // Note: There's a benign race here - between reading head_after and calling | ||||||
|  |           // xTaskNotifyGive(), the consumer could advance further. This would result | ||||||
|  |           // in an unnecessary wake-up, but is harmless and extremely rare in practice. | ||||||
|           xTaskNotifyGive(task_to_notify_); |           xTaskNotifyGive(task_to_notify_); | ||||||
|         } |         } | ||||||
|         // Otherwise: consumer is still behind, no need to notify |         // Otherwise: consumer is still behind, no need to notify | ||||||
| @@ -104,6 +118,8 @@ template<class T, uint8_t SIZE> class LockFreeQueue { | |||||||
|   // Atomic: written by producer (push/increment), read+reset by consumer (get_and_reset) |   // Atomic: written by producer (push/increment), read+reset by consumer (get_and_reset) | ||||||
|   std::atomic<uint16_t> dropped_count_;  // 65535 max - more than enough for drop tracking |   std::atomic<uint16_t> dropped_count_;  // 65535 max - more than enough for drop tracking | ||||||
|   // Atomic: written by consumer (pop), read by producer (push) to check if full |   // Atomic: written by consumer (pop), read by producer (push) to check if full | ||||||
|  |   // Using uint8_t limits queue size to 255 elements but saves memory and ensures | ||||||
|  |   // atomic operations are efficient on all platforms | ||||||
|   std::atomic<uint8_t> head_; |   std::atomic<uint8_t> head_; | ||||||
|   // Atomic: written by producer (push), read by consumer (pop) to check if empty |   // Atomic: written by producer (push), read by consumer (pop) to check if empty | ||||||
|   std::atomic<uint8_t> tail_; |   std::atomic<uint8_t> tail_; | ||||||
| @@ -113,4 +129,4 @@ template<class T, uint8_t SIZE> class LockFreeQueue { | |||||||
|  |  | ||||||
| }  // namespace esphome | }  // namespace esphome | ||||||
|  |  | ||||||
| #endif | #endif  // defined(USE_ESP32) || defined(USE_LIBRETINY) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user