Skip to content

Expose Service, Action, and the CUDA/pinned memory-pool transport to the C++ node binding #2686

Description

@rtr-thaopt

Feature request: expose Service, Action, and the CUDA/pinned memory-pool transport to the C++ node binding

Summary

The Rust and Python node APIs have grown three first-class capabilities that the
C++ node binding does not expose:

  1. Service (request/reply)send_service_request / send_service_response
    / recv_service_response, with REQUEST_ID (UUID v7) correlation and built-in
    timeout + fault-tolerance.
  2. Action (goal / feedback / result)new_request_id, GOAL_ID /
    GOAL_STATUS metadata, feedback + terminal result, cancellation, and
    recv_action_result with timeout + fault-tolerance.
  3. Pinned/CUDA memory-pool transportregister_memory_pool /
    write_memory_pool / read_memory_pool / free_memory_pool with the DORADMA
    pinned-host fast path (added in feat: add pinned memory-pool transport for CPU/CUDA tensor transfer #2168, auto pinned/pageable selection in feat(memory-pool): auto-select pinned vs pageable DMA based on tensor size #2386).

A C++-only downstream cannot use any of these without hand-patching the cxx bridge
(apis/c++/node/src/lib.rs) and rebuilding it. This request is to bring the C++
binding to parity so downstreams stop re-implementing framework primitives.

Current state of the C++ binding (verified against origin/main)

What C++ already has and works well:

  • send_output, send_output_with_metadata, send_arrow_output (metadata-carrying).
  • Full Metadata read/write surface (get_str/set_string/… + to_json/list_keys).
  • Event receive variants (next_event, next_event_timeout, try_next_event),
    close_outputs, NodeFailed / Reload events, node_config_json /
    dataflow_descriptor_json.

What C++ is missing (present in apis/rust/node):

Capability Rust API (location) C++ binding
Service request DoraNode::send_service_request (apis/rust/node/src/node/mod.rs) ❌ absent
Service response DoraNode::send_service_response ❌ absent
Await response w/ timeout+FT EventStream::recv_service_response(rid, node_id, timeout) (event_stream/mod.rs) ❌ absent
Request-id helper DoraNode::new_request_id (UUID v7) ❌ absent
Action goal/feedback/result GOAL_ID/GOAL_STATUS/send_action_* + recv_action_result ❌ absent
Memory-pool register/write/read/free apis/rust/node/src/node/control_channel.rs + dora-memory-pool crate ❌ absent
CUDA host-mapped/pinned helpers apis/python/node/dora/cuda.py (cudaHostRegister + cudaHostGetDevicePointer) ❌ absent

Grep confirmation (empty = not exposed):

$ git show origin/main:apis/c++/node/src/lib.rs | grep -iE "send_service|service_response|memory_pool|register_pool"
# (no output)

Note: send_output_with_metadata + Metadata.set_string technically let a C++ node
attach a REQUEST_ID manually, but there is no request-id generator, no
send_service_response passthrough helper, and — critically — no
recv_service_response / recv_action_result with the framework's timeout and
server-restart fault-tolerance. So the useful part (correlation + FT lifecycle) is
still C++-inaccessible.

Requests

1. Service request/reply in the C++ binding

Expose cxx equivalents of:

  • send_service_request(output_id, metadata_params, data) -> request_id
  • send_service_response(output_id, metadata_params, data)
  • recv_service_response(request_id, server_node_id, timeout) -> Event/result
  • new_request_id() -> String

so a C++ node gets the same REQUEST_ID-correlated, timeout-and-FT request/reply as
examples/service-example/.

2. Action (goal/feedback/result) in the C++ binding

Expose the goal-id/feedback/result/cancel surface + recv_action_result(goal_id, server_node_id, timeout), mirroring docs/patterns.md §3.

3. CUDA/pinned memory-pool transport in the C++ binding

Expose the pool lifecycle (register_memory_pool / write_memory_pool /
read_memory_pool / free_memory_pool) plus the DORADMA fast path, with a way to
select the host-mapped/pinned path explicitly.

Hard constraint for embedded targets (Tegra / Jetson iGPU): the cudaIpc*
handle path in dora/cuda.py does not work on the Xavier NX integrated GPU
cudaIpcGetMemHandle fails on Tegra. The C++/portable path MUST allow forcing the
cudaHostRegister(...Mapped) + cudaHostGetDevicePointer (unified-memory, zero-copy
on Tegra LPDDR) route and MUST NOT hard-require CUDA IPC. Ideally the auto-select
in #2386 gains an override / capability flag for IPC-less platforms.

Suggested approach

Acceptance criteria

  • A C++ node can issue a service request and await a REQUEST_ID-correlated
    response with a timeout, with framework fault-tolerance on server restart.
  • A C++ node can run the action goal/feedback/result + cancel lifecycle.
  • A C++ node can register a memory pool and do zero-copy CPU↔CUDA transfer via
    the host-pinned path on a NVIDIA Jetson (no CUDA IPC).
  • A C++ examples/service-example (or equivalent) mirrors the Rust one.
  • Surface is generated into dora-node-api.h; existing C++ examples still build.

References

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions