From a483c884cec057b4bfa5e3ee78fb17224c3becbf Mon Sep 17 00:00:00 2001 From: ouyangxiangzhen Date: Tue, 18 Jun 2024 20:25:50 +0800 Subject: [PATCH] sched/timer: Fix timer accuracy problems This commit fixed timer accuracy problems when repetitive timer is set. Signed-off-by: ouyangxiangzhen --- sched/timer/timer.h | 4 ++- sched/timer/timer_create.c | 1 + sched/timer/timer_getoverrun.c | 15 ++++++++-- sched/timer/timer_settime.c | 55 +++++++++++++++++----------------- 4 files changed, 43 insertions(+), 32 deletions(-) diff --git a/sched/timer/timer.h b/sched/timer/timer.h index 49e9b7f990..f91bbff2b8 100644 --- a/sched/timer/timer.h +++ b/sched/timer/timer.h @@ -58,7 +58,9 @@ struct posix_timer_s uint8_t pt_flags; /* See PT_FLAGS_* definitions */ uint8_t pt_crefs; /* Reference count */ 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 sigevent pt_event; /* Notification information */ struct sigwork_s pt_work; diff --git a/sched/timer/timer_create.c b/sched/timer/timer_create.c index 7e31a1213e..1aa1fbe95b 100644 --- a/sched/timer/timer_create.c +++ b/sched/timer/timer_create.c @@ -183,6 +183,7 @@ int timer_create(clockid_t clockid, FAR struct sigevent *evp, ret->pt_crefs = 1; ret->pt_owner = nxsched_getpid(); ret->pt_delay = 0; + ret->pt_expected = 0; /* Was a struct sigevent provided? */ diff --git a/sched/timer/timer_getoverrun.c b/sched/timer/timer_getoverrun.c index 0ad2f98031..6fe13b20ac 100644 --- a/sched/timer/timer_getoverrun.c +++ b/sched/timer/timer_getoverrun.c @@ -77,9 +77,18 @@ int timer_getoverrun(timer_t timerid) { - UNUSED(timerid); - set_errno(EINVAL); - return ERROR; + FAR struct posix_timer_s *timer = timer_gethandle(timerid); + int ret; + + if (!timer) + { + set_errno(EINVAL); + return ERROR; + } + + ret = timer->pt_overrun; + + return ret > DELAYTIMER_MAX ? DELAYTIMER_MAX : ret; } #endif /* CONFIG_DISABLE_POSIX_TIMERS */ diff --git a/sched/timer/timer_settime.c b/sched/timer/timer_settime.c index b0a386bc67..866d8d58bd 100644 --- a/sched/timer/timer_settime.c +++ b/sched/timer/timer_settime.c @@ -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, wdparm_t itimer) { + clock_t ticks; + sclock_t delay; + /* If this is a repetitive timer, then restart the watchdog */ 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 posix_timer_s *timer = timer_gethandle(timerid); - irqstate_t intflags; sclock_t delay; 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) { delay = clock_time2ticks(&value->it_interval); - - /* REVISIT: Should pt_delay be sclock_t? */ - - timer->pt_delay = (int)delay; + timer->pt_delay = delay; } else { 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 */ if ((flags & TIMER_ABSTIME) != 0) { /* 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 { @@ -304,25 +315,13 @@ int timer_settime(timer_t timerid, int flags, */ delay = clock_time2ticks(&value->it_value); - } - - /* If the specified time has already passed, the function shall succeed - * and the expiration notification shall be made. - */ - - if (delay < 0) - { - delay = 0; + timer->pt_expected = clock_systime_ticks() + delay; } /* Then start the watchdog */ - if (delay >= 0) - { - ret = wd_start(&timer->pt_wdog, delay, timer_timeout, (wdparm_t)timer); - } - - leave_critical_section(intflags); + ret = wd_start_absolute(&timer->pt_wdog, timer->pt_expected, + timer_timeout, (wdparm_t)timer); if (ret < 0) {