windows-nt/Source/XPSP1/NT/ds/dns/dnslib/timer.c
2020-09-26 16:20:57 +08:00

353 lines
6.8 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
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
//