loop {
perform_task();
sleep(T);
}
Why this one is bad should be immediately obvious, perform_task() is extremely likely to take some greater-than-zero time to execute, and the loop will thus gain a phase error that increases with each iteration.
The quick and dirty remedy to this is of course to measure the time it takes for perform_task() to run, and subtract that from the slept time.
loop {
t0 = now();
perform_task();
t1 = now();
sleep(T-(t1-t0));
}
This might seem like a good idea, until you realize that there is code that is executed (takes time), but is not between the two calls to now(), like the equation T-(t1-t0). So you will still accumulate a phase error with some time. You could always move that part inside the measured interval, but you would at any rate at least have things like the jump instructions that make up the loop construct remain unmeasured with this method. Or putting it another way, the thing that needs to be measured is everything that happens apart from the time spent sound asleep.
Another thing is that the fine print on sleep() functions often contains things like not guaranteeing that the actually slept amount of time is the time asked for.
The solution is twofold. Firstly, time is only measured at one point in the loop, and then compared it with the last measurement. That way, everything that happens in a single loop iteration will be measured. Secondly, to compensate for irregular sleeping patterns, a running counter is used to keep keep track of the accumulated error.
t0 = now();
error = 0;
loop {
perform_task();
sleep(T-error);
t1 = now();
error += t1-t0 - T;
t0 = t1;
}
Finally, you may also want to make that you are not trying to sleep a negative amount of time.
t0 = now();
nap = T;
loop {
perform_task();
if nap > 0 {
sleep(nap);
}
t1 = now();
nap -= (t1-t0) - T;
t0 = t1;
}