|
nros rmw-cffi
C vtable for plugging a third-party RMW backend into nros
|
#include <rmw_vtable.h>
Data Fields | |
| nros_rmw_ret_t(* | assert_publisher_liveliness )(nros_rmw_publisher_t *publisher) |
| int32_t(* | call_raw )(nros_rmw_service_client_t *client, const uint8_t *request, size_t req_len, uint8_t *reply_buf, size_t reply_buf_len) |
| nros_rmw_ret_t(* | close )(nros_rmw_session_t *session) |
| nros_rmw_ret_t(* | create_publisher )(nros_rmw_session_t *session, const char *topic_name, const char *type_name, const char *type_hash, uint32_t domain_id, const nros_rmw_qos_t *qos, nros_rmw_publisher_t *out) |
| nros_rmw_ret_t(* | create_service_client )(nros_rmw_session_t *session, const char *service_name, const char *type_name, const char *type_hash, uint32_t domain_id, const nros_rmw_qos_t *qos, nros_rmw_service_client_t *out) |
| nros_rmw_ret_t(* | create_service_server )(nros_rmw_session_t *session, const char *service_name, const char *type_name, const char *type_hash, uint32_t domain_id, const nros_rmw_qos_t *qos, nros_rmw_service_server_t *out) |
| nros_rmw_ret_t(* | create_subscriber )(nros_rmw_session_t *session, const char *topic_name, const char *type_name, const char *type_hash, uint32_t domain_id, const nros_rmw_qos_t *qos, nros_rmw_subscriber_t *out) |
| void(* | destroy_publisher )(nros_rmw_publisher_t *publisher) |
| void(* | destroy_service_client )(nros_rmw_service_client_t *client) |
| void(* | destroy_service_server )(nros_rmw_service_server_t *server) |
| void(* | destroy_subscriber )(nros_rmw_subscriber_t *subscriber) |
| nros_rmw_ret_t(* | drive_io )(nros_rmw_session_t *session, int32_t timeout_ms) |
| int32_t(* | has_data )(nros_rmw_subscriber_t *subscriber) |
| int32_t(* | has_request )(nros_rmw_service_server_t *server) |
| int32_t(* | next_deadline_ms )(const nros_rmw_session_t *session) |
| nros_rmw_ret_t(* | open )(const char *locator, uint8_t mode, uint32_t domain_id, const char *node_name, nros_rmw_session_t *out) |
| nros_rmw_ret_t(* | ping_session )(nros_rmw_session_t *session, int32_t timeout_ms) |
| int32_t(* | process_raw_in_place )(nros_rmw_subscriber_t *subscriber, void *ctx, void(*cb)(void *ctx, const uint8_t *ptr, size_t len)) |
| nros_rmw_ret_t(* | pub_commit )(nros_rmw_publisher_t *publisher, void *token, size_t actual_len) |
| void(* | pub_discard )(nros_rmw_publisher_t *publisher, void *token) |
| nros_rmw_ret_t(* | pub_loan )(nros_rmw_publisher_t *publisher, size_t requested_len, uint8_t **out_buf, size_t *out_cap, void **out_token) |
| nros_rmw_ret_t(* | publish_raw )(nros_rmw_publisher_t *publisher, const uint8_t *data, size_t len) |
| nros_rmw_ret_t(* | publish_streamed )(nros_rmw_publisher_t *publisher, void(*size_cb)(size_t *out_total_len, void *user_ctx), void(*chunk_cb)(uint8_t *out_buf, size_t cap, size_t *out_written, void *user_ctx), void *user_ctx) |
| nros_rmw_ret_t(* | register_publisher_event )(nros_rmw_publisher_t *publisher, nros_rmw_event_kind_t kind, uint32_t deadline_ms, nros_rmw_event_callback_t cb, void *user_context) |
| nros_rmw_ret_t(* | register_subscriber_event )(nros_rmw_subscriber_t *subscriber, nros_rmw_event_kind_t kind, uint32_t deadline_ms, nros_rmw_event_callback_t cb, void *user_context) |
| nros_rmw_ret_t(* | send_reply )(nros_rmw_service_server_t *server, int64_t seq, const uint8_t *data, size_t len) |
| nros_rmw_ret_t(* | send_request_raw )(nros_rmw_service_client_t *client, const uint8_t *request, size_t req_len) |
| int32_t(* | service_server_available )(nros_rmw_service_client_t *client) |
| nros_rmw_ret_t(* | set_wake_callback )(nros_rmw_session_t *session, void(*cb)(void *ctx), void *ctx) |
| int32_t(* | sub_borrow )(nros_rmw_subscriber_t *subscriber, const uint8_t **out_buf, size_t *out_len, void **out_token) |
| void(* | sub_release )(nros_rmw_subscriber_t *subscriber, void *token) |
| int32_t(* | subscriber_supports_in_place )(nros_rmw_subscriber_t *subscriber) |
| int32_t(* | try_recv_raw )(nros_rmw_subscriber_t *subscriber, uint8_t *buf, size_t buf_len) |
| int32_t(* | try_recv_reply_raw )(nros_rmw_service_client_t *client, uint8_t *reply_buf, size_t reply_buf_len) |
| int32_t(* | try_recv_request )(nros_rmw_service_server_t *server, uint8_t *buf, size_t buf_len, int64_t *seq_out) |
| int32_t(* | try_recv_sequence )(nros_rmw_subscriber_t *subscriber, uint8_t *buf, size_t per_msg_cap, size_t max_msgs, size_t *out_lens) |
| nros_rmw_ret_t(* nros_rmw_vtable_t::assert_publisher_liveliness) (nros_rmw_publisher_t *publisher) |
Phase 108.B — manually assert this publisher's liveliness. Required for MANUAL_BY_TOPIC / MANUAL_BY_NODE liveliness kinds; no-op (return NROS_RMW_RET_OK) for other kinds. NULL function pointer = backend doesn't support manual liveliness; runtime returns NROS_RMW_RET_OK for AUTOMATIC / NONE callers and NROS_RMW_RET_UNSUPPORTED for MANUAL_*.
| int32_t(* nros_rmw_vtable_t::call_raw) (nros_rmw_service_client_t *client, const uint8_t *request, size_t req_len, uint8_t *reply_buf, size_t reply_buf_len) |
| nros_rmw_ret_t(* nros_rmw_vtable_t::close) (nros_rmw_session_t *session) |
| nros_rmw_ret_t(* nros_rmw_vtable_t::create_publisher) (nros_rmw_session_t *session, const char *topic_name, const char *type_name, const char *type_hash, uint32_t domain_id, const nros_rmw_qos_t *qos, nros_rmw_publisher_t *out) |
Create a publisher. The runtime fills out->topic_name, out->type_name, out->qos before this call; the backend writes out->backend_data and out->can_loan_messages.
| nros_rmw_ret_t(* nros_rmw_vtable_t::create_service_client) (nros_rmw_session_t *session, const char *service_name, const char *type_name, const char *type_hash, uint32_t domain_id, const nros_rmw_qos_t *qos, nros_rmw_service_client_t *out) |
| nros_rmw_ret_t(* nros_rmw_vtable_t::create_service_server) (nros_rmw_session_t *session, const char *service_name, const char *type_name, const char *type_hash, uint32_t domain_id, const nros_rmw_qos_t *qos, nros_rmw_service_server_t *out) |
| nros_rmw_ret_t(* nros_rmw_vtable_t::create_subscriber) (nros_rmw_session_t *session, const char *topic_name, const char *type_name, const char *type_hash, uint32_t domain_id, const nros_rmw_qos_t *qos, nros_rmw_subscriber_t *out) |
| void(* nros_rmw_vtable_t::destroy_publisher) (nros_rmw_publisher_t *publisher) |
| void(* nros_rmw_vtable_t::destroy_service_client) (nros_rmw_service_client_t *client) |
| void(* nros_rmw_vtable_t::destroy_service_server) (nros_rmw_service_server_t *server) |
| void(* nros_rmw_vtable_t::destroy_subscriber) (nros_rmw_subscriber_t *subscriber) |
| nros_rmw_ret_t(* nros_rmw_vtable_t::drive_io) (nros_rmw_session_t *session, int32_t timeout_ms) |
| int32_t(* nros_rmw_vtable_t::has_data) (nros_rmw_subscriber_t *subscriber) |
| int32_t(* nros_rmw_vtable_t::has_request) (nros_rmw_service_server_t *server) |
| int32_t(* nros_rmw_vtable_t::next_deadline_ms) (const nros_rmw_session_t *session) |
Phase 110.0 — backend's next internal-event deadline in milliseconds from now (lease keepalive, heartbeat, reader ACK-NACK timeout, etc.). The runtime caps its drive_io timeout against min(user_timeout, timer_deadline, this) so quiet links don't wake early, see no user-visible work, and round-trip back into drive_io.
Returns a non-negative milliseconds value, or a negative value meaning "no internal deadline" (treat as None).
NULL function pointer is permitted — the runtime treats it the same as a negative return.
| nros_rmw_ret_t(* nros_rmw_vtable_t::open) (const char *locator, uint8_t mode, uint32_t domain_id, const char *node_name, nros_rmw_session_t *out) |
Open a session. The runtime supplies a zero-initialised nros_rmw_session_t via out with node_name / namespace_ already filled. The backend writes out->backend_data.
| nros_rmw_ret_t(* nros_rmw_vtable_t::ping_session) (nros_rmw_session_t *session, int32_t timeout_ms) |
Phase 124.F.1 — session-level connectivity probe.
Sends a wire-level round-trip probe ("is the peer / agent /
router still reachable?") and waits up to timeout_ms for a reply. No discovery state required — cheaper than the service-availability probe (which needs matched-publication bookkeeping). Lesson from micro-ROS's rmw_uros_ping_agent.
Returns:
NROS_RMW_RET_OK — peer responded within budget.NROS_RMW_RET_TIMEOUT — no reply before timeout_ms.NROS_RMW_RET_UNSUPPORTED — backend can't probe (DDS with no participant introspection).Implementation notes per backend:
z_send_ping (or session keep-alive piggyback).uxr_ping_agent_session_until_timeout.RET_UNSUPPORTED.NULL function pointer = runtime surfaces NROS_RMW_RET_UNSUPPORTED to the caller.
| int32_t(* nros_rmw_vtable_t::process_raw_in_place) (nros_rmw_subscriber_t *subscriber, void *ctx, void(*cb)(void *ctx, const uint8_t *ptr, size_t len)) |
Borrow one ready message in place: hand its raw CDR bytes to cb (with the opaque ctx) for the duration of the call, then release the slot — no copy into a caller buffer. Returns 1 if a message was processed (cb invoked), NROS_RMW_RET_NO_DATA if none was ready, or a negative error. cb MUST NOT re-enter this subscriber's receive. NULL function pointer = unsupported (the runtime uses the buffered path).
| nros_rmw_ret_t(* nros_rmw_vtable_t::pub_commit) (nros_rmw_publisher_t *publisher, void *token, size_t actual_len) |
Phase 124.A — commit a previously loaned slot.
token MUST be a value returned from a prior pub_loan on the same publisher. actual_len is the byte count actually written into the slot (≤ the loan's out_cap). Triggers the wire send.
NULL = paired NULL with pub_loan.
| void(* nros_rmw_vtable_t::pub_discard) (nros_rmw_publisher_t *publisher, void *token) |
Phase 124.A — abandon a previously loaned slot.
Releases the slot without sending. token MUST be a value returned from a prior pub_loan on the same publisher.
NULL = paired NULL with pub_loan.
| nros_rmw_ret_t(* nros_rmw_vtable_t::pub_loan) (nros_rmw_publisher_t *publisher, size_t requested_len, uint8_t **out_buf, size_t *out_cap, void **out_token) |
Phase 124.A — zero-copy publisher loan.
Reserve a writable slot of at least requested_len bytes inside the backend's outbound buffer. Returns:
NROS_RMW_RET_OK + writes *out_buf / *out_cap / *out_token.NROS_RMW_RET_TRY_AGAIN if the backend has no slot available (caller may retry or fall back to a copy path).NROS_RMW_RET_INVALID_ARGUMENT on bad pointers / size.*out_cap may exceed requested_len. The slot's bytes are valid until the matching pub_commit or pub_discard runs. *out_token is an opaque per-loan handle the backend uses to match commit / discard back to the right slot.
NULL function pointer = backend doesn't natively lend; the runtime falls back to a per-publisher staging arena and emits a single memcpy on commit.
| nros_rmw_ret_t(* nros_rmw_vtable_t::publish_raw) (nros_rmw_publisher_t *publisher, const uint8_t *data, size_t len) |
| nros_rmw_ret_t(* nros_rmw_vtable_t::publish_streamed) (nros_rmw_publisher_t *publisher, void(*size_cb)(size_t *out_total_len, void *user_ctx), void(*chunk_cb)(uint8_t *out_buf, size_t cap, size_t *out_written, void *user_ctx), void *user_ctx) |
Phase 124.E.1 — streamed publish.
Caller hands the backend two callbacks. The backend invokes size_cb once to learn the total payload length, allocates a single slot of that size in its outbound buffer, then invokes chunk_cb repeatedly to fill the slot in chunks until the buffer is full. Saves the per-publisher staging buffer on RAM-constrained nodes — useful for large messages on MCUs where the staging buffer dominates .bss.
Callback contract:
size_cb(*out_total_len, user_ctx) — write the exact total payload length, in bytes, to *out_total_len. Called exactly once per publish_streamed invocation.chunk_cb(out_buf, cap, *out_written, user_ctx) — write up to cap bytes starting at out_buf, then report the count written via *out_written. The backend may call chunk_cb repeatedly until the total promised by size_cb has been delivered. *out_written == 0 means EOF; the backend tears down the slot.Lesson from micro-ROS's rmw_uros_set_continous_serialization_callbacks: pass the callbacks per-call rather than binding them to publisher state, so different messages on the same publisher can use different serialisation strategies.
NULL function pointer = backend doesn't stream; the runtime falls back to a one-shot staging buffer (capped at the configured NROS_MAX_STREAM_CHUNK) + publish_raw.
| nros_rmw_ret_t(* nros_rmw_vtable_t::register_publisher_event) (nros_rmw_publisher_t *publisher, nros_rmw_event_kind_t kind, uint32_t deadline_ms, nros_rmw_event_callback_t cb, void *user_context) |
Register a callback for a publisher-side event. Same NULL / unsupported-kind conventions as register_subscriber_event. deadline_ms is consulted for OFFERED_DEADLINE_MISSED only.
| nros_rmw_ret_t(* nros_rmw_vtable_t::register_subscriber_event) (nros_rmw_subscriber_t *subscriber, nros_rmw_event_kind_t kind, uint32_t deadline_ms, nros_rmw_event_callback_t cb, void *user_context) |
Register a callback for a subscriber-side event. NULL function pointer = backend doesn't generate any subscriber events. Specific kind unsupported on a backend that supports some events = NROS_RMW_RET_UNSUPPORTED return. deadline_ms is consulted for REQUESTED_DEADLINE_MISSED only; ignored otherwise.
| nros_rmw_ret_t(* nros_rmw_vtable_t::send_reply) (nros_rmw_service_server_t *server, int64_t seq, const uint8_t *data, size_t len) |
| nros_rmw_ret_t(* nros_rmw_vtable_t::send_request_raw) (nros_rmw_service_client_t *client, const uint8_t *request, size_t req_len) |
Phase 130.4 — non-blocking send_request_raw.
Sends the request to the backend without blocking for a reply. Returns immediately. NULL = the runtime falls back to storing the pending request in CffiServiceClient and invoking call_raw on the next try_recv_reply_raw_slot call (blocking inside the executor, the Phase 127.C.4 root cause behaviour). Backends that implement this slot must also implement try_recv_reply_raw_slot so the executor's poll loop can drain the reply non-blockingly.
| int32_t(* nros_rmw_vtable_t::service_server_available) (nros_rmw_service_client_t *client) |
Phase 124.C.1 — service-server availability probe.
Returns 1 if ≥ 1 matching server has been discovered on the RMW graph, 0 if none yet, or a negative nros_rmw_ret_t constant on backend error. The runtime exposes this to user code as nros_client_server_available() / Client<S>::server_available() — clients use it to gate the first call_raw so a startup-ordering race doesn't surface as a request-side timeout.
Implementation notes per backend:
z_session tracks matched queryables via interest declarations.NROS_RMW_RET_UNSUPPORTED.NULL function pointer = backend cannot answer; the runtime surfaces NROS_RMW_RET_UNSUPPORTED to the caller.
| nros_rmw_ret_t(* nros_rmw_vtable_t::set_wake_callback) (nros_rmw_session_t *session, void(*cb)(void *ctx), void *ctx) |
Phase 124.B.1 — executor wake callback.
The runtime calls this once per session right after open with cb pointing at a runtime-supplied function and ctx pointing at the executor's wake state. The backend stores both in its per-session state and calls cb(ctx) whenever its transport-notification path fires — datagram arrival, condvar wake-up, select-fd ready, etc. The runtime cb does flag-write + condvar-signal atomically so a spin_once blocked on the wake condvar resumes immediately.
cb == NULL clears any previously installed callback; the backend must drop the stored (cb, ctx) and never invoke again after this returns.
NULL slot = backend has no asynchronous wake path (purely poll-driven: XRCE, bare-metal). The runtime still drains the session on its deadline-bound cv-wait boundary.
| int32_t(* nros_rmw_vtable_t::sub_borrow) (nros_rmw_subscriber_t *subscriber, const uint8_t **out_buf, size_t *out_len, void **out_token) |
Phase 124.A — zero-copy subscriber borrow.
Borrow a read-only view of the next available message in place, without copying into a caller buffer. Returns:
>= 0 — message length; writes *out_buf / *out_token.0 — no message ready (subscriber empty).< 0 — error (see nros_rmw_ret_t codes negated).The view is valid until the matching sub_release runs. Only one borrow may be outstanding per subscriber at a time — callers MUST release before requesting another borrow.
NULL function pointer = backend doesn't natively borrow; the runtime falls back to try_recv_raw into a staging buffer.
| void(* nros_rmw_vtable_t::sub_release) (nros_rmw_subscriber_t *subscriber, void *token) |
Phase 124.A — release a previously borrowed view.
token MUST be a value returned from a prior sub_borrow on the same subscriber. Lets the next message advance into the buffer.
NULL = paired NULL with sub_borrow.
| int32_t(* nros_rmw_vtable_t::subscriber_supports_in_place) (nros_rmw_subscriber_t *subscriber) |
Capability query: does this subscriber support process_raw_in_place()? Returns 1 if yes, 0 if no. The runtime consults this at subscription registration to choose in-place dispatch over the buffered (copying) path. NULL function pointer = treated as unsupported (buffered path).
| int32_t(* nros_rmw_vtable_t::try_recv_raw) (nros_rmw_subscriber_t *subscriber, uint8_t *buf, size_t buf_len) |
| int32_t(* nros_rmw_vtable_t::try_recv_reply_raw) (nros_rmw_service_client_t *client, uint8_t *reply_buf, size_t reply_buf_len) |
Phase 130.4 — non-blocking try_recv_reply_raw.
Polls the backend for a reply. >= 0 = reply bytes copied into reply_buf. NROS_RMW_RET_NO_DATA = no reply yet. Other negative = backend error. NULL = the runtime falls back to the blocking call_raw path (Phase 127.C.4 behaviour). Paired with send_request_raw — backends implement both or neither.
| int32_t(* nros_rmw_vtable_t::try_recv_request) (nros_rmw_service_server_t *server, uint8_t *buf, size_t buf_len, int64_t *seq_out) |
| int32_t(* nros_rmw_vtable_t::try_recv_sequence) (nros_rmw_subscriber_t *subscriber, uint8_t *buf, size_t per_msg_cap, size_t max_msgs, size_t *out_lens) |
Phase 124.D.1 — burst-take.
Drains up to max_msgs queued messages into a contiguous caller buffer in a single backend call, avoiding N × vtable dispatch when a burst-sensor subscriber catches up on a backlog (e.g. a 100 Hz IMU feed polled at 10 Hz).
Storage contract:
buf is a contiguous max_msgs * per_msg_cap block.buf + i * per_msg_cap and has byte length out_lens[i].out_lens is at least max_msgs entries long.Returns:
>= 0 — count of messages taken (0..=max_msgs).< 0 — nros_rmw_ret_t error code; partial drains MUST use the count form, not error-out.NULL function pointer = backend doesn't natively batch; the runtime emits a try_recv_raw loop fallback in CffiSubscriber::try_recv_sequence. The fallback gives identical observable behaviour (each call still costs N vtable hops) but lets user code commit to the batched API.