Changelog¶
Unreleased¶
Breaking Changes¶
- Transport receive model: listener and polling are now mutually
exclusive. When
set_listener()is installed, incoming messages are dispatched only viaITransportListener::on_message_received()and are no longer enqueued into the internal receive queue.receive_message()returnsnullptrin listener mode. Previously, messages were both enqueued and dispatched, causing unbounded queue growth — memory leaks on POSIX or fixed-pool exhaustion on FreeRTOS (#269). Code that relied on drainingreceive_message()while a listener was set must be updated to consume messages exclusively through one path.
New Features¶
UdpTransport::receive_message_with_sender(Endpoint& sender)— polling mode variant that also returns the sender's endpoint for reply addressing without requiring a listener.
Bug Fixes¶
- UDP/TCP: listener-only mode no longer retains
MessagePtrin the internal queue, preventing memory leaks and pool exhaustion (#269). - TCP:
on_message_received()is now invoked outsideconnection_mutex_, eliminating a potential deadlock when the callback callsdisconnect().
Unreleased — Static Allocation Backend (feature/no-heap-static-alloc)¶
Breaking Changes¶
This release introduces a compile-time static allocation backend. When
SOMEIP_USE_STATIC_ALLOC=ON, the following public types change their
underlying representation:
| Type | Dynamic (default) | Static (SOMEIP_USE_STATIC_ALLOC=ON) |
|---|---|---|
platform::ByteBuffer |
std::vector<uint8_t> |
Slab-backed buffer (pool-allocated, fixed capacity per tier) |
platform::String<N> |
std::string |
etl::string<N> (fixed capacity N, default 64) |
platform::Vector<T, N> |
std::vector<T> |
etl::vector<T, N> (fixed capacity N) |
platform::UnorderedMap<K, V, N> |
std::unordered_map<K, V> |
etl::unordered_map<K, V, N> (fixed capacity N) |
MessagePtr |
std::shared_ptr<Message> |
IntrusivePtr<Message> (pool-allocated, refcounted) |
Consumer impact:
- Code that stores
ByteBufferby value and relies on unlimited growth must account for pool-tier capacity limits.push_back(),resize(), andinsert()now return early / leave the buffer unchanged when the pool cannot satisfy the request. platform::String<N>is capacity-bounded. Assigning a string longer thanNtruncates under ETL's default error policy. OverrideNvia template parameter or theSOMEIP_DEFAULT_STRING_CAPACITYCMake variable.MessagePtris no longershared_ptrunder static-alloc. Code that callsshared_ptr-specific APIs (e.g.use_count(),weak_ptr) will not compile. UseMessagePtropaquely.
New Features¶
PayloadView— non-owning, span-like view over contiguous payload bytes. Works identically across dynamic and static backends.operator[]includes a debug-mode assertion; production builds matchstd::spansemantics (no bounds check).- Static slab allocators for
MessageandByteBufferwith configurable pool sizes viastatic_config.hor CMake-Doverrides. MallocTrapGuard(RAII) andmalloc_traplink-time interposition for verifying zero-heap behavior in tests.- FreeRTOS and ThreadX Renode CI — cross-compiled static-alloc tests run on Cortex-M4 under Renode simulation.
- SD capacity-aware contracts —
add_entry()/add_option()returnbool;deserialize()rejects messages that exceed container capacity.
Bug Fixes¶
- SD client: reserve local tracking map slot before sending network
traffic in
find_service()andsubscribe_eventgroup(). Previously, a successful send with a full map would discard the callback. - SD server: callers of
next_unicast_session_id()now abort the response when the peer table is full (returns session ID 0), instead of sending an invalid SOME/IP message. - Event publisher:
handle_subscription_locked()now rejects filter lists that exceed the bounded container's capacity instead of silently truncating them and returning success. PayloadView::operator[]now assertsi < size_in debug builds.- Overflow-safe bounds check in
e2e_header.cpp(offset + header_sizewraparound). e2e_crc.cppreturnsnulloptwhen temporary slice allocation fails under static pool pressure.sd_message.cpprejects oversized configuration strings beforeassign()to prevent ETL assertion / truncation.sd_server.cpp/event_subscriber.cppreplacedstd::to_string()with stack-localsnprintf()to eliminate heap allocation.event_subscriber.cppfield-response correlation key normalized toinstance_id=0on both store and lookup paths.serializer.hdeserialize_arrayrejects wire-controlled lengths exceeding static vector capacity (MALFORMED_MESSAGE).
Known Limitations (Intentional)¶
- E2E
make_uniqueheap allocation —std::make_unique<BasicE2EProfile>()ine2e_profiles/standard_profile.cpp(line 324) still allocates on the heap. E2E profile registration is a one-time startup cost and is performed before the malloc trap is armed. Tracked for static-pool migration if E2E is used on bare-metal targets. - Debug
to_string()heap allocation —Message::to_string(),Endpoint::to_string(),to_string(Result),to_string(MessageType), andto_string(ReturnCode)usestd::string/std::stringstream. These are diagnostic-only functions not called on the data path. - Examples disabled under static-alloc —
BUILD_EXAMPLES=OFFin all static-alloc CMake presets. Examples usestd::vector,std::string, andstd::make_sharedpervasively. Migrating them is possible but not prioritized; they serve as dynamic-backend usage documentation. - FreeRTOS zero-heap test uses size delta —
test_freertos_static_zero_heap()comparesxPortGetFreeHeapSize()before and after. A balancedpvPortMalloc/vPortFreepair within the window would go undetected. An allocation-counter approach (wrappingpvPortMalloc) would catch transient allocations. Acceptable as-is; stronger instrumentation tracked as a follow-up.