A preemptive strike against a bug that doesn't exist yet

I know that SDL currently does not support time
quereys that are more than millisecond accurate. I
could immagine that such a feature is a likely
addition to sdl 1.3 OR 2.0. Under Windows, I would
also expect that such a function would be implemented
using the QueryPerformanceCounter() WinAPI function.
I am submitting the following code in anticipation of
such an eventuallity.

It appears that this function does not work reliably
on some motherboards running Windows 2000. I suspect
that it may or may not affect other version of
windows. I have found third party verification of the
bug here:

http://www.etestinglabs.com/bi/bugs/wsbugs.asp?visitor=

but know for sure that my motherboard is affected.
After much experimentation, the bug is not as simple
as described in the link above. Most notably, while
the counter does jump every several seconds, it is not
strictly a rollover problem, as the counter jumps
unpredictably backward OR forward by sometimes as
much as 100 seconds. I did come up with a solution,
and am including it below. Hope this helps.

Best wishes,

-Loren Osborn

============BEGIN============

BOOL QueryPerformanceCounterFixed(LARGE_INTEGER*
pValue)
{
LARGE_INTEGER CurrentValue;

BOOL RetVal = QueryPerformanceCounter(&CurrentValue);

// this is a fix for the timer in Windows 2000 with

certain motherboards
static bool SystemHasPerformanceCounter = false;
static bool PerformanceCounterUnreliable = false;
static LONGLONG LastValue = CurrentValue.QuadPart;
static LONGLONG LastPerformanceValue =
CurrentValue.QuadPart;
static float Multiplier = 0.0f;
static LONGLONG PerformanceCounterOffset = 0;
static unsigned int TickCountStart = 0;

if(SystemHasPerformanceCounter || (RetVal &&

(CurrentValue.QuadPart != 0)))
{
SystemHasPerformanceCounter = true;
if(PerformanceCounterUnreliable)
{
double CurrentTickCenter = GetTickCount() -
TickCountStart;
LONGLONG NewCurrentCenter =
(LONGLONG)(CurrentTickCenter * Multiplier);
NewCurrentCenter += PerformanceCounterOffset;
LONGLONG NewCurrentMinimum = NewCurrentCenter -
((LONGLONG)(Multiplier/2));
LONGLONG NewCurrentMaximum = NewCurrentCenter +
((LONGLONG)(Multiplier/2));
LONGLONG NewCurrentValue = NewCurrentCenter;
// never go backwards
if(NewCurrentMinimum < LastValue)
{
NewCurrentMinimum = LastValue;
}
// can’t overlap
if(NewCurrentMinimum > NewCurrentMaximum)
{
// UNTESTED CODE:
// Branch not tested, but precautionary
NewCurrentMaximum = NewCurrentMinimum;
}
LONGLONG Delta = (CurrentValue.QuadPart -
LastPerformanceValue);
// can’t go backwards
if(Delta < 0)
{
Delta = 0;
}
NewCurrentValue = LastValue + Delta;
if((NewCurrentValue < NewCurrentMinimum) ||
(NewCurrentValue > NewCurrentMaximum))
{
// The new value is out of range… Resync to
center
NewCurrentValue = NewCurrentCenter;
}
if((NewCurrentValue < NewCurrentMinimum) ||
(NewCurrentValue > NewCurrentMaximum))
{
// Center has already elapsed… Use minimum
NewCurrentValue = NewCurrentMinimum;
}
LastPerformanceValue = CurrentValue.QuadPart;
CurrentValue.QuadPart = NewCurrentValue;
}
else
{
if(LastValue > CurrentValue.QuadPart)
{
// switch gears to be in GetTickCount mode
PerformanceCounterUnreliable = true;
PerformanceCounterOffset = LastValue;
TickCountStart = GetTickCount();
CurrentValue.QuadPart = LastValue;
LARGE_INTEGER Frequency;
QueryPerformanceFrequencyFixed(&Frequency);
Multiplier = (float)(Frequency.QuadPart/1000.0);
}
LastPerformanceValue = CurrentValue.QuadPart;
}
LastValue = CurrentValue.QuadPart;

	pValue->QuadPart = CurrentValue.QuadPart;
}
else // This is for systems that don't have a

performance counter
{
// UNTESTED CODE:
// Unable to test this on my hardware
pValue->QuadPart = GetTickCount();
}

return TRUE;

}

//*********************************************************************************************************************
BOOL QueryPerformanceFrequencyFixed( LARGE_INTEGER
*pFrequency )
{
LARGE_INTEGER a;

BOOL RetVal = QueryPerformanceFrequency(&a);

#ifdef _DEBUG
static bool InitialFrequencyInitialized = false;
static LARGE_INTEGER InitialFrequency;

if(!InitialFrequencyInitialized)
{
	InitialFrequencyInitialized = true;
	InitialFrequency.QuadPart = a.QuadPart;
}
assert(InitialFrequency.QuadPart == a.QuadPart);

#endif // _DEBUG

if(RetVal && (a.QuadPart != 0))
{
	pFrequency->QuadPart = a.QuadPart;
}
else // This is for systems that don't have a

performance counter
{
// UNTESTED CODE:
// Unable to test this on my hardware
pFrequency->QuadPart = 1000;
}

return TRUE;

}

=============END=============__________________________________________________
Do you Yahoo!?
The New Yahoo! Search - Faster. Easier. Bingo
http://search.yahoo.com