Skip to content

Commit f66ccdb

Browse files
committed
fix: get muldiv to be private and handle overflow properly, do freq checks
- Move rt_muldiv_u64 out of rttypes into private clock_time_internal with truncated overflow and an optional overflow out-parameter - Name the helper rt_clock_time_muldiv_u64 and include its .c from clock_time_core.c so IDE projects do not need a new source entry - Add rt_clock_time_get_res() and reject zero-frequency paths in clock_getres, timer_gettime, timer_settime, and soft RTC - Clamp or error on overflowed/out-of-range delay conversions in hrtimer and POSIX timer reload paths - Keep one-shot timer intervals from being rounded up to a 1-tick reload, and clamp timer_gettime remain time after expiry - Update clock_time core documentation for resolution and overflow
1 parent 8cdde43 commit f66ccdb

11 files changed

Lines changed: 335 additions & 56 deletions

File tree

components/drivers/clock_time/clock_boottime.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
#include <drivers/clock_time.h>
1212
#include <sys/time.h>
1313

14+
#include "clock_time_internal.h"
15+
1416
rt_err_t rt_clock_boottime_get_us(struct timeval *tv)
1517
{
1618
rt_uint64_t cnt;
@@ -26,7 +28,7 @@ rt_err_t rt_clock_boottime_get_us(struct timeval *tv)
2628
}
2729

2830
tv->tv_sec = (time_t)(cnt / freq);
29-
tv->tv_usec = rt_muldiv_u64(cnt % freq, MICROSECOND_PER_SECOND, freq, NULL);
31+
tv->tv_usec = rt_clock_time_muldiv_u64(cnt % freq, MICROSECOND_PER_SECOND, freq, RT_NULL, RT_NULL);
3032

3133
return RT_EOK;
3234
}
@@ -65,7 +67,7 @@ rt_err_t rt_clock_boottime_get_ns(struct timespec *ts)
6567
}
6668

6769
ts->tv_sec = (time_t)(cnt / freq);
68-
ts->tv_nsec = rt_muldiv_u64(cnt % freq, NANOSECOND_PER_SECOND, freq, NULL);
70+
ts->tv_nsec = rt_clock_time_muldiv_u64(cnt % freq, NANOSECOND_PER_SECOND, freq, RT_NULL, RT_NULL);
6971

7072
return RT_EOK;
7173
}

components/drivers/clock_time/clock_hrtimer.c

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <sys/time.h>
1515

1616
#include <drivers/clock_time.h>
17+
#include "clock_time_internal.h"
1718

1819
#define DBG_SECTION_NAME "drv.clock_time"
1920
#define DBG_LEVEL DBG_INFO
@@ -52,17 +53,27 @@ static rt_tick_t _hrtimer_cnt_to_tick(rt_uint64_t cnt)
5253
rt_uint64_t freq = rt_clock_hrtimer_getfrq();
5354
rt_uint64_t rem = 0;
5455
rt_uint64_t tick;
56+
rt_bool_t overflow = RT_FALSE;
5557

5658
if (freq == 0)
5759
{
5860
return 0;
5961
}
6062

