ESPHome 2026.5.0-dev
Loading...
Searching...
No Matches
event_pool.h
Go to the documentation of this file.
1#pragma once
2
3#if defined(USE_ESP32) || defined(USE_ZEPHYR)
4
5#include <atomic>
6#include <cstddef>
9
10namespace esphome {
11
12// Event Pool - On-demand pool of objects to avoid heap fragmentation
13// Events are allocated on first use and reused thereafter, growing to peak usage
14// @tparam T The type of objects managed by the pool (must have a release() method)
15// @tparam SIZE The maximum number of objects in the pool (1-255, limited by uint8_t)
16//
17// SIZING: When paired with a LockFreeQueue<T, Q_SIZE>, the pool SIZE should be
18// Q_SIZE - 1 (the queue's actual capacity, since the ring buffer reserves one slot).
19// This ensures allocate() returns nullptr before push() can fail, which:
20// - Prevents the allocate-succeeds-but-push-fails mismatch that permanently
21// leaks a pool slot (the element is never returned to the pool)
22// - Avoids needing release() on the producer path after a failed push(),
23// preserving the SPSC contract on the internal free list
24template<class T, uint8_t SIZE> class EventPool {
25 public:
26 EventPool() : total_created_(0) {}
27
29 // Clean up any remaining events in the free list
30 // IMPORTANT: This destructor assumes no concurrent access. The EventPool must not
31 // be destroyed while any thread might still call allocate() or release().
32 // In practice, this is typically ensured by destroying the pool only during
33 // component shutdown when all producer/consumer threads have been stopped.
34 T *event;
36 while ((event = this->free_list_.pop()) != nullptr) {
37 // Call destructor
38 event->~T();
39 // Deallocate using RAMAllocator
40 allocator.deallocate(event, 1);
41 }
42 }
43
44 // Allocate an event from the pool
45 // Returns nullptr if pool is full
46 T *allocate() {
47 // Try to get from free list first
48 T *event = this->free_list_.pop();
49 if (event != nullptr)
50 return event;
51
52 // Need to create a new event
53 if (this->total_created_ >= SIZE) {
54 // Pool is at capacity
55 return nullptr;
56 }
57
58 // Use internal RAM for better performance
60 event = allocator.allocate(1);
61
62 if (event == nullptr) {
63 // Memory allocation failed
64 return nullptr;
65 }
66
67 // Placement new to construct the object
68 new (event) T();
69 this->total_created_++;
70 return event;
71 }
72
73 // Return an event to the pool for reuse
74 void release(T *event) {
75 if (event != nullptr) {
76 // Clean up the event's allocated memory
77 event->release();
78 this->free_list_.push(event);
79 }
80 }
81
82 private:
83 LockFreeQueue<T, SIZE> free_list_; // Free events ready for reuse
84 uint8_t total_created_; // Total events created (high water mark, max 255)
85};
86
87} // namespace esphome
88
89#endif // defined(USE_ESP32)
void release(T *event)
Definition event_pool.h:74
An STL allocator that uses SPI or internal RAM.
Definition helpers.h:2212
void deallocate(T *p, size_t n)
Definition helpers.h:2267
T * allocate(size_t n)
Definition helpers.h:2229
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7