Repository navigation
fix(rmw_wait): keep the wait timeout's sub-millisecond resolution - #83
Merged
Merged
Conversation
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
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jh5PFqFmFoZnaSX5FfVv4G
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
Collaborator
|
Do you think that this also fix the rclrs timers issue? |
Owner
Author
@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. |
This was referenced Oct 5, 2026
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.
Description
rmw_waitrounded the caller's timeout to whole milliseconds forepoll_wait, so short waits ended late or early:RMW_RET_TIMEOUTafter 1 ms;Fix: keep the timeout and the deadline in nanoseconds and block with
epoll_pwait2(), which takes atimespec. A smallepoll_wait_ns()helper falls back toepoll_wait()with the remaining time rounded up whereepoll_pwait2()isn't available: glibc < 2.35 at build time,ENOSYS(kernel < 5.11) orEPERM(seccomp) at run time. The infinite-timeout handling from #8 and the 200 ms cap are unchanged. DESIGN.md (wait set, step 4) andtest/README.mdare 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_timerruns 29 999 of 30 000 periods in 30 s instead of 28 174.How was this tested?
test_rmw_wait.cpp(they fail ondeveland pass with the fix):WaitKeepsSubMillisecondTimeoutResolution: 20 waits each of 0.3 ms and 1.7 ms on a guard condition that never fires. Ondevel, 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-basedrmw_waitcalls over 500 periods, which is what an executor does for a 1 kHz timer. Ondevel, 264 of 500 periods wake more than 0.5 ms late (the limit is 25).--gtest_repeat=10.ctest -j1and withctest -j4, with no[ FAILED ]lines.epoll_pwait2blocked by a seccomp profile (EPERM), the other 14 binaries and the 14 existingtest_rmw_waittests pass. Only the two new resolution tests fail, as expected at millisecond resolution, and no wait returns before its deadline.develand none with the fix. The iRobotcli_srv10 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