61-
tick = rt_muldiv_u64(cnt, RT_TICK_PER_SECOND, freq, &rem);
63+
tick = rt_clock_time_muldiv_u64(cnt, RT_TICK_PER_SECOND, freq, &rem, &overflow);
64+
if (overflow)
65+
{
66+
/* soft-timer path: clamp overflowed delay into the legal half-range */
67+
return (rt_tick_t)(RT_TICK_MAX / 2 - 1);
68+
}
6269
if (rem != 0)
6370
{
6471
tick += 1; /* round up so a non-zero cnt never collapses to a 0-tick timeout */
6572
}
73+
if (tick >= (RT_TICK_MAX / 2))
74+
{
75+
tick = RT_TICK_MAX / 2 - 1;
76+
}
6677
if (tick == 0)
6778
{
6879
tick = 1; /* at least one tick */
@@ -115,13 +126,30 @@ static rt_uint64_t _cnt_convert(rt_tick_t cnt)
115126
{
116127
rt_uint64_t rtn = 0;
117128
rt_tick_t count = cnt - _clock_time_get_cnt();
129+
rt_uint64_t src_freq;
130+
rt_uint64_t event_freq;
131+
rt_bool_t overflow = RT_FALSE;
118132

119133
if (count > (RT_TICK_MAX / 2))
120134
{
121135
return 0;
122136
}
123137

124-
rtn = rt_muldiv_u64(count, rt_clock_hrtimer_getfrq(), rt_clock_time_get_freq(), NULL);
138+
src_freq = rt_clock_time_get_freq();
139+
event_freq = rt_clock_hrtimer_getfrq();
140+
if ((src_freq == 0) || (event_freq == 0))
141+
{
142+
/* frequency unavailable: never return 0, or the caller treats the
143+
* timer as already expired and may busy-loop reprocessing it */
144+
return 1;
145+
}
146+
147+
rtn = rt_clock_time_muldiv_u64(count, event_freq, src_freq, RT_NULL, &overflow);
148+
if (overflow)
149+
{
150+
/* keep a long delay; truncated overflow could schedule too soon */
151+
return RT_UINT64_MAX;
152+
}
125153
return rtn == 0 ? 1 : rtn;
126154
}
127155

@@ -408,18 +436,33 @@ rt_err_t rt_clock_hrtimer_sleep(struct rt_clock_hrtimer *timer, rt_tick_t cnt)
408436

409437
rt_err_t rt_clock_hrtimer_ndelay(struct rt_clock_hrtimer *timer, rt_uint64_t ns)
410438
{
411-
rt_tick_t cputimer_tick = (rt_tick_t)rt_muldiv_u64(ns, rt_clock_time_get_freq(), NANOSECOND_PER_SECOND, NULL);
439+
rt_uint64_t count = rt_clock_time_ns_to_counter(ns);
412440

413-
return rt_clock_hrtimer_sleep(timer, cputimer_tick);
441+
if ((count == 0) || (count >= (RT_TICK_MAX / 2)))
442+
{
443+
return -RT_EINVAL;
444+
}
445+
446+
return rt_clock_hrtimer_sleep(timer, (rt_tick_t)count);
414447
}
415448

416449
rt_err_t rt_clock_hrtimer_udelay(struct rt_clock_hrtimer *timer, rt_uint64_t us)
417450
{
451+
if (us > (RT_UINT64_MAX / 1000))
452+
{
453+
return -RT_EINVAL;
454+
}
455+
418456
return rt_clock_hrtimer_ndelay(timer, us * 1000);
419457
}
420458

421459
rt_err_t rt_clock_hrtimer_mdelay(struct rt_clock_hrtimer *timer, rt_uint64_t ms)
422460
{
461+
if (ms > (RT_UINT64_MAX / 1000000))
462+
{
463+
return -RT_EINVAL;
464+
}
465+
423466
return rt_clock_hrtimer_ndelay(timer, ms * 1000000);
424467
}
425468

components/drivers/clock_time/clock_time_core.c

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,14 @@
1313
#include <rtdevice.h>
1414

1515
#include <drivers/clock_time.h>
16+
#include "clock_time_internal.h"
17+
/*
18+
* The muldiv implementation is textually included so it compiles as part of
19+
* this translation unit. Keil/IAR project files list clock_time sources one
20+
* by one; including the .c here keeps clock_time_internal.c out of build
21+
* scripts and IDE projects while producing a single definition.
22+
*/
23+
#include "clock_time_internal.c"
1624

1725
static rt_uint64_t _clock_time_tick_get_freq(struct rt_clock_time_device *dev)
1826
{
@@ -150,28 +158,55 @@ rt_uint64_t rt_clock_time_get_event_freq(void)
150158
return event->ops->get_freq(event);
151159
}
152160

161+
/**
162+
* @brief Get the default clock source resolution in nanoseconds.
163+
*
164+
* @return Non-zero resolution on success, or 0 when frequency is unavailable.
165+
*/
166+
rt_uint64_t rt_clock_time_get_res(void)
167+
{
168+
rt_uint64_t freq = rt_clock_time_get_freq();
169+
rt_uint64_t res;
170+
171+
if (freq == 0)
172+
{
173+
return 0;
174+
}
175+
176+
res = NANOSECOND_PER_SECOND / freq;
177+
return res == 0 ? 1 : res;
178+
}
179+
153180
rt_uint64_t rt_clock_time_counter_to_ns(rt_uint64_t cnt)
154181
{
155182
rt_uint64_t freq = rt_clock_time_get_freq();
183+
rt_bool_t overflow = RT_FALSE;
184+
rt_uint64_t ns;
156185

157186
if (freq == 0)
158187
{
159188
return 0;
160189
}
161190

162-
return rt_muldiv_u64(cnt, NANOSECOND_PER_SECOND, freq, NULL);
191+
ns = rt_clock_time_muldiv_u64(cnt, NANOSECOND_PER_SECOND, freq, RT_NULL, &overflow);
192+
/* Call-site policy: report unrepresentable results as the max value. */
193+
return overflow ? RT_UINT64_MAX : ns;
163194
}
164195

165196
rt_uint64_t rt_clock_time_ns_to_counter(rt_uint64_t ns)
166197
{
167198
rt_uint64_t freq = rt_clock_time_get_freq();
199+
rt_bool_t overflow = RT_FALSE;
200+
rt_uint64_t cnt;
168201

169202
if (freq == 0)
170203
{
171204
return 0;
172205
}
173206

174-
return rt_muldiv_u64(ns, freq, NANOSECOND_PER_SECOND, NULL);
207+
cnt = rt_clock_time_muldiv_u64(ns, freq, NANOSECOND_PER_SECOND, RT_NULL, &overflow);
208+
/* Call-site policy: report unrepresentable delays as the max value. */
209+
return overflow ? RT_UINT64_MAX : cnt;
175210
}
176211

177212
rt_err_t rt_clock_time_set_timeout(rt_uint64_t delta)

src/rttypes.c renamed to components/drivers/clock_time/clock_time_internal.c

Lines changed: 84 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,24 @@
11
/*
2-
* Copyright (c) 2006-2025, RT-Thread Development Team
2+
* Copyright (c) 2006-2026, RT-Thread Development Team
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*
66
* Change Logs:
77
* Date Author Notes
88
* 2025-09-25 Yonggang Luo the first version
9+
* 2026-07-14 Yonggang Luo keep muldiv private to clock_time
910
*/
1011

11-
#include "rttypes.h"
12-
#include <rtdef.h>
12+
#include "clock_time_internal.h"
1313

14-
/* Function to count leading zeros for an rt_uint64_t */
15-
static rt_int32_t u64_count_leading_zeros(rt_uint64_t n)
14+
/**
15+
* @brief Count leading zero bits in a 64-bit value.
16+
*
17+
* @param n Input value.
18+
*
19+
* @return Number of leading zero bits; 64 when @p n is 0.
20+
*/
21+
static rt_int32_t _u64_count_leading_zeros(rt_uint64_t n)
1622
{
1723
if (n == 0)
1824
{
@@ -22,8 +28,7 @@ static rt_int32_t u64_count_leading_zeros(rt_uint64_t n)
2228
return __builtin_clzll(n);
2329
#else
2430
rt_int32_t count = 0;
25-
rt_uint64_t mask =
26-
1ULL << (sizeof(rt_uint64_t) * 8 - 1); // Most significant bit
31+
rt_uint64_t mask = 1ULL << (sizeof(rt_uint64_t) * 8 - 1);
2732

2833
while ((n & mask) == 0)
2934
{
@@ -35,19 +40,29 @@ static rt_int32_t u64_count_leading_zeros(rt_uint64_t n)
3540
}
3641

3742
/**
43+
* @brief Narrowing division of a 128-bit numerator by a 64-bit denominator.
44+
*
45+
* Algorithm from:
3846
* https://ridiculousfish.com/blog/posts/labor-of-division-episode-v.html
39-
* Perform a narrowing division: 128 / 64 -> 64, and 64 / 32 -> 32.
40-
* The dividend's low and high words are given by \p numhi and \p numlo,
41-
* respectively. The divisor is given by \p den.
42-
* \return the quotient, and the remainder by reference in \p r, if not null.
43-
* If the quotient would require more than 64 bits, or if denom is 0, then
44-
* return the max value for both quotient and remainder.
4547
*
46-
* These functions are released into the public domain, where applicable, or the
47-
* CC0 license.
48+
* The dividend is formed by high word @p numhi and low word @p numlo.
49+
* When @p den is 0, both the quotient and the optional remainder are 0 and
50+
* @p overflow is set. When the full quotient needs more than 64 bits,
51+
* @c numhi %= den truncates to a 64-bit quotient and @p overflow is set.
52+
*
53+
* These functions are released into the public domain, where applicable, or
54+
* the CC0 license.
55+
*
56+
* @param numhi High 64 bits of the numerator.
57+
* @param numlo Low 64 bits of the numerator.
58+
* @param den Denominator.
59+
* @param r Optional remainder output; may be @c RT_NULL.
60+
* @param overflow Optional overflow flag; may be @c RT_NULL.
61+
*
62+
* @return The truncated 64-bit quotient.
4863
*/
49-
static rt_uint64_t u128_div_u64_u64(rt_uint64_t numhi, rt_uint64_t numlo, rt_uint64_t den,
50-
rt_uint64_t *r)
64+
static rt_uint64_t _u128_div_u64_u64(rt_uint64_t numhi, rt_uint64_t numlo, rt_uint64_t den,
65+
rt_uint64_t *r, rt_bool_t *overflow)
5166
{
5267
/*
5368
* We work in base 2**32.
@@ -84,8 +99,34 @@ static rt_uint64_t u128_div_u64_u64(rt_uint64_t numhi, rt_uint64_t numlo, rt_uin
8499
rt_uint64_t c1;
85100
rt_uint64_t c2;
86101

87-
/* Check for overflow and divide by 0. */
88-
numhi = numhi % den;
102+
if (overflow != RT_NULL)
103+
{
104+
*overflow = RT_FALSE;
105+
}
106+
107+
/* Divide by 0: treat as a zero result. */
108+
if (den == 0)
109+
{
110+
if (r != RT_NULL)
111+
{
112+
*r = 0;
113+
}
114+
if (overflow != RT_NULL)
115+
{
116+
*overflow = RT_TRUE;
117+
}
118+
return 0;
119+
}
120+
121+
/* Truncate a wider-than-64-bit quotient to 64 bits. */
122+
if (numhi >= den)
123+
{
124+
if (overflow != RT_NULL)
125+
{
126+
*overflow = RT_TRUE;
127+
}
128+
numhi = numhi % den;
129+
}
89130

90131
/*
91132
* Determine the normalization factor. We multiply den by this, so that its leading digit is at
@@ -96,7 +137,7 @@ static rt_uint64_t u128_div_u64_u64(rt_uint64_t numhi, rt_uint64_t numlo, rt_uin
96137
* by 64. The funny bitwise 'and' ensures that numlo does not get shifted into numhi if shift is 0.
97138
* clang 11 has an x86 codegen bug here: see LLVM bug 50118. The sequence below avoids it.
98139
*/
99-
shift = u64_count_leading_zeros(den);
140+
shift = _u64_count_leading_zeros(den);
100141
den <<= shift;
101142
numhi <<= shift;
102143
numhi |= (numlo >> (-shift & 63)) & (-(rt_int64_t)shift >> 63);
@@ -137,16 +178,30 @@ static rt_uint64_t u128_div_u64_u64(rt_uint64_t numhi, rt_uint64_t numlo, rt_uin
137178
q0 = (rt_uint32_t)qhat;
138179

139180
/* Return remainder if requested. */
140-
if (r != NULL)
181+
if (r != RT_NULL)
141182
*r = (rem * b + num0 - q0 * den) >> shift;
142183
return ((rt_uint64_t)q1 << 32) | q0;
143184
}
144185

145-
rt_uint64_t rt_muldiv_u64(rt_uint64_t a, rt_uint64_t b, rt_uint64_t c, rt_uint64_t *r)
186+
rt_uint64_t rt_clock_time_muldiv_u64(rt_uint64_t a, rt_uint64_t b, rt_uint64_t c,
187+
rt_uint64_t *r, rt_bool_t *overflow)
146188
{
147189
rt_uint64_t remainder = 0;
148-
rt_uint64_t ret = 0;
149-
if (c != 0) /* Handle division by zero. */
190+
rt_uint64_t ret;
191+
192+
if (c == 0)
193+
{
194+
if (r != RT_NULL)
195+
{
196+
*r = 0;
197+
}
198+
if (overflow != RT_NULL)
199+
{
200+
*overflow = RT_TRUE;
201+
}
202+
return 0;
203+
}
204+
150205
{
151206
rt_uint64_t a_lo = a & 0xFFFFFFFF;
152207
rt_uint64_t a_hi = a >> 32;
@@ -163,9 +218,12 @@ rt_uint64_t rt_muldiv_u64(rt_uint64_t a, rt_uint64_t b, rt_uint64_t c, rt_uint64
163218

164219
rt_uint64_t lo = (p0 & 0xFFFFFFFFULL) + ((carry & 0xFFFFFFFFULL) << 32);
165220
rt_uint64_t hi = p3 + (p1 >> 32) + (p2 >> 32) + (carry >> 32);
166-
ret = u128_div_u64_u64(hi, lo, c, &remainder);
221+
ret = _u128_div_u64_u64(hi, lo, c, &remainder, overflow);
167222
}
168-
if (r)
223+
224+
if (r != RT_NULL)
225+
{
169226
*r = remainder;
227+
}
170228
return ret;
171229
}

0 commit comments

Comments
 (0)