Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Don't truncate rmw_time_t QoS durations in the override defaults - #3268

Open
Dev-next-gen wants to merge 2 commits into
ros2:rollingfrom
Dev-next-gen:fix/qos-override-default-rmw-duration-narrowing
Open

Don't truncate rmw_time_t QoS durations in the override defaults#3268
Dev-next-gen wants to merge 2 commits into
ros2:rollingfrom
Dev-next-gen:fix/qos-override-default-rmw-duration-narrowing

Conversation

@Dev-next-gen

@Dev-next-gen Dev-next-gen commented Sep 11, 2026

Copy link
Copy Markdown

Description

rclcpp::detail::rmw_duration_to_int64_t() converts an rmw_time_t by hand:

return ::rclcpp::Duration(
  static_cast<int32_t>(rmw_duration.sec),
  static_cast<uint32_t>(rmw_duration.nsec)
).nanoseconds();

rmw_time_t::sec is a uint64_t, and the durations that matter here do not fit in an int32_t. RMW_DURATION_INFINITE is {9223372036, 854775807}, and RMW_QOS_DEADLINE_BEST_AVAILABLE / RMW_QOS_LIVELINESS_LEASE_DURATION_BEST_AVAILABLE are {9223372036, 854775806}. Casting 9223372036 to int32_t wraps to 633437444, so the helper returns 633437444854775807 ns where it should return 9223372036854775807 ns.

declare_qos_parameters() uses that value twice: as the default for the deadline, lifespan and liveliness_lease_duration override parameters, and then it feeds the declared value back into the profile through apply_qos_override(). So enabling those overrides on a profile carrying an infinite duration silently turns it into a finite one of about 20 years, which is a different thing for QoS compatibility matching — a subscription requesting a 20 year deadline no longer matches a publisher offering an infinite one. Seconds in [2^31, 2^32) are worse: they wrap negative, produce a negative Duration, and QoS::deadline() throws rmw_time_t cannot be negative.

rclcpp::Duration::from_rmw_time() already does this conversion correctly — it saturates at INT64_MAX instead of wrapping — and it is what the QoS::deadline(), QoS::lifespan() and QoS::liveliness_lease_duration() getters use. This PR makes the helper use it too.

For every rmw_time_t whose seconds fit in an int32_t the two expressions produce the same number, so nothing changes for ordinary durations. The function is inline and lives in rclcpp::detail, so no public signature moves.

Fixes # (no issue filed; found while reading the QoS override code)

Is this user-facing behavior change?

Yes, for one case that is currently broken. A QoS profile with an infinite deadline, lifespan or liveliness_lease_duration keeps that infinite value when the corresponding parameter override is declared, instead of being silently reduced to ~292 years... in practice 633437444854775807 ns, about 20 years. Profiles whose durations already fit in an int32_t number of seconds are unchanged.

Did you use Generative AI?

Yes. Claude Opus 5, through Claude Code, found the truncation, wrote the one-line change in qos_parameters.hpp, wrote the declare_infinite_durations test in test_qos_parameters.cpp, and drafted this description. The build and test verification below was run afterwards and is reported exactly as the container printed it.

Additional Information

Verification. My earlier note said I could not build the full ROS 2 stack. That is now done, in the official ros:rolling-ros-base image with the test dependencies installed by rosdep. Environment: Ubuntu 26.04.1, ROS 2 Rolling, gcc 15.2.0, cmake 4.2.3. Base: e47c084 on rolling.

With this change applied, colcon build --packages-select rclcpp succeeds and the test file passes:

build : OK
test_qos_parameters.gtest.xml : total=8 failures=0 errors=0
  declare_infinite_durations                   passed

Then I reverted only rclcpp/include/rclcpp/detail/qos_parameters.hpp, keeping the new test, rebuilt, and it fails on current rolling:

build : OK
test_qos_parameters.gtest.xml : total=8 failures=1 errors=0
  declare_infinite_durations                   FAILED
    test_qos_parameters.cpp:218
    Expected equality of these values:
      infinite_ns                              Which is: 9223372036854775807
      rclcpp::detail::get_default_qos_param_value(
        rclcpp::QosPolicyKind::Deadline, qos).get<int64_t>()
                                               Which is: 633437444854775807

That is the truncated value predicted above, produced by the code as it stands today. The other seven tests in the file pass on both sides.

rclcpp::detail::rmw_duration_to_int64_t built an rclcpp::Duration from
static_cast<int32_t>(rmw_duration.sec). RMW_DURATION_INFINITE, and the
best-available deadline and liveliness lease sentinels, all carry
9223372036 seconds, so the cast wrapped and the helper returned
633437444854775807 ns instead of 9223372036854775807 ns.

That value is what declare_qos_parameters() declares as the default for
the deadline, lifespan and liveliness_lease_duration overrides, and it is
fed straight back into the profile through apply_qos_override(). Enabling
those overrides on a profile carrying an infinite duration therefore
silently replaced it with a finite ~20 year duration, which changes QoS
compatibility matching. Seconds values in [2^31, 2^32) wrap to a negative
int32_t and produce a negative duration, which QoS::deadline() then
rejects with "rmw_time_t cannot be negative".

Use rclcpp::Duration::from_rmw_time(), which is the conversion the QoS
getters already use and which saturates instead of wrapping.

Signed-off-by: leoca <[email protected]>

@fujitatomoya fujitatomoya left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could not build the full ROS 2 stack here

why? can you take a look at https://docs.ros.org/en/rolling/Get-Started/Installation/Alternatives/Ubuntu-Development-Setup.html

IMO this looks good, but we are not going to approve that is NOT verified, we are not sure if this can be built without error.

Comment thread rclcpp/test/rclcpp/test_qos_parameters.cpp Outdated
@fujitatomoya

Copy link
Copy Markdown
Collaborator

and please use our pull request template.

@Dev-next-gen

Copy link
Copy Markdown
Author

Thanks, that was a fair objection — I should not have opened it unverified.

I built and tested it. Official ros:rolling-ros-base image, test dependencies installed with rosdep, Ubuntu 26.04.1, gcc 15.2.0, cmake 4.2.3, base e47c084 on rolling.

With the change, colcon build --packages-select rclcpp succeeds and test_qos_parameters passes 8/8. I then reverted only qos_parameters.hpp, kept the new test, and rebuilt: it fails on current rolling with

Expected equality of these values:
  infinite_ns   Which is: 9223372036854775807
  get_default_qos_param_value(QosPolicyKind::Deadline, qos).get<int64_t>()
                Which is: 633437444854775807

which is the truncated value the description predicts. The other seven tests pass on both sides. Full output is in the updated description.

I also rewrote the description using the ros2 template, including the Generative AI section.

@Dev-next-gen
Dev-next-gen force-pushed the fix/qos-override-default-rmw-duration-narrowing branch from df79610 to ec6a61d Compare September 14, 2026 01:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants