Skip to main content

ServiceTrait

Trait ServiceTrait 

Source
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 handle_request<S: RosService>(
        &mut self,
        req_buf: &mut [u8],
        reply_buf: &mut [u8],
        handler: impl FnOnce(&S::Request) -> S::Reply,
    ) -> Result<bool, Self::Error>
       where Self::Error: From<TransportError> { ... }
    fn handle_request_boxed<S: RosService>(
        &mut self,
        req_buf: &mut [u8],
        reply_buf: &mut [u8],
        handler: impl FnOnce(&S::Request) -> Box<S::Reply>,
    ) -> Result<bool, Self::Error>
       where 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> { ... }
}
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

  1. Executor calls take_request(buf).
  2. If Some(req) returned, decode, run handler, encode reply.
  3. 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§

Source

type Error

Error type for service operations

Required Methods§

Source

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.

Source

fn send_response( &mut self, sequence_number: i64, data: &[u8], ) -> Result<(), Self::Error>

Send a reply for the given sequence number. Non-blocking from the application’s perspective; the backend may queue the reply for transport-level transmission.

Provided Methods§

Source

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.

Source

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).

Source

fn handle_request<S: RosService>( &mut self, req_buf: &mut [u8], reply_buf: &mut [u8], handler: impl FnOnce(&S::Request) -> S::Reply, ) -> Result<bool, Self::Error>
where Self::Error: From<TransportError>,

Handle a service request with typed messages

Source

fn handle_request_boxed<S: RosService>( &mut self, req_buf: &mut [u8], reply_buf: &mut [u8], handler: impl FnOnce(&S::Request) -> Box<S::Reply>, ) -> Result<bool, Self::Error>
where Self::Error: From<TransportError>,

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.

Source

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>,

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_interfaces message uses a DHEADER — every Serialize impl 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_buf and reply_buf are disjoint, so a handler can hold the reader and the writer at once.
  • CdrReader::read_string borrows out of req_buf rather 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.

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§