Reliable timings using Threads

One common task is to have threads do something at regular, scheduled, intervals.

The problem

An obvious solution is to write something like this:

msg_t my_thread(void *param) {
 
  while (true) {
    do_something();
    chThdSleepMilliseconds(1000); /* Fixed interval.*/
  }
}

This example works well assuming that the do_something() execution time is well below the system tick period and that my_thread() is not preempted by other threads that could insert long intervals.
If the above conditions are not satisfied you may have do_something() executed at irregular intervals, for example:

T0…T0+1000…T0+2002…T0+3002…T0+4005…

Also note that the error increases over time and this kind of behavior can lead to anomalies really hard to debug.

A better solution

It is possible to rewrite the above code using absolute deadlines rather than fixed intervals:

msg_t my_thread(void *param) {
 
  systime_t time = chVTGetSystemTimeX(); // T0
  while (true) {
    time += MS2ST(1000);                 // Next deadline
    do_something();
    chThdSleepUntil(time);
  }
}

Using this code do_something() will always be executed at an absolute deadline time and the error will not accumulate over time regardless of the execution time and delays inserted by other threads.
Note that this solution requires that the do_something() execution time must not exceed the deadline or the thread would stay sleeping into chThdSleepUntil() until the (very far) specified instant in the future.

A different solution

Another way to perform activities at regular intervals is the use of a virtual timer. Virtual timers are able to generate callbacks at scheduled intervals. Virtual timers are one shot timers so you need to restart them from within the callback if you need a periodic timer like in this case.

VirtualTimer vt;
 
void do_something(void *p) {
 
  /* Restarts the timer.*/
  chSysLockFromISR();
  chVTSetI(&vt, MS2ST(1000), do_something, p);
  chSysUnlockFromISR();
 
  /* Periodic code here.*/
}
 
int main(int argc, char **argv) {
 
  /* Starts the timer.*/
  chVTSet(&vt, MS2ST(1000), do_something, NULL);
  ...
}

Note that the callback code is executed from within the I-Locked state so you can only execute I-Class APIs from there. This solution has the advantage to not require a dedicated thread and thus uses much less RAM but the periodic code must have a very short execution time or it would degrade the overall system response time, the reason is that virtual timers callbacks are invoked from an interrupt handler.

More articles and guides are available on the technical wiki.

learn more

Need Tutorials?

Try the video tutorials and guides on Play Embedded.

learn more

Need Support?

The forums is the best place, registration required.

learn more