353 lines
6.8 KiB
C
353 lines
6.8 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1995-2000 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
timer.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
Domain Name System (DNS) Server
|
|||
|
|
|||
|
Wrap proof timer routines.
|
|||
|
|
|||
|
The purpose of this module is to create a timer function which
|
|||
|
returns a time in seconds and eliminates all timer wrapping issues.
|
|||
|
|
|||
|
These routines are non-DNS specific and may be picked up
|
|||
|
cleanly by any module.
|
|||
|
|
|||
|
For DNS the added instructions are well worth the cost in that it
|
|||
|
eliminates any issue involving cleaning packet queues or resetting
|
|||
|
cache timeouts when millisecond timer (GetCurrentTime) wraps.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Jim Gilroy (jamesg) 9-Sep-1995
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
|
|||
|
#include "local.h"
|
|||
|
|
|||
|
// Note: this modules requires only windows.h.
|
|||
|
// local.h is included only to allow precompiled header
|
|||
|
|
|||
|
#include <windows.h>
|
|||
|
|
|||
|
|
|||
|
#if 1
|
|||
|
|
|||
|
//
|
|||
|
// GetTickCount() timer routines
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// Timer globals
|
|||
|
//
|
|||
|
|
|||
|
BOOL g_InitializedTimerCs = FALSE;
|
|||
|
BOOL g_TimerInitInProgress = FALSE;
|
|||
|
CRITICAL_SECTION csTimerWrap;
|
|||
|
|
|||
|
DWORD g_WrapTime = 0;
|
|||
|
DWORD g_PreviousTopBit = 0;
|
|||
|
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
Dns_InitializeSecondsTimer(
|
|||
|
VOID
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Initialize DNS timer.
|
|||
|
|
|||
|
This will be done automatically, but allow caller to do it explicitly.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
//
|
|||
|
// protect CS init with interlock
|
|||
|
// - first thread through does CS init
|
|||
|
// - any others racing, are not released until init
|
|||
|
// completes
|
|||
|
//
|
|||
|
|
|||
|
if ( !g_InitializedTimerCs )
|
|||
|
{
|
|||
|
if ( InterlockedIncrement( &g_TimerInitInProgress ) == 1 )
|
|||
|
{
|
|||
|
InitializeCriticalSection( &csTimerWrap );
|
|||
|
g_InitializedTimerCs = TRUE;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
while ( !g_InitializedTimerCs )
|
|||
|
{
|
|||
|
Sleep( 10 );
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
Dns_GetCurrentTimeInSeconds(
|
|||
|
VOID
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Get current time in seconds.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Time since boot in seconds.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
DWORD currentTime;
|
|||
|
DWORD topBit;
|
|||
|
DWORD preWrapTime;
|
|||
|
DWORD postWrapTime;
|
|||
|
|
|||
|
//
|
|||
|
// get time
|
|||
|
//
|
|||
|
// read wrap time on either side so we can detect and handle
|
|||
|
// a wrap occuring (handled by another thread) while we are
|
|||
|
// in this function
|
|||
|
//
|
|||
|
|
|||
|
preWrapTime = g_WrapTime;
|
|||
|
|
|||
|
currentTime = GetCurrentTime();
|
|||
|
|
|||
|
postWrapTime = g_WrapTime;
|
|||
|
|
|||
|
//
|
|||
|
// check for timer wrap
|
|||
|
//
|
|||
|
// need to detect when timer flips from large to small DWORD;
|
|||
|
//
|
|||
|
// i first did this by keeping a previous time global, but
|
|||
|
// setting this global must also be carefully locked around timer
|
|||
|
// wrap to avoid race conditions resulting in double wrap
|
|||
|
//
|
|||
|
// to avoid locking all the time we can set previous time only
|
|||
|
// when it "substantively" changes for our purposes -- this is
|
|||
|
// when it changes its top bit; by saving it twice a wrap
|
|||
|
// we have enough info to detect the wrap (the change from
|
|||
|
// top bit set to clear), yet still only need to lock a few
|
|||
|
// times every wrap
|
|||
|
//
|
|||
|
// algorithm:
|
|||
|
// - top bit same as previous => done
|
|||
|
// - top bit changed
|
|||
|
// - take lock
|
|||
|
// - test again
|
|||
|
// - no change => no-op
|
|||
|
// - changed to top bit set
|
|||
|
// - just save new bit setting
|
|||
|
// - changed to top bit clear
|
|||
|
// - save new bit setting
|
|||
|
// - add one cycle to wrap time
|
|||
|
//
|
|||
|
|
|||
|
topBit = currentTime & 0x80000000;
|
|||
|
|
|||
|
if ( topBit != g_PreviousTopBit )
|
|||
|
{
|
|||
|
//
|
|||
|
// possible wrap or "half-wrap"
|
|||
|
//
|
|||
|
// not intializing lock until actually need it
|
|||
|
// - lock init is MT safe (see above)
|
|||
|
//
|
|||
|
|
|||
|
Dns_InitializeSecondsTimer();
|
|||
|
|
|||
|
EnterCriticalSection( &csTimerWrap );
|
|||
|
|
|||
|
//
|
|||
|
// timer wrap
|
|||
|
// - recheck inequality as another thread might have beaten
|
|||
|
// us to the lock and handled wrap already
|
|||
|
// - topBit must be clear (time is now low DWORD)
|
|||
|
//
|
|||
|
|
|||
|
if ( topBit != g_PreviousTopBit && topBit == 0 )
|
|||
|
{
|
|||
|
g_WrapTime += (MAXDWORD / 1000);
|
|||
|
}
|
|||
|
|
|||
|
// reset previous top bit
|
|||
|
// - not necessary in equality case, but a no-op
|
|||
|
|
|||
|
g_PreviousTopBit = topBit;
|
|||
|
|
|||
|
LeaveCriticalSection( &csTimerWrap );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// return time
|
|||
|
// - current time + any wrap time
|
|||
|
// - if pre\post wrap times use topBit to determine which is valid
|
|||
|
// - if our time was snapshot right before wrap, use pre time
|
|||
|
// - otherwise post time ok
|
|||
|
//
|
|||
|
// note this is done completely without globals, so no race
|
|||
|
//
|
|||
|
|
|||
|
if ( preWrapTime != postWrapTime )
|
|||
|
{
|
|||
|
if ( topBit )
|
|||
|
{
|
|||
|
postWrapTime = preWrapTime;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return ( currentTime / 1000 + postWrapTime );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
#else
|
|||
|
//
|
|||
|
// FILETIME timer routines
|
|||
|
//
|
|||
|
// Unfortunately these don't work because FILETIME moves
|
|||
|
// around when clock reset -- it is not monotonically increasing
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// Timer globals
|
|||
|
//
|
|||
|
|
|||
|
LONGLONG g_TimerBaseTime = 0;
|
|||
|
|
|||
|
//
|
|||
|
// File time timer in 100ns intervals
|
|||
|
// (10 million to second)
|
|||
|
//
|
|||
|
|
|||
|
#define FILE_TIME_INTERVALS_IN_SECOND (10000000)
|
|||
|
|
|||
|
//
|
|||
|
// File time base to avoid starting timer at zero
|
|||
|
// Give roughly a day to avoid any startup issues.
|
|||
|
//
|
|||
|
|
|||
|
#define FILE_TIME_BASE_OFFSET (1000000000000)
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
Dns_GetCurrentTimeInSeconds(
|
|||
|
VOID
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Get current time in seconds.
|
|||
|
Time is relative to first call to the timer.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Time since first timer call in seconds.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
LONGLONG time64;
|
|||
|
|
|||
|
GetSystemTimeAsFileTime( (PFILETIME) &time64 );
|
|||
|
|
|||
|
//
|
|||
|
// convert to seconds
|
|||
|
// - file time is in 100ns intervals (since Jan 1, 1601)
|
|||
|
//
|
|||
|
// if first call, save 64-bit base time;
|
|||
|
// this allows us to run a DWORD of seconds ~137 years
|
|||
|
//
|
|||
|
// repeated calls are offset from base time
|
|||
|
//
|
|||
|
|
|||
|
if ( g_TimerBaseTime == 0 )
|
|||
|
{
|
|||
|
g_TimerBaseTime = time64 - FILE_TIME_BASE_OFFSET;
|
|||
|
}
|
|||
|
|
|||
|
time64 -= g_TimerBaseTime;
|
|||
|
time64 = time64 / FILE_TIME_INTERVALS_IN_SECOND;
|
|||
|
|
|||
|
return (DWORD)time64;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
Dns_InitializeSecondsTimer(
|
|||
|
VOID
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Initialize DNS timer.
|
|||
|
|
|||
|
Note, this is not a reset -- it's just backward compatibility
|
|||
|
for old timer routines.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
//
|
|||
|
// call the timer, for uninitialized timer that makes time
|
|||
|
// now
|
|||
|
|
|||
|
Dns_GetCurrentTimeInSeconds();
|
|||
|
|
|||
|
//
|
|||
|
// note if want a timer reset, then zero base, however
|
|||
|
// this is NOT MT safe -- thread in function could get
|
|||
|
// huge bogus time
|
|||
|
//
|
|||
|
}
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
// End of timer.c
|
|||
|
//
|