Is there a “Nt” or similar (i.e. non-kernelmode-driver) function equivalent for KeQueryInterruptTime or anything similar? There seems to be no such thing as NtQueryInterruptTime, at least I’ve not found it.
What I want is some kind of reasonably accurate and reliable, monotonic timer (thus not QPC) which is reasonably efficient and doesn’t have surprises as an overflowing 32-bit counter, and no unnecessary “smartness”, no time zones, or complicated structures.
So ideally, I want something like timeGetTime with a 64 bit value. It doesn’t even have to be the same timer.
There exists GetTickCount64 starting with Vista, which would be acceptable as such, but I’d not like to break XP support only for such a stupid reason.
Reading the quadword at 0x7FFE0008 as indicated here … well, works … and it proves that indeed the actual internal counter is 64 bits under XP (it’s also as fast as it could possibly get), but meh… let’s not talk about what a kind of nasty hack it is to read some unknown, hardcoded memory location.
There must certainly be something in between calling an artificially stupefied (scaling a 64 bit counter down to 32 bits) high-level API function and reading a raw memory address?
Here’s an example of a thread-safe wrapper for GetTickCount() extending the tick count value to 64 bits and in that being equivalent to GetTickCount64().
To avoid undesired counter roll overs, make sure to call this function a few times every 49.7 days. You can even have a dedicated thread whose only purpose would be to call this function and then sleep some 20 days in an infinite loop.
EDIT: And here’s a complete application that tests MyGetTickCount64().
As a test I’ve been running this test app under Windows XP for 5+ hours on an otherwise idle machine that has 2 CPUs (idle, to avoid potential long starvation times and therefore avoid missing counter overflows that occur every 5 seconds) and it’s still doing well.
Here’s the latest output from the console:
As you can see,
MyGetTickCount64()has observed 3824 32-bit overflows and failed to update the value ofCountwith its secondInterlockedCompareExchange64()110858 times. So, overflows indeed occur and the last number means that the variable is, in fact, being concurrently updated by the two threads.You can also see that the 64-bit tick counts that the two threads receive from
MyGetTickCount64()inTickUserThread()don’t have anything missing in the top 32 bits and are pretty close to the actual 64-bit tick count inSimulatedTickCount, whose 32 low bits are returned bySimulatedGetTickCount(). 0x00000E1BC8800000 is visually behind 0x00000E1BFA800000 due to thread scheduling and infrequent stat prints, it’s behind by exactly 100*TICK_COUNT_10MS_INCREMENT, or 1 second. Internally, of course, the difference is much smaller.Now, on availability of
InterlockedCompareExchange64()… It’s a bit odd that it’s officially available since Windows Vista and Windows Server 2003. Server 2003 is in fact build from the same code base as Windows XP.But the most important thing here is that this function is built on top of the Pentium
CMPXCHG8Binstruction that’s been available since 1998 or earlier (1), (2). And I can see this instruction in my Windows XP’s (SP3) binaries. It’s in ntkrnlpa.exe/ntoskrnl.exe (the kernel) and ntdll.dll (the DLL that exports kernel’s NtXxxx() functions that everything’s built upon). Look for a byte sequence of 0xF0, 0x0F, 0xC7 and disassemble the code around that place to see that these bytes aren’t there coincidentally.You can check availability of this instruction through the
CPUIDinstruction (EDX bit 8 of CPUID function 0x00000001 and function 0x80000001) and refuse to run instead of crashing if the instruction isn’t there, but these days you’re unlikely to find a machine that doesn’t support this instruction. If you do, it won’t be a good machine for Windows XP and probably your application as well anyways.