What happens to lost timer messages if I don’t process them fast enough?


Some time ago, I noted that if your WM_TIMER handler takes longer than the timer period, your queue will not fill up with WM_TIMER messages. The WM_TIMER message is generated on demand, and if your handler doesn't check for messages, then there is no demand for timer messages. But what happens when your thread finally starts processing messages again? What happens to the timers that elapsed while you were busy? Do they accumulate?

Here's a sketch of how timers work. (Note that the timers under discussion here are the timers set by the Set­Timer function.)

When a timer is created, it is initially not ready.

Every n milliseconds (where n is the period of the timer), the timer is marked ready. This is done regardless of the state of the UI thread. Note that ready is a flag, not a counter. If the timer is already ready, then it stays ready; there is no such thing as "double ready". The QS_TIMER flag is set on the queue state, indicating that there is now a pending timer for the thread. This in turn may cause a function like Get­Message or Msg­Wait­For­Multiple­Objects to wake up.

When the appropriate conditions are met (as discussed earlier), the window manager checks whether there are any timers for the thread that are marked ready. If so, then the corresponding WM_TIMER message is generated and the ready flag is cleared.

Let's illustrate this with our scratch program. Make the following changes:

#include <strsafe.h>

DWORD g_tmStart;

void SquirtTime()
{
 TCHAR sz[256];
 StringCchPrintf(sz, 256, "%d\r\n", GetTickCount() - g_tmStart);
 OutputDebugString(sz);
}

This adds a function that prints the number of milliseconds which have elapsed since g_tmStart. Note that we use simple subtraction and rely on unsigned arithmetic to handle timer rollover issues.

void CALLBACK OnTimer(HWND hwnd, UINT, UINT_PTR, DWORD)
{
 SquirtTime();
}

Our timer tick handler merely prints the elapsed time to the debugger.

BOOL
OnCreate(HWND hwnd, LPCREATESTRUCT lpcs)
{
 g_tmStart = GetTickCount();
 SetTimer(hwnd, 1, 500, OnTimer);
 Sleep(1750);
 return TRUE;
}

Finally, we create a 500ms timer on our window, but we also intentionally stall the thread for 1750ms.

Can you predict the output of this program?

Here's what I got when I ran the program:

1797
2000
2500
3000
3500
4000
4500
...

Let's see if we can explain it.

Since the timer is set to fire at 500ms intervals, every 500ms, the timer gets marked ready.

  • At T = 500ms, the timer is marked ready.
  • At T = 1000ms, the timer is marked ready. This is redundant, since the timer is already ready.

  • At T = 1500ms, the timer is marked ready. Again, this is redundant.

  • At T = 1750ms, the program finally wakes up from its Sleep and eventually gets around to processing messages.

  • Hey look, there is a ready timer, so we generate a WM_TIMER message and clear the ready flag.

  • At T = 1797ms, the timer message is processed.
  • The program returns to its message loop, where there are no further messages to process, so it sits and waits.

  • At T = 2000ms, the timer is marked ready. This causes the Get­Message to wake up generate a WM_TIMER message and clear the ready flag.

  • At T = 2000ms, the timer message is processed.
  • At T = 2500ms, the timer is marked ready. This causes the Get­Message to wake up generate a WM_TIMER message and clear the ready flag.

  • At T = 2500ms, the timer message is processed.
  • And so on, with a new timer message every 500ms that is processed immediately.

Observe that when the program begins processing messages at T = 1750ms, it receives only one timer message right away, even though three timer periods have elapsed.

I guess another way of looking at this is to think of your timer as setting a frame rate. If your thread is busy when it's time to render the next frame, then the next frame is late. And if your thread is really busy, it may drop frames entirely.

