Skip to content

Commit fff7036

Browse files
committed
Fix cumulative drift in periodic timers
Currently, periodic timers allow errors to accumulate because the next deadline is calculated relative to the current system tick (now + period). Any latency in the tick handler execution—whether due to interrupt jitter or system load—permanently shifts the phase of all future expirations. Fix the drift by anchoring the timer schedule to an absolute baseline. A new field, last_expected_fire_tick, is introduced to track the theoretical expiration time. When the timer fires, the next deadline is now computed by advancing this expected time by the period, rather than relying on the actual execution time. This ensures the timer maintains its long-term frequency accuracy regardless of short-term scheduling delays.
1 parent b4c2d10 commit fff7036

File tree

2 files changed

+10
-3
lines changed

2 files changed

+10
-3
lines changed

include/sys/timer.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@ typedef enum {
2626
typedef struct {
2727
/* Timing Parameters */
2828
uint32_t deadline_ticks; /* Expiration time in absolute system ticks */
29-
uint32_t period_ms; /* Reload period in milliseconds */
29+
uint32_t last_expected_fire_tick; /* Last calculated expected fire time for
30+
* periodic timer
31+
*/
32+
uint32_t period_ms; /* Reload period in milliseconds */
3033

3134
/* Timer Identification and State */
3235
uint16_t id; /* Unique handle assigned by the kernel */

kernel/timer.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,9 @@ void _timer_tick_handler(void)
251251

252252
/* Handle auto-reload timers */
253253
if (t->mode == TIMER_AUTORELOAD) {
254-
t->deadline_ticks = now + MS_TO_TICKS(t->period_ms);
254+
/* Calculate next expected fire tick to prevent cumulative error */
255+
t->last_expected_fire_tick += MS_TO_TICKS(t->period_ms);
256+
t->deadline_ticks = t->last_expected_fire_tick;
255257
timer_sorted_insert(t); /* Re-insert for next expiration */
256258
} else {
257259
t->mode = TIMER_DISABLED; /* One-shot timers are done */
@@ -305,6 +307,7 @@ int32_t mo_timer_create(void *(*callback)(void *arg),
305307
t->arg = arg;
306308
t->period_ms = period_ms;
307309
t->deadline_ticks = 0;
310+
t->last_expected_fire_tick = 0;
308311
t->mode = TIMER_DISABLED;
309312
t->_reserved = 0;
310313

@@ -387,7 +390,8 @@ int32_t mo_timer_start(uint16_t id, uint8_t mode)
387390

388391
/* Configure and start timer */
389392
t->mode = mode;
390-
t->deadline_ticks = mo_ticks() + MS_TO_TICKS(t->period_ms);
393+
t->last_expected_fire_tick = mo_ticks() + MS_TO_TICKS(t->period_ms);
394+
t->deadline_ticks = t->last_expected_fire_tick;
391395

392396
if (unlikely(timer_sorted_insert(t) != ERR_OK)) {
393397
t->mode = TIMER_DISABLED;

0 commit comments

Comments
 (0)