Make Event listener ownership RAII-safe and enforce bounded retained Event counts
Problem
Event listener registration currently returns a raw owning IEventListenerHandle*. Losing this pointer leaks the registration handle, while manual deletion requirements make lifetime management error-prone.
Event receiver queues also default to an unlimited pending-event count. A producer that consistently outruns a consumer can therefore exhaust the embedded heap.
The existing pending limit counts queued Events only. Events moved into an active processing batch are removed from that count before their references are released, allowing the actual number of retained Event references to temporarily exceed the configured limit.
Proposed changes
Listener ownership
- Introduce
EventListenerHandlePtrasstd::unique_ptr<IEventListenerHandle>. - Change callback and Observer registration APIs to return the owning smart-pointer type.
- Automatically unregister listeners when their handles are reset or destroyed.
- Make
EventListenerHandledestruction exception-safe. - Update Event Observer and callback-listener examples to use RAII.
- Remove manual
deleterequirements from the documentation and examples.
Event queue bounds
- Default each
EventReceiverto a maximum of 64 retained Events. - Continue using
BlockProduceras the default overflow policy. - Allow the default bound to be configured through:
ESPRESSIO_EVENT_DEFAULT_MAX_PENDING_EVENT_COUNT
- Continue allowing an explicit maximum of zero for applications that genuinely require an unbounded receiver.
- Include Events currently being processed in the retained-event bound.
- Release capacity only after the processing batch has released its Event references.
- Ensure
DropOldestrejects the incoming Event safely when all retained capacity belongs to an active batch and no queued Event can be displaced. - Preserve exception-safe reference release when listener processing throws.
Infrastructure documentation
- Document
EventManageras process-lifetime FreeRTOS infrastructure. - Clarify that its task, semaphore, dispatcher, and per-type routing buckets are fixed infrastructure rather than per-dispatch Event leaks.
Version impact
The Event listener registration return type and ownership contract change.
Target version: 4.0.0
Dependency changes
- ESPressio-Threads
>=2.0.0 - ESPressio-Observable
>=3.0.0
Acceptance criteria
- Listener and Observer registrations return owning smart pointers.
- Losing manual raw-pointer cleanup is no longer a leak path.
- Receiver queues are bounded by default.
- The configured bound includes queued and actively processed Event references.
- Overflow policies continue releasing rejected or displaced Event references correctly.
- Listener destruction cannot propagate an exception.
- Strict host tests pass with warnings treated as errors.
- UBSan tests pass.
- The 10,000-Event reference-count stress test passes.
- Heap analysis reports no leaked listener allocations.