Comments (13)
  1. Henke37 says:

    I would personally point out that the answer is in the question. Lost messages are lost. Of course, technically they didn't exist in the first place…

  2. Kevin says:

    So, in short, a timer is like an event, rather than a semaphore.

  3. Andrew says:

    Raymond, thanks!  I've had this question in my head, in vague form, since I started working with timers in Windows Forms over 10 years ago.

  4. EduardoS says:

    And how things works when there are two timers?

  5. @EduardoS: What would you expect to happen?  Each timer owns its own ready state, and once a registered timer is ready, the thread gets notified with an event.  The mechanisms are the same, you just have multiple ready states that get checked by the window manager.

  6. McBucket says:

    @EsduardoS: I believe that there is plenty of context in the article above to answer your question. It would appear that each timer has a corresponding flag, so given that, the rest should fall out pretty readily…

  7. Darran Rowe says:

    I think one thing that really tells what is meant to happen is if you look at the timer callback function. The final parameter is basically the result of a call to GetTickCount.

    So what this says to me is that timer events are not really guaranteed to be reliable, and if you really need to be sure, you should work on the difference in time between the current timer event and the previous.

    Another thing that I'm sure is obvious, but if I don't address it, someone will focus on it is the function that you use to get the time from the system. Yes the WinAPI itself uses GetTickCount, but the callback in Windows is old. If you do use a timer, you should use one that can last as long as you need and is as precise as you need.

  8. Rick C says:

    "It would appear that each timer has a corresponding flag, so given that, the rest should fall out pretty readily…"

    And it would only take about 5 more lines of code to test that theory, as well, code that should be obvious from what was already provided.

  9. meh says:

    @Darran Rowe. Also obvious is if you need heaps accurate timing then maybe you need to look at basing your whole thing on some other mechanism than WM_TIMER.

    @EduardoS. I think it depends on the inverse square of the distance between the timers.

  10. Roel Schroeven says:

    Do you really get such nice rounded timings? I've never seen that in practice. Using your program, I get this output:

    1763

    2059

    2559

    3073

    3573

    4087

    4587

    5101

    5601

    That confirms what I've always noticed in practice: the delta between triggers is often larger than the specified interval.

  11. Sven2 says:

    I also wonder why the first timer fires at 1797 in the example. That's 47 milliseconds to set up the timer, sleep, wake up and process the timer message. Seems like a lot of time for a simple task to me.

  12. EduardoS says:

    @everybody

    What I asked was, let's say you have set up two timers, every one second, for example, but the message processing takes more than one second, let's say, 2 seconds, so, the flags of both timers are checked, one get's issued, but processin takes too long and the flag is checked again, in the next GetMessage wich timer message will be dispatched?

  13. Darran Rowe says:

    @EduardoS:

    That should be implementation defined. IMO, the most likely ordering is that it will dispatch the timer messages in order of them being first triggered, but I don't remember enough of the documentation to comment on if this is true or guaranteed.

    The important thing to remember though is that if there are multiple timers active, all of the timers will eventually trigger.

    @Roel Schroeven, Sven2:

    The output I get is similar to Raymond's.

    1781

    2000

    2500

    3000

    3500

    4000

    4500

    ….

    Remember there are some important things with getting these times. First, the initial wait time is there because the thread is set to sleep for 1750, but it is set to sleep right at the end of WM_CREATE. There are some really heavy messages that it has to process before it gets to the timer ones. For example, it seems like the relative priority is that the WM_PAINT message gets processed before WM_TIMER. So is it hard to believe that drawing the non-client area, and then the client area may take time? There are a few more things to remember, how the paint message is non trivial regardless, you have to clear the invalidated region at the very least, and also how many other messages are in the queue after WM_CREATE? When you take that into account, it is not that surprising that the initial time is not 1750.

    Second, my development system is an Intel system that is quad core + hyper threading. The system is also not busy while running the application. So in the cases where the WM_TIMER occur, the thread wakes, is guaranteed to run immediately, and gets the call to GetTickCount done quickly. If you are doing this while your system is busy then it isn't that surprising that you will get some extra time when the timer is handled.

    @Raymond:

    StringCchPrintf(sz, 256, "%drn", GetTickCount() – g_tmStart);

    I do that quite often myself, you are being good using TCHAR, and then a literal comes along and you forget to wrap it with the TEXT macro. I can't remember how many compiler errors I have gotten from that.

Comments are closed.

Skip to main content