mirror of
https://github.com/esphome/esphome.git
synced 2025-08-10 12:27:46 +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