|
| 1 | +# `rmw_unix_socket_cpp` — Code Walkthrough |
| 2 | + |
| 3 | +A reference for reading the source line by line. Every doc traces a single feature **from the user-visible rclcpp API down through rcl into our RMW**, then explains what our RMW actually does on Linux primitives. |
| 4 | + |
| 5 | +Layer model used throughout: |
| 6 | + |
| 7 | +``` |
| 8 | ++-----------------------------------------------------------+ |
| 9 | +| User code: rclcpp::Node, create_publisher(), ... | <-- C++ user-facing API |
| 10 | ++-----------------------------------------------------------+ |
| 11 | +| rclcpp (/home/abe/ros2_source/rclcpp/...) | <-- C++ wrapper, executors, callback dispatch |
| 12 | ++-----------------------------------------------------------+ |
| 13 | +| rcl (/opt/ros/jazzy/include/rcl/...) | <-- C client library, language-agnostic |
| 14 | ++-----------------------------------------------------------+ |
| 15 | +| rmw (/opt/ros/jazzy/include/rmw/...) | <-- C ABI; the function set we implement |
| 16 | ++-----------------------------------------------------------+ |
| 17 | +| rmw_unix_socket_cpp (rmw_unix_socket_cpp/src/...) | <-- This repo |
| 18 | ++-----------------------------------------------------------+ |
| 19 | +| Linux kernel: AF_UNIX, epoll, eventfd, shm_open, mmap | |
| 20 | ++-----------------------------------------------------------+ |
| 21 | +``` |
| 22 | + |
| 23 | +The reader should be able to start at any rclcpp API call and follow the chain down to a kernel syscall. |
| 24 | + |
| 25 | +## Reading order |
| 26 | + |
| 27 | +| # | Document | What it covers | |
| 28 | +|---|---|---| |
| 29 | +| 00 | [overview.md](00_overview.md) | Layer architecture, how the RMW is dynamically loaded, identifier mechanism, the `extern "C"` ABI surface, conventions used in every file. | |
| 30 | +| 01 | [initialization.md](01_initialization.md) | `rclcpp::init` → `rcl_init` → `rmw_init`. Context lifecycle, shutdown, `rmw_init_options_t`. | |
| 31 | +| 02 | [node_lifecycle.md](02_node_lifecycle.md) | `rclcpp::Node` constructor → `rcl_node_init` → `rmw_create_node`. Registry registration, graph guard condition. | |
| 32 | +| 03 | [publisher.md](03_publisher.md) | `create_publisher` → `rcl_publisher_init` → `rmw_create_publisher`. The publish path, QoS resolution, TRANSIENT_LOCAL replay, subscriber-path caching. | |
| 33 | +| 04 | [subscription.md](04_subscription.md) | `create_subscription` → `rcl_subscription_init` → `rmw_create_subscription`. The take path, the queue, the on-new-message callback, drain loop. | |
| 34 | +| 05 | [service_client.md](05_service_client.md) | Services and clients. `rmw_create_service` / `rmw_create_client`, the request/response wire types, client-route caching. | |
| 35 | +| 06 | [parameters.md](06_parameters.md) | Parameters live entirely in rclcpp on top of services and a `/parameter_events` topic. Trace `declare_parameter` through `NodeParameters` and `ParameterService`. | |
| 36 | +| 07 | [timers.md](07_timers.md) | Timers live in rcl + rclcpp. **They never enter the RMW** — but they do enter the wait set. | |
| 37 | +| 08 | [callback_groups.md](08_callback_groups.md) | rclcpp-only. How entities are partitioned for executors. **No RMW interaction.** | |
| 38 | +| 09 | [executor.md](09_executor.md) | rclcpp::Executor / `spin()`. The event loop: collect entities → build wait set → `rcl_wait` → execute ready entity. **The only entry point from rclcpp into our RMW's wait/take/publish paths.** | |
| 39 | +| 10 | [wait_set.md](10_wait_set.md) | `rmw_create_wait_set`, `rmw_wait`. Our epoll-based implementation: drain → check ready → block → re-drain. | |
| 40 | +| 11 | [guard_condition.md](11_guard_condition.md) | `rmw_create_guard_condition`, `rmw_trigger_guard_condition`. Our eventfd-based implementation. Used for graph events and shutdown signaling. | |
| 41 | +| 12 | [graph_events.md](12_graph_events.md) | Discovery API: `rmw_get_node_names`, `rmw_get_topic_names_and_types`, `rmw_count_publishers`, etc. All reduce to registry queries. | |
| 42 | +| 13 | [registry.md](13_registry.md) | The shared-memory discovery registry. Lock-free per-slot seqlock + atomic state machine. The `/dev/shm/ros2_uds_<domain>` file. | |
| 43 | +| 14 | [transport.md](14_transport.md) | `AF_UNIX` + `SOCK_DGRAM` socket layer. Send/recv path. Socket-file naming. Stale socket cleanup. | |
| 44 | +| 15 | [serialization.md](15_serialization.md) | CDR serialization via `fastcdr` + `rosidl_typesupport_fastrtps_*` generated callbacks. | |
| 45 | + |
| 46 | +## Quick file map of this RMW |
| 47 | + |
| 48 | +| File | Lines | Purpose | |
| 49 | +|---|--:|---| |
| 50 | +| [identifier.hpp](../rmw_unix_socket_cpp/src/identifier.hpp) | 13 | The RMW implementation identifier string (compared by pointer). | |
| 51 | +| [types.hpp](../rmw_unix_socket_cpp/src/types.hpp) | 243 | All internal structs: `UdsContext`, `UdsNode`, `UdsPublisher`, `UdsSubscription`, `UdsService`, `UdsClient`, `UdsGuardCondition`, `UdsWaitSet`, `WireHeader`, `ReceivedMessage`. | |
| 52 | +| [registry.hpp](../rmw_unix_socket_cpp/src/registry.hpp) / [.cpp](../rmw_unix_socket_cpp/src/registry.cpp) | 167 / 425 | Shared-memory discovery registry. | |
| 53 | +| [transport.hpp](../rmw_unix_socket_cpp/src/transport.hpp) / [.cpp](../rmw_unix_socket_cpp/src/transport.cpp) | 54 / 218 | UDS socket layer. | |
| 54 | +| [serialization.hpp](../rmw_unix_socket_cpp/src/serialization.hpp) / [.cpp](../rmw_unix_socket_cpp/src/serialization.cpp) | 46 / 150 | CDR (de)serialization via fastcdr. | |
| 55 | +| [rmw_init.cpp](../rmw_unix_socket_cpp/src/rmw_init.cpp) | 171 | `rmw_init`, `rmw_shutdown`, `rmw_context_fini`, init-options helpers. | |
| 56 | +| [rmw_node.cpp](../rmw_unix_socket_cpp/src/rmw_node.cpp) | 136 | `rmw_create_node`, `rmw_destroy_node`, graph guard condition accessor. | |
| 57 | +| [rmw_publisher.cpp](../rmw_unix_socket_cpp/src/rmw_publisher.cpp) | 376 | Publisher lifecycle, publish path, QoS query, GID query. | |
| 58 | +| [rmw_subscription.cpp](../rmw_unix_socket_cpp/src/rmw_subscription.cpp) | 515 | Subscription lifecycle, take variants, on-new-message callback. | |
| 59 | +| [rmw_service.cpp](../rmw_unix_socket_cpp/src/rmw_service.cpp) | 314 | Service server lifecycle, take_request, send_response. | |
| 60 | +| [rmw_client.cpp](../rmw_unix_socket_cpp/src/rmw_client.cpp) | 340 | Service client lifecycle, send_request, take_response, server-available query. | |
| 61 | +| [rmw_guard_condition.cpp](../rmw_unix_socket_cpp/src/rmw_guard_condition.cpp) | 83 | eventfd-backed guard condition. | |
| 62 | +| [rmw_wait.cpp](../rmw_unix_socket_cpp/src/rmw_wait.cpp) | 398 | `rmw_create_wait_set`, `rmw_wait` (epoll-based). | |
| 63 | +| [rmw_graph.cpp](../rmw_unix_socket_cpp/src/rmw_graph.cpp) | 417 | All discovery / introspection APIs. | |
| 64 | +| [rmw_event.cpp](../rmw_unix_socket_cpp/src/rmw_event.cpp) | 74 | Event init/take stubs — events are not generated. | |
| 65 | +| [rmw_features.cpp](../rmw_unix_socket_cpp/src/rmw_features.cpp) | 84 | `rmw_get_implementation_identifier`, `rmw_feature_supported`, QoS compatibility check. | |
| 66 | +| [rmw_serialize.cpp](../rmw_unix_socket_cpp/src/rmw_serialize.cpp) | 68 | `rmw_serialize` / `rmw_deserialize` (standalone). | |
| 67 | +| [rmw_unsupported.cpp](../rmw_unix_socket_cpp/src/rmw_unsupported.cpp) | 168 | All unsupported APIs (loaned messages, dynamic types, network flow endpoints). | |
| 68 | + |
| 69 | +## Conventions used in this RMW (read before everything else) |
| 70 | + |
| 71 | +- **Implementation identifier is compared by pointer, not by string.** Every RMW handle (`rmw_node_t`, `rmw_publisher_t`, ...) carries an `implementation_identifier` field. `RMW_CHECK_TYPE_IDENTIFIERS_MATCH` does a pointer compare. That is why `identifier.hpp` declares the identifier as `inline constexpr char[]` — a single address across all TUs. |
| 72 | +- **All entry points are `extern "C"`.** rcl dlopens the RMW shared library and calls fixed C symbols. C++ name mangling would break linkage. Every `.cpp` file in `src/` wraps its public functions in `extern "C" { ... }`. |
| 73 | +- **No exceptions cross the RMW boundary.** Every RMW function returns `rmw_ret_t` or a pointer. Internal C++ exceptions from fastcdr are caught at the boundary (see [serialization.cpp:139-146](../rmw_unix_socket_cpp/src/serialization.cpp#L139-L146)). |
| 74 | +- **The `*->data` opaque pointer holds our private struct.** `rmw_publisher_t::data` is a `void *` from rcl's perspective. We cast it to `UdsPublisher *` on every call. |
| 75 | +- **Lock-free registry, mutex-protected per-entity caches.** The shared-memory registry uses a per-slot seqlock + atomic state machine (no global mutex). Per-publisher/subscriber/client/service caches use ordinary `std::mutex` because they are process-local. |
| 76 | + |
| 77 | +If something here surprises you, [DESIGN.md](../rmw_unix_socket_cpp/DESIGN.md) has the rationale. |
0 commit comments