Repository navigation
Conversation
Both functions stored a 64-bit quantity in 32 bits, and the first one is far
more damaging than the second.
timer_settime():
unsigned long res = rt_clock_time_get_res_scaled();
timer->reload = (ns * RT_CLOCK_TIME_RESMUL) / res;
rt_clock_time_get_res_scaled() is (1e9 * res_scale) / freq, so it is 1e13 for a
100 Hz source - well above UINT32_MAX. Narrowing it to unsigned long picked up
the low 32 bits (1316134912 on qemu-vexpress-a9) and armed the timer for the
wrong duration: a 10 s timer got reload = 7598005 ticks, about 21 hours. The
reported remaining time was therefore wrong even before timer_gettime() had a
chance to truncate anything.
timer_gettime():
rt_uint32_t nanoseconds; /* holds the total, not just the sub-second part */
unsigned long remain_cnt;
nanoseconds = ((remain_cnt - rt_clock_time_get_counter()) * ...) / ...;
Two problems here: the total number of nanoseconds does not fit in rt_uint32_t
once the remainder exceeds about 4.29 s, and the 32-bit expiry count was
subtracted from the 64-bit hardware counter, which underflows into a huge value.
Fix both:
- convert in 64 bits in timer_settime(), and reject a zero resolution instead of
dividing by it
- subtract the counter from the 32-bit expiry count in 32-bit space, treating a
difference in the upper half as "already expired", the same way the hrtimer
core does in _cnt_convert()
- keep the total in 64 bits until it is split into seconds and nanoseconds
Measured on bsp/qemu-vexpress-a9 (100 Hz tick source, res_scaled = 1e13,
QEMU 11.0.0, arm-none-eabi-gcc 15.2.1). A timer is armed and timer_gettime() is
called immediately afterwards:
requested before after
1 s {0 s, 202853376 ns} {1 s, 0 ns}
3 s {0 s, 759022738 ns} {3 s, 0 ns}
5 s {1 s, 305192100 ns} {5 s, 0 ns}
10 s {2 s, 640384201 ns} {10 s, 0 ns}
60 s {7 s, ...} {60 s, 0 ns}
The new utest covers 1/3/5/10 s and fails on all four cases without this patch.
Refs RT-Thread#11924
|
👋 感谢您对 RT-Thread 的贡献!Thank you for your contribution to RT-Thread! 为确保代码符合 RT-Thread 的编码规范,请在你的仓库中执行以下步骤运行代码格式化工作流(如果格式化CI运行失败)。 🛠 操作步骤 | Steps
完成后,提交将自动更新至 如有问题欢迎联系我们,再次感谢您的贡献!💐 |
📌 Code Review Assignment🏷️ Tag: componentsReviewers: @Maihuanyi Changed Files (Click to expand)
🏷️ Tag: components_libcReviewers: @GorrayLi @mysterywolf Changed Files (Click to expand)
📊 Current Review Status (Last Updated: 2026-10-28 14:12 CST)
📝 Review Instructions
|
|
拉取/合并请求描述:(PR description)
[
为什么提交这份PR (why to submit this PR)
修复 #11924。issue 指出
timer_gettime()把总纳秒存进rt_uint32_t,这是真的;但进一步查发现它前面还有一个更严重的同类截断,只修 gettime 不会让结果变对。1.
timer_settime()把 64 位的分辨率截成 32 位(主要问题)rt_clock_time_get_res_scaled()的实现是(1e9 * res_scale) / freq(clock_time_core.c:78)。对 100 Hz 的时钟源它就是 1e13,远超UINT32_MAX。存进unsigned long只留下低 32 位(在 qemu-vexpress-a9 上是 1316134912),于是:定时器本身就被 arm 成了错的时长,这时候
timer_gettime()再准也没意义。2.
timer_gettime()的两处截断你的解决方案是什么 (what is your solution)
timer_settime():用 64 位做换算,并对res == 0返回EINVAL而不是拿它做除数timer_gettime():在 32 位空间里做减法(与 hrtimer 核心_cnt_convert()的做法一致),差值落在上半区视为已到期请提供验证的bsp和config (provide the config and bsp)
bsp/qemu-vexpress-a9(RT_TICK_PER_SECOND=100,res_scaled=1e13)RT_USING_POSIX_TIMER(默认已开)+ utest 相关开关1c801efa同一份固件、同一个探针,改前/改后各跑一次,
timer_settime()之后立刻timer_gettime():{0 s, 202853376 ns}{1 s, 0 ns}{0 s, 759022738 ns}{3 s, 0 ns}{1 s, 305192100 ns}{5 s, 0 ns}{2 s, 640384201 ns}{10 s, 0 ns}{60 s, 0 ns}新增 utest 覆盖 1/3/5/10 s,在不打补丁的 master 上四个用例全部失败,打上后全部通过。断言同时检查「上报值不超过请求值」与「不小于请求值减 200 ms」,因为时钟源是 100 Hz 的 tick,读数时已经过了一部分 tick 是正常的。
两点说明
timer->reload仍是unsigned long。修正 res 之后reload本身是 tick 数,10 秒在 100 Hz 下只有 1000,装得下;只有当单次定时超过UINT32_MAX / freq(100 Hz 下约 497 天)才会溢出,那是另一个层面的问题,本次没有一并处理。utest 的 Kconfig 依赖是
RT_USING_POSIX_TIMER,需要RT_USING_SIGNALS(用例用SIGEV_SIGNAL创建定时器)。如果 CI 的某个 BSP 没开信号,用例会创建失败——需要确认放在哪个 BSP/config 跑。]
当前拉取/合并请求的状态 Intent for your PR
必须选择一项 Choose one (Mandatory):
代码质量 Code Quality:
我在这个拉取/合并请求中已经考虑了 As part of this pull request, I've considered the following:
#if 0代码,不包含已经被注释了的代码 All redundant code is removed and cleaned up