Populate message info for intra-process messages - #3271
Open
thomasmoore-torc wants to merge 5 commits into
Open
thomasmoore-torc wants to merge 5 commits into
thomasmoore-torc wants to merge 5 commits into
Conversation
(cherry picked from commit 8f255f3da12d5d0cc8aa5a49f69bb74339226820) (cherry picked from commit 94a3d5856c046e3a7b6bda942394cd8662926c28) Signed-off-by: Thomas Moore <thomas.moore@torc.ai>
(cherry picked from commit f2302935bd1b41a75f56c6a9f4ef5b7fbe4d7e9b) (cherry picked from commit 8a875ee1df60afcdb7ed68ceb27f097acdcdacf0) Signed-off-by: Thomas Moore <thomas.moore@torc.ai>
Signed-off-by: Thomas Moore <thomas.moore@torc.ai>
fujitatomoya
requested changes
Sep 20, 2026
| continue; | ||
| } | ||
|
|
||
| message_info.reception_sequence_number = subscription_it->second.reception_sequence_number++; |
Collaborator
There was a problem hiding this comment.
same racy condition here?
Author
There was a problem hiding this comment.
Switched to using std::atomic
Collaborator
|
@thomasmoore-torc i agree with you.
especially this behavior is not transparent and consistent for the applicaiton. got several comments above to be addressed. please have them checked out! |
Addresses five issues raised by @fujitatomoya on the PR: - publication_sequence_number and reception_sequence_number were plain uint64_t members mutated (via postfix ++) from do_intra_process_publish(), which only holds a shared (reader) lock on mutex_. Concurrent publishes could race on these counters. Made both std::atomic<uint64_t>; since std::atomic has no copy/move assignment, PublisherData/SubscriptionData gained explicit constructors and their insertion sites switched to try_emplace() instead of default-construct-then-assign. - IntraProcessBuffer::add_impl() (SharedPtr case) promoted a MessageUniquePtr to a MessageSharedPtr via `MessageSharedPtr(unique_msg.release())`, which drops the unique_ptr's (possibly allocator-aware) deleter in favor of plain delete -- an allocate/deallocate mismatch. Changed to `MessageSharedPtr(std::move(unique_msg))`, which preserves the original deleter via shared_ptr's converting constructor. - SubscriptionIntraProcess::take_data() checked `data.message.index() == std::variant_npos` to detect an empty buffer, but variant_npos only occurs for a valueless-by-exception variant. A default-constructed Data (what an empty buffer's consume() returns) holds a null pointer in its default alternative instead, so the check never fired. The original pre-refactor code checked pointer nullness directly (`if (!shared_msg) return nullptr;` / `if (!unique_msg) return nullptr;`); restored that via `std::visit` across both alternatives. - SubscriptionIntraProcessBuffer::provide_intra_process_message() called trigger_guard_condition() before buffer_->add(), unlike the sibling provide_intra_process_data() which does it in the correct order. This left a window where a consumer woken by the guard condition could find the buffer still empty. Reordered to add() then trigger. Signed-off-by: Thomas Moore <thomas.moore@torc.ai> Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Each test was verified to fail against the pre-fix code and pass against the fix in the previous commit: - test_intra_process_buffer.cpp: shared_buffer_add_preserves_custom_deleter adds a unique_ptr with a tracking deleter to a SharedPtr-typed buffer and confirms the *original* deleter runs when the promoted shared_ptr's refcount hits zero, not a plain `delete`. - test_subscription_intra_process.cpp (new file, plus scaffolding to construct a real SubscriptionIntraProcess directly, without a full Node/executor): take_data_returns_nullptr_when_buffer_is_empty covers the variant_npos check; provide_intra_process_message_adds_data_before_triggering patches rcl_trigger_guard_condition (via mocking_utils) to observe whether the buffer already has data at the moment the guard condition actually fires. - test_publisher.cpp: concurrent_publish_produces_unique_sequence_numbers publishes from 8 threads at once and checks every delivered publication_sequence_number is unique. Against the pre-fix plain uint64_t counter this fails reliably (8/8 runs); this is a stronger regression guard than a typical data-race test since the race window here is wide enough to hit consistently under load, not just occasionally. Signed-off-by: Thomas Moore <thomas.moore@torc.ai> Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Author
|
@fujitatomoya - I have addressed your comments and have also added some additional tests around your concerns |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
rclcpp: Populate message info for intra-process messages
Branch:
intra-process-message-info(this repo:ros2/rclcpp)Related PR: #3272 — "Iterate over IntraProcessBuffer instead of creating a vector" (
intra-process-reduce-copies). See Landing order below — no functional dependency, but the two touch overlapping code.Summary
Subscriptions that receive a message via intra-process delivery currently get a default-constructed, empty
rmw_message_info_t— no sequence number, no publisher GID, nofrom_intra_processflag. The same subscription receiving the same message via inter-process (rmw) delivery gets a fully populated one. Anything relying onmessage_info(deduplicating by publisher GID, checkingfrom_intra_process, etc.) behaves inconsistently depending on whether a given publish happens to stay in-process or not.This populates
message_infofor intra-process messages too, so subscribers see consistent, meaningful info regardless of delivery path.Changes
Populate message info for intra-process messages— threads anrmw_message_info_talongside the message through the intra-process publish path (Publisher::do_intra_process_publish/do_intra_process_ros_message_publish_and_return_shared→IntraProcessManager→IntraProcessBuffer→ subscription).IntraProcessBufferstorage changes from a bareshared_ptr/unique_ptrto a bundledIntraProcessBufferData(message +rmw_message_info_t), added with a copy constructor that deep-copies the unique-ptr alternative (needed soget_all_data()-based paths, e.g. transient-local replay, keep working with move-only storage).do_transient_local_publishto correctly convert between shared/unique storage and shared/unique subscription requests viastd::visitinstead of assuming the two always match.Move IntraProcessBufferData to its own header— pulls the new struct out intointra_process_buffer_data.hppto keepintra_process_buffer.hppfocused and avoid a circular-ish coupling between buffer and data definitions.Update intra-process unit tests— updatestest_intra_process_buffer.cppandtest_intra_process_manager.cppfor the newData/IntraProcessBufferDataAPI and adds coverage for message-info propagation across both shared- and unique-ownership subscriptions.Compatibility
Internal to
rclcpp's intra-process implementation (rclcpp::experimental::*); no public API signature changes. Behavior change: intra-process subscribers now observe non-defaultmessage_infowhere they previously saw an empty one.Testing
Built and tested against
rollingin aros:rolling-ros-basecontainer viacolcon build/colcon testforrclcpp(3091 tests, 0 failures), plusuncrustify/cpplintlint targets. All green.Landing order
intra-process-reduce-copies(opened separately) touches the same files (intra_process_buffer.hpp,intra_process_manager.hpp, and both intra-process test files) as part of an unrelated copy-reduction optimization. The two branches are independently buildable and testable, but not free to merge in either order without a rebase — real textual conflicts exist between them (confirmed via a trial merge). No functional dependency either way; whichever of the two merges second will need to reconcile against the other.🤖 Generated with Claude Code