server: split run() into run_with_buffers + alloc shim#104
Draft
JustinKovacich wants to merge 1 commit into
Draft
server: split run() into run_with_buffers + alloc shim#104JustinKovacich wants to merge 1 commit into
JustinKovacich wants to merge 1 commit into
Conversation
Introduces `Server::run_with_buffers(&mut self, unicast_buf: &mut [u8], sd_buf: &mut [u8])` as the no-alloc-friendly entry point for the server event loop. The existing `Server::run` becomes a thin convenience shim that heap-allocates two 65535-byte buffers via `alloc::vec!` and delegates. Why: bare-metal consumers (TC4D + future no-alloc targets) cannot call `Server::run` because it pulls in `alloc::vec![0u8; 65535]` for the recv buffers. Splitting the buffer allocation out of the event loop body lets those consumers supply their own storage (typically `static`- declared `[u8; 65535]` arrays) while leaving std consumers' ergonomics unchanged. Pre-existing 64 KiB sizing rationale carries over verbatim to `run_with_buffers`'s docs: peer SD messages are bounded by link MTU, but the server is a sink for any peer datagram landing on its SD/unicast port — a smaller buffer would silently truncate larger-than-MTU peer messages instead of surfacing them. Caller picks an appropriate size for their target. Reborrow nuance: inside the loop, `recv_from(&mut *buf)` rather than `recv_from(&mut buf)` because `unicast_buf` / `sd_buf` are now `&mut [u8]` parameters, not owned `Vec<u8>` locals. Direct `&mut buf` would produce `&mut &mut [u8]`. Clears 20-pre alloc audit's category-D recv-buffer item without breaking any std-side caller. Existing tests pass: - 11 client_server tests (serialized to avoid pre-existing port races) - 2 bare_metal_e2e tests - 3 simple-someip-embassy-net loopback tests Doesn't yet eliminate the other 20-pre findings: - D / 19f H = Arc<F::Socket> default — handled by separate `Server::new_with_handles` work - E.1 Arc<EventPublisher> — handled by separate `EventPublisherHandle` work - E.2 Arc<SdStateManager> — handled by separate `SdStateHandle` work What this leaves: the bare-metal consumer must still hold the 65535-byte buffers in static storage (or wherever the firmware can spare 128 KB total recv-buffer RAM). On TC4D specifically with a typical RAM budget the size may need to shrink to something like 1500 + a documented truncation caveat — that's a per-consumer decision now exposed via the new API surface. Gates green: - cargo fmt --check - cargo clippy --tests (2 pre-existing warnings, unrelated) - cargo build --workspace --all-targets - cargo build --no-default-features --features client,server,bare_metal - cargo build -p simple-someip-embassy-net --target thumbv7em-none-eabihf - cargo test --features client-tokio,server-tokio --test client_server -- --test-threads=1 - cargo test --features client,server,bare_metal --test bare_metal_e2e - cargo test -p simple-someip-embassy-net --test loopback Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR introduces a no-alloc-friendly server event loop entry point by splitting the existing Server::run() into a new Server::run_with_buffers(&mut self, unicast_buf: &mut [u8], sd_buf: &mut [u8]) that uses caller-provided receive buffers, plus an alloc-based run() convenience shim that heap-allocates the (existing) 64 KiB buffers and delegates.
Changes:
- Added
Server::run_with_buffers(...)as the primary event-loop implementation with externally supplied recv buffers. - Refactored
Server::run()into a thin alloc-backed wrapper that allocates two 65535-byte buffers and callsrun_with_buffers. - Updated rustdocs to explain buffer sizing and bare-metal usage patterns.
Comments suppressed due to low confidence (1)
src/server/mod.rs:770
- The
# Errorsdocs here say this returnsError::Iowithstd::io::ErrorKind::InvalidInputwhen called on a passive server, but the implementation returnsError::InvalidUsage("passive_server_run")(and this API is also usable inno_stdcontexts). Update the docs to match the actual error variant/message so callers know what to handle.
/// # Errors
///
/// Returns [`Error::Io`] with [`std::io::ErrorKind::InvalidInput`] if
/// called on a server constructed via `Server::new_passive` — passive
/// servers have no real SD socket to read from, so the run loop would
/// block forever on the ephemeral placeholder socket.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+747
to
+750
| /// unicast port. Backends that surface truncation | ||
| /// (`ReceivedDatagram::truncated`) emit a `tracing::warn!` when | ||
| /// the caller's buffer was too small; backends that don't | ||
| /// (TokioSocket today) silently truncate at the OS level. |
Comment on lines
+813
to
+822
| // Reborrow `&mut *foo` rather than `&mut foo` because | ||
| // `unicast_buf` / `sd_buf` are `&mut [u8]` parameters | ||
| // here (caller-owned), not owned `Vec<u8>` locals | ||
| // — direct `&mut foo` would produce `&mut &mut [u8]`. | ||
| let unicast_fut = self | ||
| .unicast_socket | ||
| .socket() | ||
| .recv_from(&mut unicast_buf) | ||
| .recv_from(&mut *unicast_buf) | ||
| .fuse(); | ||
| let sd_fut = self.sd_socket.socket().recv_from(&mut sd_buf).fuse(); | ||
| let sd_fut = self.sd_socket.socket().recv_from(&mut *sd_buf).fuse(); |
| let sd_fut = self.sd_socket.socket().recv_from(&mut sd_buf).fuse(); | ||
| let sd_fut = self.sd_socket.socket().recv_from(&mut *sd_buf).fuse(); | ||
| pin_mut!(unicast_fut, sd_fut); | ||
| select_biased! { |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Introduces
Server::run_with_buffers(&mut self, unicast_buf: &mut [u8], sd_buf: &mut [u8])as the no-alloc-friendly entry point for the server event loop. The existingServer::runbecomes a thin convenience shim that heap-allocates two 65535-byte buffers viaalloc::vec!and delegates.Why: bare-metal consumers (TC4D + future no-alloc targets) cannot call
Server::runbecause it pulls inalloc::vec![0u8; 65535]for the recv buffers. Splitting the buffer allocation out of the event loop body lets those consumers supply their own storage (typicallystatic- declared[u8; 65535]arrays) while leaving std consumers' ergonomics unchanged.Pre-existing 64 KiB sizing rationale carries over verbatim to
run_with_buffers's docs: peer SD messages are bounded by link MTU, but the server is a sink for any peer datagram landing on its SD/unicast port — a smaller buffer would silently truncate larger-than-MTU peer messages instead of surfacing them. Caller picks an appropriate size for their target.Reborrow nuance: inside the loop,
recv_from(&mut *buf)rather thanrecv_from(&mut buf)becauseunicast_buf/sd_bufare now&mut [u8]parameters, not ownedVec<u8>locals. Direct&mut bufwould produce&mut &mut [u8].Clears 20-pre alloc audit's category-D recv-buffer item without breaking any std-side caller. Existing tests pass:
Doesn't yet eliminate the other 20-pre findings:
Server::new_with_handlesworkEventPublisherHandleworkSdStateHandleworkWhat this leaves: the bare-metal consumer must still hold the 65535-byte buffers in static storage (or wherever the firmware can spare 128 KB total recv-buffer RAM). On TC4D specifically with a typical RAM budget the size may need to shrink to something like 1500 + a documented truncation caveat — that's a per-consumer decision now exposed via the new API surface.
Gates green: