pub trait ServiceTrait {
type Error;
// Required methods
fn take_request<'a>(
&mut self,
buf: &'a mut [u8],
) -> Result<Option<ServiceRequest<'a>>, Self::Error>;
fn send_response(
&mut self,
sequence_number: i64,
data: &[u8],
) -> Result<(), Self::Error>;
// Provided methods
fn has_request(&self) -> bool { ... }
fn register_waker(&self, _waker: &Waker) { ... }
fn refused_request_len(&self) -> Option<usize> { ... }
fn handle_request<S>(
&mut self,
req_buf: &mut [u8],
reply_buf: &mut [u8],
handler: impl FnOnce(&<S as RosService>::Request) -> <S as RosService>::Reply,
) -> Result<bool, Self::Error>
where S: RosService,
Self::Error: From<TransportError> { ... }
fn handle_request_boxed<S>(
&mut self,
req_buf: &mut [u8],
reply_buf: &mut [u8],
handler: impl FnOnce(&<S as RosService>::Request) -> Box<<S as RosService>::Reply>,
) -> Result<bool, Self::Error>
where S: RosService,
Self::Error: From<TransportError> { ... }
fn handle_request_raw(
&mut self,
req_buf: &mut [u8],
reply_buf: &mut [u8],
handler: impl FnOnce(&mut CdrReader<'_>, &mut CdrWriter<'_>) -> Result<(), TransportError>,
) -> Result<bool, Self::Error>
where Self::Error: From<TransportError> { ... }
fn request_subscription_actual_qos(&self) -> QoSProfile { ... }
fn response_publisher_actual_qos(&self) -> QoSProfile { ... }
}Expand description
Service server trait for handling requests.
§Threading
&mut self on take_request and send_response — the executor
owns the server while a request is being handled. Handler bodies
run synchronously on the executor thread; long handlers should
dispatch work to a worker queue and reply later via the recorded
sequence_number.
§Calling pattern
- Executor calls
take_request(buf). - If
Some(req)returned, decode, run handler, encode reply. send_response(req.sequence_number, &reply_buf).
sequence_number is the canonical request → reply correlation
token; backends derive it from the wire-level metadata (zenoh
query id, DDS sample identity).
Required Associated Types§
Required Methods§
Sourcefn take_request<'a>(
&mut self,
buf: &'a mut [u8],
) -> Result<Option<ServiceRequest<'a>>, Self::Error>
fn take_request<'a>( &mut self, buf: &'a mut [u8], ) -> Result<Option<ServiceRequest<'a>>, Self::Error>
Try to receive a service request into buf (non-blocking).
On success returns a ServiceRequest that borrows from
buf. The borrow is released when the returned struct is
dropped — typically before send_response is called, since
send_response takes &mut self.
Provided Methods§
Sourcefn has_request(&self) -> bool
fn has_request(&self) -> bool
Check if a request is available without consuming it.
Non-destructive. Default returns true (always assume one
may be available); backends should override with a real
check.
Sourcefn register_waker(&self, _waker: &Waker)
fn register_waker(&self, _waker: &Waker)
Phase 122.3.c.6.e — register a Waker for event-driven
service servers. Mirrors the matching method on
SubscriberTrait / ClientTrait. Backends that
surface incoming-request notifications wake waker when
has_request() flips true. Default: no-op (backends without
wake support ignore — caller falls back to polling).
Sourcefn refused_request_len(&self) -> Option<usize>
fn refused_request_len(&self) -> Option<usize>
Issue 1632 — how many bytes the request the most recent
take_request REFUSED as too big for the
caller’s buffer needed.
Subscription::refused_sample_len’s contract, for the service
server: read it right after a take returns BufferTooSmall; None
means the backend does not know (the default), never “zero bytes”.
Sourcefn handle_request<S>(
&mut self,
req_buf: &mut [u8],
reply_buf: &mut [u8],
handler: impl FnOnce(&<S as RosService>::Request) -> <S as RosService>::Reply,
) -> Result<bool, Self::Error>
fn handle_request<S>( &mut self, req_buf: &mut [u8], reply_buf: &mut [u8], handler: impl FnOnce(&<S as RosService>::Request) -> <S as RosService>::Reply, ) -> Result<bool, Self::Error>
Handle a service request with typed messages
Sourcefn handle_request_boxed<S>(
&mut self,
req_buf: &mut [u8],
reply_buf: &mut [u8],
handler: impl FnOnce(&<S as RosService>::Request) -> Box<<S as RosService>::Reply>,
) -> Result<bool, Self::Error>
fn handle_request_boxed<S>( &mut self, req_buf: &mut [u8], reply_buf: &mut [u8], handler: impl FnOnce(&<S as RosService>::Request) -> Box<<S as RosService>::Reply>, ) -> Result<bool, Self::Error>
Handle a service request where the handler returns Box<S::Reply>
Identical to handle_request but the handler returns a heap-allocated reply.
This is needed for services with large response types (e.g., parameter services
where Vec<ParameterValue, 64> is ~1MB+) that would overflow the stack.
Sourcefn handle_request_raw(
&mut self,
req_buf: &mut [u8],
reply_buf: &mut [u8],
handler: impl FnOnce(&mut CdrReader<'_>, &mut CdrWriter<'_>) -> Result<(), TransportError>,
) -> Result<bool, Self::Error>
fn handle_request_raw( &mut self, req_buf: &mut [u8], reply_buf: &mut [u8], handler: impl FnOnce(&mut CdrReader<'_>, &mut CdrWriter<'_>) -> Result<(), TransportError>, ) -> Result<bool, Self::Error>
Handle one request by STREAMING: the handler reads fields off the wire and writes the reply’s fields straight back, so neither the request nor the reply is ever materialised as a value.
phase-382 W1’. handle_request_boxed above boxes the REPLY because the
parameter responses are enormous — GetParametersResponse measures
1,176,072 bytes. It does not box the REQUEST, which is deserialized by
value into a stack local one line above the handler, and
SetParametersRequest measures 1,192,968 bytes. So every
ros2 param set against a node put a 1.19 MB local on the calling
task’s stack — larger than the reply the boxing exists for, on every
platform, with param-services live on Zephyr.
Streaming removes both, and removes the alloc requirement with them:
the whole value is never needed, because serialisation happens on the
line after construction. Three things make it safe rather than clever:
- No
rcl_interfacesmessage uses a DHEADER — everySerializeimpl is plain sequential CDR, so hand-written field writes are byte-identical to the generated ones. (If a future message gains XCDR2 extensibility this stops being true for THAT message; see RFC-0055.) req_bufandreply_bufare disjoint, so a handler can hold the reader and the writer at once.CdrReader::read_stringborrows out ofreq_bufrather than copying, so a handler can look a name up without a buffer of its own.
The cost is that the hand-written writes can drift from the generated
Serialize. Guard it with a round-trip test that deserialises the
streamed bytes back into the generated type — the by-value handler makes
a good test-only oracle.
No alloc, deliberately: this is the seam that lets param-services and
lifecycle-services build without it.
Sourcefn request_subscription_actual_qos(&self) -> QoSProfile
fn request_subscription_actual_qos(&self) -> QoSProfile
The profile this entity is ACTUALLY running — what the backend GRANTED, not what the call site requested.
Upstream’s rmw_service_request_subscription_get_actual_qos. The two differ whenever a
backend serves something other than what it was asked for, and the
difference is exactly what answers “why is nothing arriving”: a
RELIABLE reader does not match a BEST_EFFORT writer, and a clamped
history depth drops samples the caller believed were kept.
Per-field, and an unreportable field is an ABSENCE. A backend that
determines four policies and not the fifth returns the four and leaves
the fifth at its Unknown variant. Never echo the request into a field
you did not read back — that is the inverse of this method’s meaning,
and it is what the six C-ABI slots did until this phase.
The default is QoSProfile::QOS_PROFILE_UNKNOWN — every field an
absence — which is the honest answer for a backend with no read-back at
all (XRCE has none; zenoh-pico’s QoS is per-message flags). It is NOT
the request, and it is NOT QOS_PROFILE_DEFAULT.
Sourcefn response_publisher_actual_qos(&self) -> QoSProfile
fn response_publisher_actual_qos(&self) -> QoSProfile
The profile this entity is ACTUALLY running — what the backend GRANTED, not what the call site requested.
Upstream’s rmw_service_response_publisher_get_actual_qos. The two differ whenever a
backend serves something other than what it was asked for, and the
difference is exactly what answers “why is nothing arriving”: a
RELIABLE reader does not match a BEST_EFFORT writer, and a clamped
history depth drops samples the caller believed were kept.
Per-field, and an unreportable field is an ABSENCE. A backend that
determines four policies and not the fifth returns the four and leaves
the fifth at its Unknown variant. Never echo the request into a field
you did not read back — that is the inverse of this method’s meaning,
and it is what the six C-ABI slots did until this phase.
The default is QoSProfile::QOS_PROFILE_UNKNOWN — every field an
absence — which is the honest answer for a backend with no read-back at
all (XRCE has none; zenoh-pico’s QoS is per-message flags). It is NOT
the request, and it is NOT QOS_PROFILE_DEFAULT.
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".
Implementations on Foreign Types§
Source§impl ServiceTrait for CffiService
impl ServiceTrait for CffiService
Source§fn request_subscription_actual_qos(&self) -> QoSProfile
fn request_subscription_actual_qos(&self) -> QoSProfile
What the REQUEST endpoint — the subscription this service takes requests on — was granted.
Source§fn response_publisher_actual_qos(&self) -> QoSProfile
fn response_publisher_actual_qos(&self) -> QoSProfile
What the RESPONSE endpoint — the publisher this service answers on — was granted. Not necessarily the same profile: one request built both endpoints, and they negotiate against different peers.