TIMING AND EVENT SCHEDULING

BACKGROUND INFORMATION:

Unlike Atlas' KURT Linux implementation, Windows NT does not support microsecond timer resolution. By default, Windows uses a lower resolution counter that records the number of milliseconds since Windows started. This value is stored in a 32-bit word and it wraps after 49.7 days of continuous running. Win32’s function call GetTickCount() returns this value. To set scheduled events, Win32 also supplies the SetTimer() function which allows the scheduling of a timer in millisecond intervals.

There exists an alternative to this low-resolution counter known as a performance monitor counter. With the additional hardware of this high-resolution performance counter, the value stored in the counter is expanded to 64-bits wide, thus increasing the precision of the counter. This greater precision is typically useful when determining performance of a system where many transactions can occur within a millisecond. The frequency of the counter is determined by the hardware; on Intel-based CPUs it is about 0.8 microseconds (0.838 on the computer used in this implementation).  The high-performance counters are available for performing time-sensitive calculations, but there is no support yet for high-resolution timed events. This basically means that all timing boundaries must be made on millisecond boundaries leaving programmers with not many alternatives other than the SetTimer() function.

Therefore, the scope of this project is limited to millisecond event scheduling.  However, additional problems now surface. First of all, there are substantial timing discrepancies when using the SetTimer() function.  That function uses the same lower-resolution timer that GetTickCount() uses above.  Although SetTimer() boasts the ability to fire on millisecond boundaries, it is limited by the resolution of the timer. A simple accuracy test performed by Chih-Hao Tsai at the University of Illinois at Urbana-Champaign showed that the average error of this timer was about 10 milliseconds.  Additional tests that I performed and my own experience with timers in previous versions of this project proved exactly the same.

TIMING AND EVENT SCHEDULING IN THIS PROJECT:

The timing solution implemented in the SRMS scheduling service introduces the use of Window's multimedia timer.  This timer is not part of the Win32 API and instead is hidden as part of Window's multimedia library (winmm.lib).  This timer uses a counter whose resolution is much more precise, but still not perfect, giving an average error of 1 millisecond.  The multimedia timer's tick value is returned by the function timeGetTime().  The multimedia library also has an event timer with the same accuracy as timeGetTime().  The problem with that timer is that it is not implemented in the NT kernel like the SetTimer() function which sends WM_TIMER messages to the calling application's message handler.  Instead, the timeSetEvent() function schedules a thread at the highest possible NT system priority that executes in a tight loop until it reaches the scheduled time, and when reached, it executes a pre-defined callback function.

threads.gif (5177 bytes)

When the SRMS scheduler calls SetNextSchedulerEvent(), that function calls timeSetEvent() to set a timer to fire after a given time elapses.  Behind the scenes, timeSetEvent() creates a single-threaded process executing at the highest possible NT system priority that efficiently polls the system counter until the time elapses.   When reached, the timer thread executes the callback function SrmsSchedulerThreadProc() to immediately send a WM_TimerFired message to the SRMS scheduler thread.  After sending it, the timer thread terminates and the SRMS scheduler wakes from its waiting state because of the arrival of the timer fired message.   The scheduler proceeds to determine the next job to run and repeats this procedure by calling SetNextSchedulerEvent() to set another interrupt timer. 

WHAT IS SRMS RELATIVE TIME?

When the SRMS service first starts, part of its initialization process includes recording the current system time.  When events are logged in the progress window, their message includes a timestamp relative to this system start value.  The current system time is again recorded when the SRMS service enters its 'started' state, signifying the start of the SRMS scheduler.  All tasks receive this value as their system start time when the await_system_start() SRMS API function is called.  Also, all start, end, and period times recorded in the execution history log are relative to this value.  For clarification, the service's start time is stored in the DWORD startTime, and the scheduler's start time is stored in the DWORD systemStart, both of which are defined in mainapp.c.

[NOTE:  One problem not addressed in this implementation that should be mentioned is the potential errors that may occur with the multimedia timer and the timeGetTime() function.  Its return value wraps around to 0 every 2^32 milliseconds, which is about 49.71 days.  This can cause problems in code that directly uses the timeGetTime return value in computations, particularly where the value is used to control code execution. To account for this, the code should always use the difference between two timeGetTime return values in computations.]

< Back to the SRMS Service Home Page >