Skip to content

fix(rmw_wait): keep the wait timeout's sub-millisecond resolution - #83

Merged
benaliabderrahmane merged 2 commits into
develfrom
fix/rmw-wait-ns-timeout
Oct 2, 2026
Merged

benaliabderrahmane merged 2 commits into
develfrom
fix/rmw-wait-ns-timeout

Conversation

@benaliabderrahmane

Copy link
Copy Markdown
Owner

Description

rmw_wait rounded the caller's timeout to whole milliseconds for epoll_wait, so short waits ended late or early:

  • a timeout under 1 ms became 1 ms, so a 1 kHz timer woke up late and rclcpp skipped about 6 % of its periods;
  • the deadline came from the truncated value, so a 1.7 ms wait returned RMW_RET_TIMEOUT after 1 ms;
  • every re-block rounded the remaining time up again.

Fix: keep the timeout and the deadline in nanoseconds and block with epoll_pwait2(), which takes a timespec. A small epoll_wait_ns() helper falls back to epoll_wait() with the remaining time rounded up where epoll_pwait2() isn't available: glibc < 2.35 at build time, ENOSYS (kernel < 5.11) or EPERM (seccomp) at run time. The infinite-timeout handling from #8 and the 200 ms cap are unchanged. DESIGN.md (wait set, step 4) and test/README.md are updated to match.

Fixes #82

Is this user-facing behavior change?

Timing only, no API change. A wait now lasts as long as the caller asked, including below 1 ms. A 1 kHz create_wall_timer runs 29 999 of 30 000 periods in 30 s instead of 28 174.

How was this tested?

  • New tests in test_rmw_wait.cpp (they fail on devel and pass with the fix):
    • WaitKeepsSubMillisecondTimeoutResolution: 20 waits each of 0.3 ms and 1.7 ms on a guard condition that never fires. On devel, every 1.7 ms wait times out after ~1.06 ms, and the shortest 0.3 ms wait takes 1.057 ms.
    • OneKilohertzWaitLoopStaysOnSchedule: a 1 kHz loop of deadline-based rmw_wait calls over 500 periods, which is what an executor does for a 1 kHz timer. On devel, 264 of 500 periods wake more than 0.5 ms late (the limit is 25).
    • Both pass 10/10 with --gtest_repeat=10.
  • Full suite (Jazzy): 15/15 binaries pass with ctest -j1 and with ctest -j4, with no [ FAILED ] lines.
  • Fallback: with epoll_pwait2 blocked by a seccomp profile (EPERM), the other 14 binaries and the 14 existing test_rmw_wait tests pass. Only the two new resolution tests fail, as expected at millisecond resolution, and no wait returns before its deadline.
  • End to end (Jazzy, rclcpp, separate processes): a 1 kHz request/reply loop lost 2–7 % of its replies on devel and none with the fix. The iRobot cli_srv 10 B benchmark completes 40 000 of 40 000 calls instead of 35 310.

Did you use Generative AI?

Yes. Claude Code (Claude Opus 5.5) found the bug while benchmarking, wrote the fix and the tests, and drafted this description.

Additional Information

The two new tests check the resolution itself, so they would fail on a host where the fallback is active (kernel < 5.11, or a container runtime that blocks epoll_pwait2).

🤖 Generated with Claude Code

https://claude.ai/code/session_01Jh5PFqFmFoZnaSX5FfVv4G

rmw_wait converted the caller's timeout to whole milliseconds for
epoll_wait: a timeout under 1 ms became 1 ms, the deadline was computed
from the truncated value, and every re-block rounded the remaining time
up. rcl passes the time to the next timer in nanoseconds, so a 1 kHz
timer woke up to a whole period late and rclcpp skipped about 6 % of its
periods, and a 1.7 ms wait returned RMW_RET_TIMEOUT after 1 ms.

Keep the timeout and the deadline in nanoseconds and block with
epoll_pwait2. Where it is unavailable (glibc < 2.35 at build time, ENOSYS
or EPERM at run time) fall back to epoll_wait with the remaining time
rounded up, so the fallback never returns before the deadline either.

Test: WaitKeepsSubMillisecondTimeoutResolution and
OneKilohertzWaitLoopStaysOnSchedule fail on devel (1.7 ms waits time out
after ~1.06 ms; 264 of 500 periods wake more than 0.5 ms late) and pass
with the fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jh5PFqFmFoZnaSX5FfVv4G
@benaliabderrahmane benaliabderrahmane added bug Something isn't working P1 Next labels Oct 2, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jh5PFqFmFoZnaSX5FfVv4G
@benaliabderrahmane benaliabderrahmane self-assigned this Oct 2, 2026
@benaliabderrahmane
benaliabderrahmane deleted the fix/rmw-wait-ns-timeout branch October 2, 2026 10:36
@benaliabderrahmane
benaliabderrahmane restored the fix/rmw-wait-ns-timeout branch October 2, 2026 12:36
@benaliabderrahmane
benaliabderrahmane merged commit 3ca379b into devel Oct 2, 2026
8 checks passed
@benaliabderrahmane
benaliabderrahmane deleted the fix/rmw-wait-ns-timeout branch October 2, 2026 12:39
benaliabderrahmane added a commit that referenced this pull request Oct 2, 2026
The listener-thread docs were written before #83, which made rmw_wait
block in epoll_pwait2 (falling back to epoll_wait). Say so in the three
places that describe the wait's own blocking call; the listener thread
itself still blocks in epoll_wait with no timeout, so that sentence stays.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jh5PFqFmFoZnaSX5FfVv4G
@romainreignier

Copy link
Copy Markdown
Collaborator

Do you think that this also fix the rclrs timers issue?

@benaliabderrahmane

Copy link
Copy Markdown
Owner Author

Do you think that this also fix the rclrs timers issue?

@romainreignier yes it should be fixed it but I didn't do the test yet with rclrs, the dev was done because on high frequency timers (eg, +1KHz) I was missing some of the callbacks.
(PS: the current devel won't work with some ros2 packages like rosbag2 and image_transport because I flag the ros2events as not supported 7e39aa3 it was done intentionally prepping the events support in the PRs #64 #65 #66 #67 #68 #69 but I didn't have the time yet to review and test the code)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working P1 Next

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants