timer Basics


The win+ function that starts a timer is set_timer. This function may be used in a number of different ways. Firstly, a timer may be associated with a window or it may be associated only with the message queue of the calling thread. The first type of timer is referred to as a window timer; whereas, the second timer may be referred to as a queue timer. For queue timers, messages are generated but a callback is directly invoked when the message is dispatched. For window timers, the message message::timer is sent to the associated window.

Theoretically, a timer can range from 1 millisecond to the upper limit of unsigned integer arithmetic (which is nearly 50 days - 4,294,967,295 milliseconds to be precise). The interval specified for a timer indicates the rate at which timer notifications are received. Of course, 1000 milliseconds specifies a timer interval of one second - but as shall be explained shortly, the actual timer interval may be less than that which is specified. A 'one shot' timer may be achieved merely by cancelling the timer upon the first notification; otherwise, the timer is repetitive.

The System and the Timer

Timer logic is built into the BIOS of the computer. The ROM BIOS (read only memory - basic input output system) initializes a timer chip to generate hardware timer interrupts at system startup. The interrupts occur every 54.925 milliseconds or about 18.2 times per second. The operating system handles these interrupts for each program that makes use of timers by decrementing a counter that was originally passed when the timer was created. When the timer reaches zero, a timer message is placed in the appropriate message queue. The counter is then reset to its original value.

Because of the underlying hardware:

  1. an application may not receive messages at a rate of faster than 18.2 times per second and

  2. the timer interval specified when creating the timer is rounded down to an integral multiple of the hardware timer interval.

For example, if 1000 milliseconds is specified as the timer interval, this is divided by 54.925 milliseconds yielding 18.207 clock ticks. This is rounded down to 18 timer cycles which yields an interval of 989 milliseconds - which is the interval at which the timer messages or callbacks are received. When an interval of less than 55 milliseconds is specified, the application receives a notification once each cycle of the timer.

Having said all of the above, recent hardware innovations are such that a high-performance counter with a different (higher) frequency may be installed in the hardware. When available, the operating system makes use of such devices. To ascertain whether a high performance counter is installed, the C function query_performance_counter may be issued. The C function query_performance_frequency may be used to obtain the frequency of the performance counter when one is present. If a high-performance counter is installed, of course the rules change.

Asynchronicity

Timer interrupts are asynchronous to program execution, but timer notifications are received in a synchronous fashion. That is to say, although the operating system services asychronous timer interrupts, the timer messages sent to an application are not processed in an asychronous fashion. For example, if an interval of 1000 milliseconds is specified in a call to create a timer, the program is not guaranteed to receive a notification every second or even every 989 milliseconds. If the application is busy performing other tasks for more than a second, it will not receive a timer message during that time. In fact, timer messages (like paint messages) are of low priority and only processed when no other messages of a higher priority reside in the queue. The similarity of timer and paint messages extends to another respect. Instead of placing multiple timer messages in the queue when the application has not yet serviced a previous timer message, a subsequent timer message is merged with the existing message. This implies that an application will not receive a lot of timer messages in quick succession although it may receive two close together. An application has no way of determining if it has 'missed' a timer message. Generally, an application uses timer messages to update itself, but it usually queries the system time upon receiving the timer message (if required). It may not use timer messages in the fashion of counting the time between notifications.