Skip to content

Commit 7d961ee

Browse files
add readme
1 parent c43f554 commit 7d961ee

1 file changed

Lines changed: 77 additions & 0 deletions

File tree

‎docs/README.md‎

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
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

Comments
 (0)