Full Changelog: 1.0.0...2.1.0
ESPressio Observable 2.0 integration
Dependency changes
- Added
Flowduino ESPressio-Observableversion2.0.0tolibrary.json. - Added
Flowduino ESPressio-Observable (>=2.0.0)tolibrary.properties. - Retained ESPressio Threads as the asynchronous Event-processing dependency.
- Updated the README dependency and PlatformIO examples to include ESPressio Observable 2.0.0.
Typed Event Observer API
- Added
ESPressio_EventObserver.hpp. - Added the typed
IEventObserver<EventType>interface. - Made
IEventObserver<EventType>derive from ESPressio Observable'sIObserver. - Used virtual
IObserverinheritance so one object can implement multipleIEventObserver<EventType>interfaces without creating ambiguousIObserverbase objects. - Added the required typed Observer method:
virtual void OnEvent(
EventType* event,
EventDispatchMethod dispatchMethod,
EventPriority priority
) = 0;- Added the optional custom-interest method:
virtual bool IsInterestedInEvent(EventType* event);- The default
IsInterestedInEvent()implementation returnstrue.
Observer registration
- Added
RegisterObserver<EventType>()toIEventListener. - Made Observer registration available through both
EventListenerandEventThread. - Adapted typed Observer methods into the existing asynchronous Event listener pipeline.
- Preserved the existing
RegisterListener()callback API. - Allowed callback listeners and typed Observers to coexist for the same Event type.
- Passed identical Event, dispatch-method, and priority values to callbacks and Observers.
- Preserved the existing caller-owned
IEventListenerHandlelifetime model. - Added typed null-Observer rejection using
InvalidObserverRegistrationException.
Observer interest filtering
- Added support for
EventListenerInterest::All. - Added support for
EventListenerInterest::YoungerThan. - Added support for
EventListenerInterest::Custom. - Custom Observer filtering invokes
IsInterestedInEvent()instead of requiring a predicate callback. - Preserved independent interest policies for every Observer registration.
- Allowed one Observer object to register independently for multiple Event types.
Public header integration
- Added
ESPressio_EventObserver.hppto the mainESPressio_Event.hppinclude surface. - Added the Observable exception and Observer interfaces to EventListener's required includes.
- Added missing direct standard-library includes used by EventListener.
Listener dispatch safety
- Replaced unsafe casts between different
EventListenerContainer<EventType>template specializations. - Added a virtual, type-erased
ProcessEvent()operation to the listener-container interface. - Each typed listener container now performs a checked
dynamic_castto its actual Event type. - Event callbacks are invoked only when the Event matches the container's declared Event type.
- Centralized
All,YoungerThan, andCustominterest evaluation inside each typed listener container. - Custom-interest registrations with no predicate are now treated as uninterested instead of invoking an empty function.
Callback and Observer exception safety
- Added an event-reference RAII guard to
ProcessEvent(). - Event references are now released when callbacks or Observer methods throw.
- Replaced manual shared-mutex locking with RAII locks.
- Listener registry locks are now released automatically during exceptions.
- Callback and Observer exceptions continue to propagate to the caller after cleanup.
Callback-time registration and unregistration
- Changed listener buckets to store shared listener-container records.
ProcessEvent()now copies a stable listener snapshot while holding the registry lock.- The registry lock is released before callbacks or Observer methods execute.
- Callback listeners can now unregister themselves safely.
- Typed Event Observers can now unregister themselves safely.
- Other listeners can be registered or unregistered during Event processing without invalidating the active dispatch snapshot.
- Removed the previous callback-time deadlock and use-after-free risk.
EventManager routing fixes
- EventManager routing is now registered only when the first listener or Observer for an Event type is added.
- Removing one registration no longer disables routing while other callbacks or Observers for the same Event type remain.
- EventManager routing is unregistered only after the final registration for that Event type is removed.
- Empty listener buckets are now deleted instead of being leaked.
- Added an explicit
UnregisterAllListeners()shutdown path. EventThreadnow unregisters all Event types before its listener base is destroyed.- Added the previously missing EventManager registration and unregistration hooks to
EventThreadWithLoop. EventThreadWithLoopnow unregisters all Event types during destruction.- Prevented EventManager from retaining dangling EventThread receiver pointers after destruction.
Event Listener Handle improvements
- Replaced the heap-allocated registration-state mutex with a directly owned
ReadWriteMutex<bool>. - Removed the registration-state allocation leak.
- Added explicit
overridedeclarations to handle interface implementations. - Handles are forcibly invalidated when their owning EventListener is destroyed.
- A handle that outlives its EventListener now reports itself as unregistered.
- Repeated handle unregistration remains safe and idempotent.
Registration exception safety
- Replaced manual registry locking with
std::unique_lock. - Used temporary smart pointers while constructing listener handles and containers.
- Listener handles and containers are released to caller/registry ownership only after insertion succeeds.
- Failed allocations or container insertions now release partially constructed state automatically.
- EventManager registration hooks run only after registry locks have been released.
Documentation
- Documented the ESPressio Observable 2.0.0 dependency.
- Added a complete typed Event Observer example.
- Documented Observer and callback coexistence.
- Documented non-owning Observer lifetime requirements.
- Documented caller ownership of returned
IEventListenerHandleobjects. - Documented all supported interest modes.
- Added a custom
IsInterestedInEvent()example. - Documented multi-Event Observer implementations and independent registrations.
Tests
- Added a repository-owned CMake/CTest integration suite.
- Added a host-side ESPressio Threads test stub.
- Verified that
IEventObserver<EventType>satisfies ESPressio Observable'sIObserverinterface. - Verified that multi-Event Observers have one unambiguous
IObserveridentity. - Verified null Observer rejection and exact exception typing.
- Verified normal Observer delivery.
- Verified callback and Observer coexistence.
- Verified Event pointer, dispatch-method, and priority forwarding.
- Verified
Allinterest behavior. - Verified
YoungerThanboundary behavior. - Verified custom-interest acceptance and rejection.
- Verified multi-Event Observer delivery.
- Verified handle-driven unregistration.
- Verified repeated unregistration.
- Verified Observer self-unregistration during Event processing.
- Verified callback exception propagation.
- Verified event-reference cleanup following callback exceptions.
- Verified Event processing when no listener bucket exists.
- Verified routing registration occurs only for the first listener.
- Verified routing remains active when one of several registrations is removed.
- Verified routing unregistration occurs after the final listener is removed.
- Verified handle invalidation when an EventListener is destroyed.
- Verified explicit all-listener shutdown behavior.
Verification
- Passed strict C++17 compilation with:
-Wall-Wextra-Wpedantic-Werror
- Passed AddressSanitizer.
- Passed UndefinedBehaviorSanitizer.
- Passed standalone
ESPressio_EventObserver.hppcompilation. - Passed standalone
ESPressio_EventListener.hppcompilation. - Passed
library.jsonvalidation. - Passed
git diff --check.