Welcome to Suricrasia Online!

"Connecting your world, whenever!"

How to Make an Egg Timer

In programming it's rare to find a problem that has only one solution. May it be string formatting or resource allocation, there is always more than one way to do it. This is one of the reasons why programming is in some ways a creative process.

Often times a problem has an obvious and nice solution that seems perfect theoretically, but has unpredicted, nasty behaviour in practice.

Here's an example of such a problem: Suppose you want to write an egg timer, also known as a countdown timer. Ultimately, this task boils down to solving the following problem:

For M many seconds, call a function U once every N seconds.

Bad Solution: Create a timer that sleeps for N seconds. When time is up it calls U then resets itself if the number of past iterations is less than M/N.

What might happen: If M is very large and N is very small, the program will call U as many as M/N times, but it's not guaranteed that it will finish after EXACTLY M seconds. It could be quite a bit more.

What goes wrong: When it comes to keeping timers, computers are usually very inaccurate. The only accurate clock on a computer is the RTC (Real Time Clock) which is a physical clock circuit on the motherboard. The only thing the RTC can do is return the current time. (This isn't 100% true, there are hardware timers that can be set to produce interrupts, but they're almost always unavailable unless you're writing kernel code)

To keep track of a timer, your program or operating system must check the RTC at regular intervals to see if the deadline of the timer has passed. When a timer goes off, there is an associated overrun value that indicates how much time has passed since the deadline. Even if the average amount of overrun is low, every time you reset the timer you add more overrun.

If M is large and N is small, then M/N is VERY large, and hence there will be a lot of overrun, and your solution will run for more than M seconds. This is bad if you're writing a countdown timer, but have to call a drawing function every few milliseconds to update the user interface.

Better Solution 1: On initialization get the system time, add M seconds to it, and keep that new time in a variable. This variable represents our deadline. Then, create a repeating timer that “ticks” every N seconds. When the timer goes off, call U, then check the system time again. If the deadline has passed the timer turns itself off.

This works by making a kind of implicit timer that you check every N seconds. The overrun of the solution is now bounded by N, so our program will run for at most M+N many seconds.

Better Solution 2: Instead of one timer, keep two. One that repeats every N seconds to call U, and another that waits for M many seconds. When the second timer finishes, it turns the repeating one off. This is more elegant than the above, but doesn't work if the system you're writing for doesn't support multiple timers.

Examples:

Recently there was a countdown timer on the Team Fortress 2 website. It was counting down the seconds until the release of a new TF2 short, as well as some kind of gesture pack (I don't play the game.) The countdown was written in javascript, and among a few other programming mistakes, it made the one outlined in this post.

This is especially bad since it's written in javascript, which has a reputation for inaccurate timing. I want to make it perfectly clear that using setInterval with a timeout of 1 second, in conjunction with keeping track of how many seconds have passed manually, is a BAD MOVE.

I had the countdown open for two hours, and the countdown became slow by two minutes. Thats about one minute per hour. If you kept it open for a week (which is not uncommon for a release countdown) it may become almost 3 hours slow.

Hopefully you can understand how terrible this would be in a mission critical system. If you're keeping track of time manually, in any sense of the word, then you're doing it wrong. Go back to the RTC, ask the system for the correct time and keep asking if you want to stay accurate. The overhead of a system call is always better than the incremental error that piles up when you keep setting timers.


← Back