sched/timer: Fix timer accuracy problems

This commit fixed timer accuracy problems when repetitive timer is set.

Signed-off-by: ouyangxiangzhen <ouyangxiangzhen@xiaomi.com>
This commit is contained in:
ouyangxiangzhen 2024-06-18 20:25:50 +08:00 committed by Xiang Xiao
parent 756faf3cc9
commit a483c884ce
4 changed files with 43 additions and 32 deletions

View file

@ -58,7 +58,9 @@ struct posix_timer_s
uint8_t pt_flags; /* See PT_FLAGS_* definitions */ uint8_t pt_flags; /* See PT_FLAGS_* definitions */
uint8_t pt_crefs; /* Reference count */ uint8_t pt_crefs; /* Reference count */
pid_t pt_owner; /* Creator of timer */ pid_t pt_owner; /* Creator of timer */
int pt_delay; /* If non-zero, used to reset repetitive timers */ int pt_overrun; /* Overrun time */
sclock_t pt_delay; /* If non-zero, used to reset repetitive timers */
clock_t pt_expected; /* Expected absolute time */
struct wdog_s pt_wdog; /* The watchdog that provides the timing */ struct wdog_s pt_wdog; /* The watchdog that provides the timing */
struct sigevent pt_event; /* Notification information */ struct sigevent pt_event; /* Notification information */
struct sigwork_s pt_work; struct sigwork_s pt_work;

View file

@ -183,6 +183,7 @@ int timer_create(clockid_t clockid, FAR struct sigevent *evp,
ret->pt_crefs = 1; ret->pt_crefs = 1;
ret->pt_owner = nxsched_getpid(); ret->pt_owner = nxsched_getpid();
ret->pt_delay = 0; ret->pt_delay = 0;
ret->pt_expected = 0;
/* Was a struct sigevent provided? */ /* Was a struct sigevent provided? */

View file

@ -77,9 +77,18 @@
int timer_getoverrun(timer_t timerid) int timer_getoverrun(timer_t timerid)
{ {
UNUSED(timerid); FAR struct posix_timer_s *timer = timer_gethandle(timerid);
set_errno(EINVAL); int ret;
return ERROR;
if (!timer)
{
set_errno(EINVAL);
return ERROR;
}
ret = timer->pt_overrun;
return ret > DELAYTIMER_MAX ? DELAYTIMER_MAX : ret;
} }
#endif /* CONFIG_DISABLE_POSIX_TIMERS */ #endif /* CONFIG_DISABLE_POSIX_TIMERS */

View file

@ -96,11 +96,32 @@ static inline void timer_signotify(FAR struct posix_timer_s *timer)
static inline void timer_restart(FAR struct posix_timer_s *timer, static inline void timer_restart(FAR struct posix_timer_s *timer,
wdparm_t itimer) wdparm_t itimer)
{ {
clock_t ticks;
sclock_t delay;
/* If this is a repetitive timer, then restart the watchdog */ /* If this is a repetitive timer, then restart the watchdog */
if (timer->pt_delay) if (timer->pt_delay)
{ {
wd_start(&timer->pt_wdog, timer->pt_delay, timer_timeout, itimer); /* Check whether next expected time is reached */
ticks = clock_systime_ticks();
timer->pt_overrun = 0;
for (; ; )
{
timer->pt_expected += timer->pt_delay;
delay = timer->pt_expected - ticks;
if (delay > 0)
{
break;
}
timer->pt_overrun++;
}
wd_start_absolute(&timer->pt_wdog, timer->pt_expected,
timer_timeout, itimer);
} }
} }
@ -224,7 +245,6 @@ int timer_settime(timer_t timerid, int flags,
FAR struct itimerspec *ovalue) FAR struct itimerspec *ovalue)
{ {
FAR struct posix_timer_s *timer = timer_gethandle(timerid); FAR struct posix_timer_s *timer = timer_gethandle(timerid);
irqstate_t intflags;
sclock_t delay; sclock_t delay;
int ret = OK; int ret = OK;
@ -272,29 +292,20 @@ int timer_settime(timer_t timerid, int flags,
if (value->it_interval.tv_sec > 0 || value->it_interval.tv_nsec > 0) if (value->it_interval.tv_sec > 0 || value->it_interval.tv_nsec > 0)
{ {
delay = clock_time2ticks(&value->it_interval); delay = clock_time2ticks(&value->it_interval);
timer->pt_delay = delay;
/* REVISIT: Should pt_delay be sclock_t? */
timer->pt_delay = (int)delay;
} }
else else
{ {
timer->pt_delay = 0; timer->pt_delay = 0;
} }
/* We need to disable timer interrupts through the following section so
* that the system timer is stable.
*/
intflags = enter_critical_section();
/* Check if abstime is selected */ /* Check if abstime is selected */
if ((flags & TIMER_ABSTIME) != 0) if ((flags & TIMER_ABSTIME) != 0)
{ {
/* Calculate a delay corresponding to the absolute time in 'value' */ /* Calculate a delay corresponding to the absolute time in 'value' */
clock_abstime2ticks(timer->pt_clock, &value->it_value, &delay); timer->pt_expected = clock_time2ticks(&value->it_value);
} }
else else
{ {
@ -304,25 +315,13 @@ int timer_settime(timer_t timerid, int flags,
*/ */
delay = clock_time2ticks(&value->it_value); delay = clock_time2ticks(&value->it_value);
} timer->pt_expected = clock_systime_ticks() + delay;
/* If the specified time has already passed, the function shall succeed
* and the expiration notification shall be made.
*/
if (delay < 0)
{
delay = 0;
} }
/* Then start the watchdog */ /* Then start the watchdog */
if (delay >= 0) ret = wd_start_absolute(&timer->pt_wdog, timer->pt_expected,
{ timer_timeout, (wdparm_t)timer);
ret = wd_start(&timer->pt_wdog, delay, timer_timeout, (wdparm_t)timer);
}
leave_critical_section(intflags);
if (ret < 0) if (ret < 0)
{ {