mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
633 lines
22 KiB
633 lines
22 KiB
/* OS/2 Version
|
|
* Timer.c - Source file for a statistical
|
|
* dll package that exports four
|
|
* entry points:
|
|
* a) TimerOpen
|
|
* b) TimerInit
|
|
* c) TimerRead
|
|
* d) TimerClose
|
|
* e) TimerReport
|
|
* f) TimerQueryPerformanceCounter
|
|
* g) TimerConvertTicsToUSec
|
|
*
|
|
* Entry point a) opens a timer object
|
|
* and returns a handle to the timer. This
|
|
* handle is an index into an array of timer
|
|
* objects (structs) that are allocated at
|
|
* the time of the initialization of the dll.
|
|
* This ensures that allocation is done once only.
|
|
* Each application program will call this
|
|
* this function so that it has its own set
|
|
* of timers to use with TimerInit and TimerRead.
|
|
* The units of the time returned by TimerRead
|
|
* is also made available as a parameter to
|
|
* this call.
|
|
*
|
|
* Entry point b) is called by the application
|
|
* before commencing a timing operation. This
|
|
* function is called with a handle to a timer
|
|
* object that was opened. This function has to
|
|
* to be called before a call to TimerRead. The
|
|
* current time is stored in the timer object.
|
|
*
|
|
* Entry point c) is called each time the time
|
|
* since the previous call to TimerInit is
|
|
* desired. This call also uses the handle to
|
|
* a timer that has been previosly opened. The
|
|
* current time is obtained form the lowlevel
|
|
* timer and this and the time at TimerInit time
|
|
* are used, along with the clock frequency and
|
|
* the return time units and the elapsed time
|
|
* is obtained and returned as a ULONG.
|
|
*
|
|
* Entry point d) is called whenever an opened
|
|
* timer is not needed any longer. This call
|
|
* resets the timer and makes this timer as
|
|
* available to future calls to TimerOpen.
|
|
*
|
|
* Entry point e) returns the time obtained during
|
|
* the last call to TimerInit, TimerRead and the
|
|
* last returned time.
|
|
*
|
|
* Entry point f) accepts pointers to 2 64 bit
|
|
* vars. Upon return, the first will contain the
|
|
* the current timer tic count and the second,
|
|
* if not NULL, will point to the timer freq.
|
|
*
|
|
* Entry point g) accepts Elapsed Tics as ULONG,
|
|
* Frequency as a ULONG and returns the time in
|
|
* microsecs. as a ULONG.
|
|
*
|
|
* The dll initialization routine does the
|
|
* following:
|
|
* a) Obtains the timer overhead for calibration
|
|
* purposes.
|
|
* b) Allocates memory for a large number of
|
|
* timer objects (this will be system dep).
|
|
* c) Initializes each timer objects "Units'
|
|
* element to a "TIMER_FREE" indicator.
|
|
* d) Determines the lowlevel timer's frequency.
|
|
*
|
|
* TimerRead uses an external asm routine to perform
|
|
* its computation for elapsed time.
|
|
*
|
|
* Created - Paramesh Vaidyanathan (vaidy)
|
|
* Initial Version - October 18, '90
|
|
*
|
|
* Modified to include f). - Feb. 14, 1992. (vaidy).
|
|
*/
|
|
|
|
char *COPYRIGHT = "Copyright Microsoft Corporation, 1991-1998";
|
|
|
|
#ifdef SLOOP
|
|
#define INCL_DOSINFOSEG
|
|
#define INCL_DOSDEVICES
|
|
#define INCL_DOSPROCESS
|
|
#endif
|
|
|
|
#include <nt.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
#include <windows.h>
|
|
|
|
#include "timing.h"
|
|
/*****************************END OF INCLUDES*************************/
|
|
#define ITER_FOR_OVERHEAD 250
|
|
#define SUCCESS_OK 0
|
|
#define ONE_MILLION 1000000L
|
|
#define MICROSEC_FACTOR 1000000
|
|
#define TIMER_FREQ 1193167L /* clock frequency - Hz */
|
|
/*********************************************************************/
|
|
Timer pTimer [MAX_TIMERS]; /* array of timer struct */
|
|
|
|
BOOL bTimerInit = FALSE; /* TRUE indicates low level timer exists */
|
|
ULONG ulTimerOverhead = 50000L; /* timer overhead stored here */
|
|
BOOL bCalibrated = FALSE; /* TRUE subtracts overhead also */
|
|
ULONG ulFreq; /* timer frequency */
|
|
LONG aScaleValues[] = {1000000000L, 1000000L, 1000L, 1L, 10L, 1000L};
|
|
/* this is the table for scaling the units */
|
|
ULONG ulElapsedTime = 0L;
|
|
/********************* internal unexported routines ***************/
|
|
ULONG CalibrateTimerForOverhead (VOID);
|
|
/*****************DEFINE VARIBLES AND PROTOTYPE FNS. FOR PLATFORMS*****/
|
|
NTSYSAPI
|
|
NTSTATUS
|
|
NTAPI
|
|
NtQueryPerformanceCounter (
|
|
OUT PLARGE_INTEGER PerformanceCount,
|
|
OUT PLARGE_INTEGER PerformanceFrequency OPTIONAL
|
|
);
|
|
|
|
LARGE_INTEGER PerfFreq;
|
|
LARGE_INTEGER CountCurrent;
|
|
|
|
SHORT GetTimerFreq (VOID);
|
|
|
|
/****************** internal unexported routines end***************/
|
|
|
|
/*
|
|
* Function - TimerOpen (EXPORTED)
|
|
* Arguments -
|
|
* (a) SHORT far * - address to which to return handle of
|
|
* the timer object.
|
|
* (b) TimerUnits - units in which to return time from
|
|
* TimerRead. It is one of the enum
|
|
* types defined in the header file.
|
|
*
|
|
* Returns - SHORT - 0 if handle was returned successfully
|
|
* Else, an error code which may be one of:
|
|
*
|
|
* TIMERERR_NOT_AVAILABLE
|
|
* TIMERERR_NO_MORE_HANDLES
|
|
* TIMERERR_INVALID_UNITS
|
|
*
|
|
* Obtains the handle to a timer object after opening it.
|
|
* Should precede any calls to timer manipulation. Checks
|
|
* for validity of timer units.
|
|
*/
|
|
|
|
SHORT
|
|
TimerOpen (
|
|
SHORT * phTimerHandle,
|
|
_TimerUnits TimerUnits
|
|
)
|
|
{
|
|
SHORT csTemp;
|
|
|
|
if ((TimerUnits < KILOSECONDS)
|
|
|| (TimerUnits > NANOSECONDS)) /* out of the enum range */
|
|
return (TIMERERR_INVALID_UNITS);
|
|
|
|
if (!bTimerInit) /* set during dll initialization */
|
|
return (TIMERERR_NOT_AVAILABLE);
|
|
|
|
/* else check if any timers are not in use and return the first
|
|
available timer handle....actually the index into the
|
|
timer object array */
|
|
for (csTemp = 0; csTemp < MAX_TIMERS; csTemp++) {
|
|
if (pTimer [csTemp].TUnits == TIMER_FREE) {
|
|
*phTimerHandle = csTemp; /* found a free timer. Return
|
|
the handle */
|
|
pTimer [csTemp].ulHi = pTimer[csTemp].ulLo = 0L;
|
|
pTimer [csTemp].TUnits =
|
|
TimerUnits; /* set the units for timer */
|
|
return (SUCCESS_OK);
|
|
}
|
|
}
|
|
/* if exec reached here, all timers are being used */
|
|
return (TIMERERR_NO_MORE_HANDLES);
|
|
}
|
|
|
|
/*
|
|
* Function - TimerInit (EXPORTED)
|
|
* Arguments -
|
|
* (a) SHORT - hTimerHandle
|
|
*
|
|
* Returns - SHORT - 0 if call successful
|
|
* Else, an error code if handle invalid:
|
|
*
|
|
* TIMERERR_INVALID_HANDLE
|
|
*
|
|
* Calls the low-level timer and sets the ulHi and ulLo of the
|
|
* chosen timer to the time returned by the timer. Should be
|
|
* called after opening the timer with TimerOpen.
|
|
*/
|
|
|
|
SHORT
|
|
TimerInit (
|
|
SHORT hTimerHandle
|
|
)
|
|
{
|
|
|
|
NTSTATUS NtStatus;
|
|
|
|
if ((hTimerHandle > MAX_TIMERS - 1) ||
|
|
(pTimer [hTimerHandle].TUnits == TIMER_FREE))
|
|
/* this timer has not been opened or does not exist. Return error */
|
|
return (TIMERERR_INVALID_HANDLE);
|
|
|
|
/* otherwise get the time from the low-level timer into
|
|
the structure */
|
|
|
|
NtStatus = NtQueryPerformanceCounter (&CountCurrent, NULL);
|
|
pTimer [hTimerHandle].ulLo = CountCurrent.LowPart;
|
|
pTimer [hTimerHandle].ulHi = CountCurrent.HighPart;
|
|
/* this timer structure has all the information needed to compute
|
|
the elapsed time. So return success, if there was no problem */
|
|
|
|
return (SUCCESS_OK);
|
|
}
|
|
|
|
/*
|
|
* Function - TimerRead (EXPORTED)
|
|
* Arguments -
|
|
* (a) SHORT - hTimerHandle
|
|
*
|
|
* Returns - ULONG - elapsed time since last call to TimerInit
|
|
* if call successful.
|
|
*
|
|
* Else, an error code if handle invalid or output
|
|
* overflow. The error code will be the same:
|
|
*
|
|
* TIMERERR_OVERFLOW (max possible ULONG)
|
|
*
|
|
* Calls the low-level timer. Uses the ulLo and ulHi from the
|
|
* timer's structure and subtracts the current time from the
|
|
* saved time. Uses ReturnElapsedTime (an external ASM proc)
|
|
* to return the elapsed time taking into account the clock
|
|
* frequency and the units for this timer. Each call to this
|
|
* returns the time from the previous TimerInit.
|
|
*
|
|
* The user should interpret the return value sensibly to check
|
|
* if the result is an error or a real value.
|
|
*/
|
|
|
|
ULONG
|
|
TimerRead (
|
|
SHORT hTimerHandle
|
|
)
|
|
{
|
|
NTSTATUS NtStatus;
|
|
LARGE_INTEGER ElapsedTime, CountPrev, LargeOverhead;
|
|
|
|
if ((hTimerHandle > MAX_TIMERS - 1)
|
|
|| (pTimer [hTimerHandle].TUnits == TIMER_FREE))
|
|
/* this timer has not been opened or does not exist.
|
|
Return TIMERERR_OVERFLOW ie. 0xffffffff, the max. possible
|
|
ULONG. The user should interpret such a result sensibly */
|
|
return (TIMERERR_OVERFLOW);
|
|
|
|
NtStatus = NtQueryPerformanceCounter (&CountCurrent, NULL);
|
|
CountPrev.LowPart = pTimer [hTimerHandle].ulLo;
|
|
CountPrev.HighPart = (LONG) pTimer [hTimerHandle].ulHi;
|
|
ElapsedTime.LowPart = CountCurrent.LowPart;
|
|
ElapsedTime.HighPart = (LONG) CountCurrent.HighPart;
|
|
/* everything is just fine, convert to double, subtract the times,
|
|
divide by the frequency, convert to MICROSECONDS and return
|
|
the elapsed time as a ULONG */
|
|
/* convert to us., divide the count by the clock frequency that
|
|
has already been obtained */
|
|
|
|
ElapsedTime = RtlLargeIntegerSubtract (ElapsedTime, CountPrev);
|
|
|
|
ElapsedTime = RtlExtendedIntegerMultiply (ElapsedTime, MICROSEC_FACTOR);
|
|
|
|
ElapsedTime = RtlExtendedLargeIntegerDivide (ElapsedTime,
|
|
PerfFreq.LowPart,
|
|
NULL);
|
|
|
|
// if the timer is not calibrated, set ulElapsedTime to be the
|
|
// low part of ElapsedTime. This is because, we do not have to
|
|
// do to any arithmetic to this before returning the value.
|
|
|
|
if (!bCalibrated)
|
|
ulElapsedTime = ElapsedTime.LowPart;
|
|
|
|
/* this code is common for all platforms but OS2386. For Win3.x
|
|
if VTD.386 has been installed, the code below should not matter,
|
|
since we should have returned the time by now.
|
|
|
|
The elapsed time will be scaled, overhead subtracted
|
|
and the time returned */
|
|
|
|
/* we have ulElapsedTime. Scale it and do the needful */
|
|
/* divide or multiply by the scale factor */
|
|
|
|
if (bCalibrated) {
|
|
// Applications like the PROBE call TimerRead repeatedly
|
|
// without calling TimerInit, for more than 70 minutes. This
|
|
// screws up things. So treat everything as 64 bit numbers
|
|
// until the very end.
|
|
|
|
if ((ElapsedTime.LowPart < ulTimerOverhead) &&
|
|
(!ElapsedTime.HighPart)) { // low part is lower than overhead
|
|
// and high part is zero..then make
|
|
// elapsed time 0. We don't want
|
|
// negative numbers.
|
|
ElapsedTime.HighPart = 0L;
|
|
ElapsedTime.LowPart = 0L;
|
|
}
|
|
|
|
else { // subtract the overhead in tics before converting
|
|
// to time units
|
|
LargeOverhead.HighPart = 0L;
|
|
LargeOverhead.LowPart = ulTimerOverhead;
|
|
|
|
ElapsedTime = RtlLargeIntegerSubtract (ElapsedTime,
|
|
LargeOverhead);
|
|
}
|
|
|
|
|
|
if (pTimer [hTimerHandle].TUnits <= MICROSECONDS) {
|
|
|
|
ElapsedTime = RtlExtendedLargeIntegerDivide (
|
|
ElapsedTime,
|
|
aScaleValues [pTimer [hTimerHandle].TUnits],
|
|
NULL
|
|
);
|
|
} else {
|
|
ElapsedTime = RtlExtendedIntegerMultiply (
|
|
ElapsedTime,
|
|
aScaleValues [pTimer [hTimerHandle].TUnits]
|
|
);
|
|
}
|
|
|
|
// scaling is done. Now get the time back into 32 bits. This
|
|
// should fit.
|
|
|
|
ulElapsedTime = ElapsedTime.LowPart;
|
|
}
|
|
|
|
if ((LONG) ulElapsedTime < 0L) /* if this guy is -ve, return a 0 */
|
|
return (0L);
|
|
|
|
return (ulElapsedTime);
|
|
}
|
|
|
|
/*
|
|
* Function - TimerClose (EXPORTED)
|
|
* Arguments -
|
|
* (a) SHORT - hTimerHandle
|
|
*
|
|
* Returns - SHORT - 0 if call successful
|
|
* Else, an error code if handle invalid:
|
|
*
|
|
* TIMERERR_INVALID_HANDLE
|
|
*
|
|
* Releases the timer for use by future TimerOpen calls.
|
|
* Resets the elements of the timer structure, setting the
|
|
* Timer's Units element to TIMER_FREE.
|
|
*/
|
|
|
|
SHORT
|
|
TimerClose (
|
|
SHORT hTimerHandle
|
|
)
|
|
{
|
|
if ((hTimerHandle > MAX_TIMERS - 1) ||
|
|
(pTimer [hTimerHandle].TUnits == TIMER_FREE))
|
|
/* error condition, wrong handle */
|
|
return (TIMERERR_INVALID_HANDLE);
|
|
|
|
/* otherwise, set the TimerUnits of this timer to TIMER_FREE,
|
|
reset the other elements to zero and return */
|
|
|
|
pTimer [hTimerHandle].TUnits = TIMER_FREE;
|
|
pTimer [hTimerHandle].ulLo = 0L;
|
|
pTimer [hTimerHandle].ulHi = 0L;
|
|
return (SUCCESS_OK);
|
|
}
|
|
|
|
/*******************************************************************
|
|
|
|
Added this routine TimerReport to report individual
|
|
times. Bob Day requested that such a routine be
|
|
created. It just maintains the time from the last
|
|
TimerInit and TimerRead and also the last time returned.
|
|
This routine copies this to a user specified buffer.
|
|
|
|
Accepts - PSZ - a pointer to a buffer to print the data out
|
|
SHORT - timer handle
|
|
|
|
Returns - TRUE if Timer exists and is open
|
|
- FALSE if Timer not opened
|
|
|
|
*******************************************************************/
|
|
|
|
BOOL
|
|
FAR
|
|
PASCAL
|
|
TimerReport (
|
|
PSZ pszReportString,
|
|
SHORT hTimerHandle
|
|
)
|
|
{
|
|
if (pTimer [hTimerHandle].TUnits == TIMER_FREE)
|
|
return (FALSE);
|
|
|
|
/* stored value is in pTimer[hTimerHandle].ulLo and .ulHi */
|
|
/*
|
|
wsprintf (pszReportString,
|
|
"Init Count (tics) %lu:%lu Current Count (tics) %lu:%lu Returned Time %lu ",
|
|
pTimer [hTimerHandle].ulHi,
|
|
pTimer [hTimerHandle].ulLo, CountCurrent.HighPart,
|
|
CountCurrent.LowPart,
|
|
ulElapsedTime);
|
|
*/
|
|
return (TRUE);
|
|
}
|
|
|
|
/*******************************************************************
|
|
|
|
Added this routine TimerQueryPerformanceCounter to report
|
|
current tic count at behest of NT GDI folks.
|
|
|
|
|
|
Accepts - PQWORD - a pointer to a 64 bit struct. that will
|
|
contain tic count on return.
|
|
|
|
PQWORD [OPTIONAL) - a pointer to a 64 bit struct. that will
|
|
contain frequency on return.
|
|
|
|
Returns - None.
|
|
|
|
*******************************************************************/
|
|
|
|
VOID
|
|
FAR
|
|
PASCAL
|
|
TimerQueryPerformanceCounter (
|
|
PQWORD pqTic,
|
|
PQWORD pqFreq OPTIONAL
|
|
)
|
|
{
|
|
|
|
LARGE_INTEGER TempTic, TempFreq;
|
|
|
|
// call the NT API to do the needful and return.
|
|
NtQueryPerformanceCounter (&TempTic, &TempFreq);
|
|
pqTic->LowPart = TempTic.LowPart;
|
|
pqTic->HighPart = TempTic.HighPart;
|
|
pqFreq->LowPart = TempFreq.LowPart;
|
|
pqFreq->HighPart = TempFreq.HighPart;
|
|
|
|
return;
|
|
}
|
|
|
|
/*******************************************************************
|
|
|
|
Added this routine TimerConvertTicsToUSec to return
|
|
time in usecs. for a given elapsed tic count and freq.
|
|
|
|
|
|
Accepts - ULONG - Elapsed Tic Count.
|
|
|
|
ULONG - Frequency.
|
|
|
|
Returns - Elapsed Time in usecs. as a ULONG.
|
|
- Zero if input freq. is zero.
|
|
|
|
*******************************************************************/
|
|
|
|
ULONG
|
|
TimerConvertTicsToUSec (
|
|
ULONG ulElapsedTics,
|
|
ULONG ulInputFreq
|
|
)
|
|
{
|
|
|
|
LARGE_INTEGER ElapsedTime;
|
|
ULONG ulRemainder = 0L;
|
|
|
|
// if the person gives me a zero freq, return him a zero.
|
|
// Let him tear his hair.
|
|
|
|
if (!ulInputFreq)
|
|
return 0L;
|
|
|
|
// multiply tics by a million and divide by the frequency.
|
|
|
|
ElapsedTime = RtlEnlargedIntegerMultiply (ulElapsedTics, MICROSEC_FACTOR);
|
|
|
|
ElapsedTime = RtlExtendedLargeIntegerDivide (ElapsedTime,
|
|
ulInputFreq,
|
|
&ulRemainder);
|
|
|
|
ElapsedTime.LowPart += (ulRemainder > (ulInputFreq / 2L));
|
|
|
|
return (ElapsedTime.LowPart) ; /* get the result into a ULONG */
|
|
}
|
|
|
|
/**************** ROUTINES NOT EXPORTED, FOLLOW ************************/
|
|
|
|
/*
|
|
* Function - CalibrateTimerForOverhead (NOT EXPORTED)
|
|
* Arguments - None
|
|
* Returns - ULONG
|
|
*
|
|
* Calls TimerElapsedTime a few times to compute the expected
|
|
* mean. Calls TimerElapsedTime more number of times and
|
|
* averages the mean out of those calls that did not exceed
|
|
* the expected mean by 15%.
|
|
*/
|
|
|
|
ULONG
|
|
CalibrateTimerForOverhead (VOID)
|
|
{
|
|
ULONG ulOverhead [ITER_FOR_OVERHEAD];
|
|
ULONG ulTempTotal = 0L;
|
|
ULONG ulExpectedValue = 0L;
|
|
SHORT csIter;
|
|
SHORT csNoOfSamples = ITER_FOR_OVERHEAD;
|
|
SHORT hTimerHandle;
|
|
|
|
if (TimerOpen (&hTimerHandle, MICROSECONDS)) /* open failed. Return 0 */
|
|
return (0L);
|
|
|
|
for (csIter = 0; csIter < 5; csIter++) {
|
|
TimerInit (hTimerHandle);
|
|
ulOverhead [csIter] = TimerRead (hTimerHandle);
|
|
/* if negative, make zero */
|
|
if (((LONG) ulOverhead [csIter]) < 0)
|
|
ulOverhead [csIter] = 0L;
|
|
}
|
|
/* The get elapsed time function has been called 6 times.
|
|
The idea is to calculate the expected mean, then call
|
|
TimerElapsedTime a bunch of times and throw away all times
|
|
that are 15% larger than this mean. This would give a
|
|
really good overhead time */
|
|
|
|
for (csIter = 0; csIter < 5; csIter++ )
|
|
ulTempTotal += ulOverhead [csIter];
|
|
|
|
ulExpectedValue = ulTempTotal / 5;
|
|
|
|
for (csIter = 0; csIter < ITER_FOR_OVERHEAD; csIter++) {
|
|
TimerInit (hTimerHandle);
|
|
ulOverhead [csIter] = TimerRead (hTimerHandle);
|
|
/* if negative, make zero */
|
|
if (((LONG) ulOverhead [csIter]) < 0)
|
|
ulOverhead [csIter] = 0L;
|
|
}
|
|
|
|
ulTempTotal = 0L; /* reset this value */
|
|
for (csIter = 0; csIter < ITER_FOR_OVERHEAD; csIter++ ) {
|
|
if (ulOverhead [csIter] <= (ULONG) (115L * ulExpectedValue/100L))
|
|
/* include all samples that is < 115% of ulExpectedValue */
|
|
ulTempTotal += ulOverhead [csIter];
|
|
else
|
|
/* ignore this sample and dec. sample count */
|
|
csNoOfSamples--;
|
|
}
|
|
TimerClose (hTimerHandle);
|
|
|
|
if (csNoOfSamples == 0) /* no valid times. Return a 0 for overhead */
|
|
return (0L);
|
|
|
|
return (ulTempTotal/csNoOfSamples);
|
|
}
|
|
|
|
/*
|
|
* Function - GetTimerFreq (NOT EXPORTED)
|
|
*
|
|
* Arguments - None
|
|
*
|
|
*
|
|
* Return - 0 if successful or an error code if timer not aailable
|
|
*
|
|
* Calls the function to return freq
|
|
*
|
|
*/
|
|
SHORT
|
|
GetTimerFreq (VOID)
|
|
{
|
|
LARGE_INTEGER PerfCount, Freq;
|
|
NTSTATUS NtStatus;
|
|
|
|
NtStatus = NtQueryPerformanceCounter (&PerfCount, &Freq);
|
|
|
|
if ((Freq.LowPart == 0L) && (Freq.HighPart == 0L))
|
|
/* frequency of zero implies timer not available */
|
|
return (TIMERERR_NOT_AVAILABLE);
|
|
|
|
PerfFreq.LowPart = Freq.LowPart;
|
|
PerfFreq.HighPart = (LONG) Freq.HighPart;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/***************************************************
|
|
|
|
NT native dll init routine
|
|
|
|
****************************************************/
|
|
SHORT csTempCtr; /* a counter - had to make this global..compile fails */
|
|
ULONG culTemp; /* - do - */
|
|
|
|
NTSTATUS
|
|
TimerDllInitialize (
|
|
IN PVOID DllHandle,
|
|
ULONG Reason,
|
|
IN PCONTEXT Context OPTIONAL
|
|
)
|
|
{
|
|
DllHandle, Context; // avoid compiler warnings
|
|
|
|
if (Reason != DLL_PROCESS_ATTACH) { // if detaching return immediately
|
|
return TRUE;
|
|
}
|
|
|
|
for (csTempCtr = 0; csTempCtr < MAX_TIMERS; csTempCtr++) {
|
|
pTimer [csTempCtr].ulLo = 0L;
|
|
pTimer [csTempCtr].ulHi = 0L;
|
|
pTimer [csTempCtr].TUnits = TIMER_FREE;
|
|
}
|
|
|
|
bTimerInit = TRUE;
|
|
GetTimerFreq ();
|
|
ulTimerOverhead = CalibrateTimerForOverhead ();
|
|
/* the timer overhead will be placed in a global variable */
|
|
bCalibrated = TRUE;
|
|
return TRUE;
|
|
|
|
}
|