I assume StartTime is a positive signed integer; I assume you earlier did StartTime = (Environment.TickCount And Int32.MaxValue).
Abs()
does not fix the problem. If Interval < 12.43 days, the code will time correctly while StartTime is small. But some of the time, whenever StartTime > (24.86 days - Interval), the test will pass earlier than it should, the moment that (TickCount And Int32.MaxValue) rolls over to 0. Additionally, if Interval > 12.43 days AND (TickCount And Int32.MaxValue) is close to 12.43 days, the test will never pass.
Doing Environment.TickCount And Int32.MaxValue
is not necessary and not helpful. It makes a positive signed integer32 representing time in ms that wraps around to 0 every 24.86 days. The alternatives are harder to understand, but they let you time intervals up to 49.71 days with no difficulty. The raw Environment.TickCount
wraps around to Int32.MinValue after 24.86 days and every 49.71 days after that. Casting unchecked((Uint32)Environment.TickCount)
makes a [positive] unsigned integer32 representing time in ms that wraps around to 0 every 49.71 days. (Because Environment.TickCount
calls the Windows function GetTickCount(), which returns a DWORD (UInt32), and unchecked-casts it to Int32.)
The wraparound of Environment.TickCount
does not impair measurement of elapsed time. Unchecked subtraction always gives the correct result.
You can compute elapsed time as signed int32 (-24.86 to +24.86 days), more useful if you are comparing independent times or potentially mixing future and past; or unsigned int32 (0 to +49.71 days). You can capture T0 as signed int32 or unsigned int32; it makes no difference to the results. You only need [unchecked] casting if T0 or elapsed time are unsigned; with signed T0 and signed elapsed time, you need no casts.
Capturing T0 signed: (C#)
...
int iT0 = Environment.TickCount; // T0 is NOW.
...
iElapsed = unchecked(Environment.TickCount - iT0) // -24.81 to +24.81 days
uiElapsed = unchecked((uint)Environment.TickCount - (uint)iT0) // 0 to +49.71 days
if (uiElapsed >= uiTimeLimit) // IF time expired,
{
iT0 = Environment.TickCount; // For the next interval, new T0 is NOW.
...
}
Capturing T0 unsigned (my preference; Environment.TickCount
should never have been signed):
...
uint uiT0 = unchecked((uint)Environment.TickCount); // T0 is NOW.
...
iElapsed = unchecked(Environment.TickCount - (int)uiT0) // -24.81 to +24.81 days
uiElapsed = unchecked((uint)Environment.TickCount - uiT0) // 0 to +49.71 days
if (uiElapsed >= uiTimeLimit) // IF time expired,
{
uiT0 = unchecked((uint)Environment.TickCount); // For the next interval, new T0 is NOW.
...
}
If your TimeLimit is close to the wraparound limit (24.81 or 49.71 days), it is possible for time to expire without the test ever passing. You have to test at least once while Elapsed >= TimeLimit. (If you are not sure of testing often enough, you can add a backup test on Elapsed. If Elapsed ever decreases, it has wrapped, so the time must be up.)
=====
To time intervals longer than 49.71 days, you can count how many times uiElapsed wraps around, or you can count how many times Environment.TickCount
wraps around. You can compose the counters into a 64-bit value, emulating GetTickCount64()
(only available in on Windows Vista and newer). A 64-bit value has full range (292 million years) and full resolution (1ms). Or you can make a 32-bit value that has reduced range and/or resolution. The code that checks for wrapping must execute at least once every 49.71 days, to ensure that no wrap goes detected.
uint uiTickCountPrev = 0;
uint uiTickWrap = 0;
Int64 TickCount64;
...
uiTickCount = unchecked((uint)Environment.TickCount) // 0 to +49.71 days
if (uiTickCount < uiTickCountPrev) // IF uiElapsed decreased,
uiWrapcount++; count that uiElapsed wrapped.
uiElapsedPrev = uiElapsed; // Save the previous value.
TickCount64 = (Int64)uiTickWrap << 32 + (Int64)uiTickCount;
Notes:
Environment.TickCount
gives time since boot, in ms, with 10-16ms resolution.
The unchecked Int32 difference gives the time difference, -24.81 to +24.81 days, with wrap-around. Unchecked subtraction of 32-bit integers overflows and wraps around to the correct value. The sign of Environment.TickCount
never matters. Example, wraparound by one: iBase = Environment.TickCount
gets Int32.MaxValue
; one tick later, Environment.TickCount
wraps to Int32.MinValue
; and unchecked(Environment.TickCount - iBase)
yields 1.
The unchecked UInt32 difference gives the time difference, 0 to +49.71 days, with wrap-around. (Elapsed time can never be negative, so this is the better choice.) Unchecked subtraction of unsigned 32-bit integers overflows and wraps around to the correct value. Example, wraparound by one: iBase = Environment.TickCount
gets UInt32.MaxValue
; one tick later, Environment.TickCount
wraps to UInt32.MinValue
; and unchecked(Environment.TickCount - iBase)
yields 1.