Larry Osterman's WebLog

Confessions of an Old Fogey
Blog - Title

APIs you never heard of - the Timer APIs

APIs you never heard of - the Timer APIs

  • Comments 24

It's time for another "APIs you never heard of" article :)

This time, I'd like to talk about the time* APIs.

The time* APIs are a set of 7 APIs built into the windows multimedia extensions (winmm.dll).  They provide a rudimentary set of timer functions for Windows applications.  At this point, except for two of the APIs, they exist only for historical purposes, the core OS now provides significantly higher quality APIs for timers.

The time APIs fall into three rough categories:

  1. Timer information (timeGetDevCaps, timeGetTime and timeGetSystemTime)
  2. Timer callback functions (timeSetEvent and timeKillEvent)
  3. Timer frequency functions (timeBeginPeriod and timeEndPeriod)

The first two categories are obsolete (arguably timeGetDevCaps still has valid uses).  The timeGetTime API is effectively identical to the GetTickCount() API, and timeGetSystemTime simply returns the exact same value that timeGetTime would have returned, packed into a MMTIME structure. 

The timeSetEvent and timeKillEvent have been replaced with the Win32 Timer Queue functions, I'm not sure if I know of any reason to ever call the MME versions of these functions :).  In fact, timeSetEvent will call PulseEvent API, which is fundamentally flawed.  There is one difference between timeSetEvent and the Win32 timer queue functions - timeSetEvent will call timeBeginPeriod to set the timer resolution to the resolution specified in the call to timeSetEvent.  Even with this, you're better off calling timeBeginPeriod and using the Win32 Timer Queue functions (because the Win32 timer queue functions are far more flexible). 

But then there's the timeBeginPeriod and timeEndPeriod APIs.  These are actually fun APIs, especially in the multimedia or gaming space, because they allow you to change the resolution of the internal scheduler, which can lower (or raise) the resolution with which the internal clock runs.

This has a number of side effects - it increases the responsiveness of the system to periodic events (when event timeouts occur at a higher resolution, they expire closer to their intended time).  But that increased responsiveness comes at a cost - since the system scheduler is running more often, the system spends more time scheduling tasks, context switching, etc.  This can ultimately reduce overall system performance, since every clock cycle the system is processing "system stuff" is a clock cycle that isn't being spent running your application.  For some multimedia applications (video, for example) the increased system responsiveness is worth the system overhead (for instance, if you're interested in very low latency audio or video, you need the system timers to run at a high frequency).

Edit: Added comment about timeSetEvent calling timeBeginPeriod to set the resolution.\

Edit2 (years later): Updated the link to GetTickCount...

  • Thanks for the entry Larry. It's always refreshing to see little-known APIs getting mentioned in the present day.

    I just took a look at the timeBeginPeriod documentation and got a chuckle out of the return values: "Returns TIMERR_NOERROR if successful or TIMERR_NOCANDO if the resolution specified in uPeriod is out of range."

    No can do!
  • Hey-I use timeSetEvent! What is the accuracy of the Timers in the Timer Queues? I thought the multi media timers were supposed to be high resolution timers. Also, I can't get the example at http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/using_timer_queues.asp to compile. The error is
    error C2664: 'CreateTimerQueueTimer' : cannot convert parameter 3 from 'void (void *,int)' to 'void (__stdcall *)(void *,unsigned char)'
  • timeSetEvent has the same resolution as all the other waitable objects in the system.

    On all NT versions, the multimedia timers use the NT internal timer infrastructure, which is the exact same infrastructure that's used by the timer queue timers. For Win 3.1, it may have used a higher resolution timer.

    However, on closer inspection, it appears that the timeSetEvent API will call timeBeginPeriod to set the system clock frequency to the resolution of the timer. I'll update the article.

    For your compiler error, it appears that your callback function isn't declared with the correct function parameters - it needs to be a stdcall function that takes a LPVOID and a BYTE parameter, not a <whatever> function that takes a LPVOID and an int parameter. I'll talk to the owner of the API.

  • Interesting comparison of timing function at http://developer.nvidia.com/object/timer_function_performance.html .
    Strangely however, timeGetTime() and GetTickCount() perform completely different (and not by a small margin).. which is odd if they are really equivalent like you say.

    Anyway in every place I worked, timeGetTime() was quite a popular function :)
  • Fascinating purplet. It turns out that GetTickCount returns the tick count multiplied by the clock frequency, while TimeGetTime returns the "interrupt time", which is a somewhat different calculation (and is apparently not updated as frequently).
  • With CPU's that can change their clock frequency (often seen in laptops) I'm never quite certain about the accuracy of timer functions.
  • Chris, a good and relevent question. The simple answer is that NT guarantees that the timers are stable, which means that it doesn't matter what the clock frequency does, the timer frequency won't change.
  • Larry, did you get GetTickCount() and timeGetTime() mixed up in the comment to purplet? I thought it was the other way around, that GetTickCount() returns interrupt-counted time (10ms/55ms), whereas timeGetTime() returns timer-based time (8254 PIT @ 1.1MHz, usually).

    Also, the current online documentation for timeSetEvent() seems rather scrambled. It says that the function only exists on Windows XP, even though it's been around since Windows 95. TIME_KILL_SYNCHRONOUS seems to have appeared silently -- it wasn't in my October 2000 MSDN Library -- but it isn't marked as requiring a particular OS version. I remember having to work around that missing flag by killing the periodic event from within the callback itself in order to avoid a race.
  • Thanks, Larry, I think it was this blog entry that got me concerned about it: (beginning near the bottom with "on certain mobile processors the tick frequency is not constant..."

    http://www.interact-sw.co.uk/iangblog/2005/03/31/nanoperf
  • I once needed a time measuring facility to assess performance of my code. The multimedia timer API turned out to be surprisingly lightweight in comparison with QueryPerformanceCounter, and more precise than GetTickCount.
  • Suggest: There was internal discussion recently about code that raises 1st chance exceptions and why not to do that even though technically it's legit. Could make for an interesting post (series of posts?).
    Actually another one I'd like to see is "why you really want that would-be service in kernel mode vs. why you really want it to be a service".
    I know the answers I'd give, but you're both more experienced than I am and seem to swear less in public than I do. I'd rather see your explanation.

    Your suggestion box doesn't seem to have a comments link (grumble - where in Product Studio do I file *that* bug?), so I'm posting these here.
  • "NT guarantees that the timers are stable"

    Except when it doesn't:
    http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q274323

    I do not like bad hardware chips, I do not want their timer slips. I do not want them on my bus, I do not want them erroneous.
  • Dave, good point.

    Centaur, QPC is heavyweight, and is documented as such. timeGetTime is faster but much less accurate.

    I actually checked the code for timeGetTime and GetTickCount(). GetTickCount() takes the number of clock interrupts and multiplies it by the clock frequency. timeGetTime() reads a field called the "interrupt time", which is updated periodically by the kernel (I wasn't able to find out how frequently).
  • > I'm not sure if I know of any reason to ever call the MME versions of these functions :).

    How about if you want your code to still be able to run on Win9x?

    I would LOVE to be able to stop having to consider Win9x, but about 15% of my customer base still runs some form of it (about 1% still runs Win95!)
  • So your blog is not correct and will mislead the people who come across it.. timeGetTime is not obsolete, and it isn't effectvly identical to GetTickCount, which is the point of the first paragraph after the bullet list. And the problem Raymond raises about pulse event doesn't affect timeSetEvent.
Page 1 of 2 (24 items) 12