What happens when GetTickCount() wraps?
From the docs:
The elapsed time is stored as a DWORD value. Therefore, the time will wrap around to zero if the system is run continuously for 49.7 days. To avoid this problem, use GetTickCount64. Otherwise, check for an overflow condition when comparing times.
However, DWORD is unsigned - so you should be okay. 0 - "very big number" = "small number" (assuming you don't have any overflow checking active, of course). I had a previous edit which suggested you'd get a negative number, but that was before I took into account that DWORD is unsigned.
You'll still have a problem if the operation takes just under 49.7 days though. That may not be an issue for you ;)
One way to test would be to stub out the GetTickCount()
method so you could write unit tests where you explicitly make it wrap. Then again, if you're really only doubting the arithmetic part, you can easily write unit tests for that :) Really, the fact that the number is coming from a system clock is pretty much irrelevant so long as you know the behaviour when it wraps - and that's specified in the documentation.
Nothing bad happens, as long as:
You subtract
DWORD
s, rather than converting to some other type first.Nothing you're trying to time takes longer than 49.7 days.
This is because unsigned arithmetic overflow is well-defined in C, and wrapping behavior does exactly what we want.
DWORD t1, t2;
DWORD difference;
t1 = GetTickCount();
DoSomethingTimeConsuming();
t2 = GetTickCount();
t2 - t1
will produce the correct the value, even if GetTickCount
wraps around. Just don't convert t2
and t1
to some other type (e.g. int
or double
) before doing the subtraction.
This won't work if the programming language treats overflow as an error. It also won't work if DoSomethingTimeConsuming()
takes longer than 49.7 days. You can't tell just by looking at t2
and t1
how many times GetTickCount
wrapped around, unfortunately.
Let's start with the the usual case, where no wraparound comes into play:
t1 = 13487231
t2 = 13492843
Here, t2 - t1 = 5612
, which means the operation took about five seconds.
Now consider an operation that takes a short amount of time, but where GetTickCount
did wrap around:
t1 = 4294967173
t2 = 1111
The operation took 1234ms, but the timer wrapped around, and 1111 - 4294967173
is the bogus value of -4294966062
. What ever will we do?
Well, modulo 232, the result of subtraction wraps around, too:
(DWORD)-4294966062 == (DWORD)1234
Finally, consider the edge case where an operation takes nearly 232 milliseconds, but not quite:
t1 = 2339189280
t2 = 2339167207
Here, GetTickCount
wrapped around, and came right back around where it was.
Now t2 - t1
yields the bogus-looking value of 4294945223
. That's because that's the amount of time the operation actually took!
In general:
(base + offset) - base ≡ offset mod 2^32
If you want to test what happens when GetTickCount()
wraps, you could enable Application Verifier's TimeRollOver test.
From Using Application Verifier Within Your Software Development Lifecycle:
TimeRollOver forces the GetTickCount and TimeGetTime APIs to roll over faster than they normally would. This allows applications to test their handling of time rollover more easily.