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>(
        &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> { ... }
}
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>( &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>,

Handle a service request with typed messages

Source

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

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

Implementations on Foreign Types§

Source§

impl ServiceTrait for CffiService

Source§

type Error = TransportError

Source§

fn has_request(&self) -> bool

Source§

fn take_request<'a>( &mut self, buf: &'a mut [u8], ) -> Result<Option<ServiceRequest<'a>>, TransportError>

Source§

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

Implementors§