reSIProcate/rutil  9694
Time.cxx
Go to the documentation of this file.
00001 #include "Time.hxx"
00002 #if defined(WIN32)
00003 #define WIN32_LEAN_AND_MEAN             // Exclude rarely-used stuff from Windows headers
00004 #include <windows.h>
00005 #include <mmsystem.h>
00006 #if (_MSC_VER >= 1400) //no atomic intrinsics on Visual Studio 2003 and below
00007 #include <intrin.h>
00008 #pragma intrinsic(_InterlockedCompareExchange64)
00009 #endif
00010 #endif
00011 #include "rutil/Random.hxx"
00012 #include "rutil/Logger.hxx"
00013 #include "rutil/Lock.hxx"
00014 
00015 #define RESIPROCATE_SUBSYSTEM Subsystem::SIP
00016 
00017 using namespace resip;
00018 
00019 ResipClock::ResipClock(void)
00020 {
00021 }
00022 
00023 ResipClock::~ResipClock(void)
00024 {
00025 }
00026 
00027 #if defined(WIN32) && defined(_RESIP_MONOTONIC_CLOCK)
00028 unsigned ResipClock::mMaxSystemTimeWaitMs = 60000; //set low initially, may get adjusted by actual timer chosen
00029 #else
00030 unsigned ResipClock::mMaxSystemTimeWaitMs = UINT_MAX;
00031 #endif
00032 
00033 #ifdef WIN32
00034 ResipClock::WinMonoClock::PGTC64 ResipClock::WinMonoClock::mGTC64 = &ResipClock::WinMonoClock::GTCLockDuringRange::GTC64;
00035 
00036 ResipClock::WinMonoClock::WinMonoClock()
00037 {
00038    Initialize();
00039 }
00040 
00041 void 
00042 ResipClock::WinMonoClock::Initialize(void)
00043 {
00044    static Mutex mtxStart;
00045    static volatile bool isInitialized=false;    
00046 
00047    Lock lock(mtxStart);
00048 
00049    if (isInitialized)
00050    {
00051       return;
00052    }
00053  
00054 #if defined(_MSC_VER) && (_MSC_VER >= 1400)
00055    ResipClock::mMaxSystemTimeWaitMs  = GTCInterlocked::GetMaxWaitMs();
00056    mGTC64 = (PGTC64)&GTCInterlocked::GTC64;
00057    DebugLog(<< "Using GTCInterlocked::GTC64 as the monotonic clock for time functions.");
00058 #else                
00059    ResipClock::mMaxSystemTimeWaitMs  = GTCLockDuringRange::GetMaxWaitMs();
00060    mGTC64 = (PGTC64)&GTCLockDuringRange::GTC64;
00061    DebugLog(<< "Using GTCLockDuringRange::GTC64 as the monotonic clock for time functions.");
00062 #endif 
00063 
00064    unsigned min=0,max=0,actual=0;
00065    bool isMono = false;
00066 
00067    ResipClock::queryTimerInfo(min,max,actual,isMono);
00068 
00069    InfoLog(<< "Timer resolution: (min/max/actual/isMonotonic) = " << min << '/' << max << '/' << actual <<
00070       '/' << ((isMono)?("true"):("false")));
00071 
00072    isInitialized = true;
00073 }
00074 
00075 #define IS_ALIGNED(_pointer, _alignment) ((((ULONG_PTR) (_pointer)) & ((_alignment) - 1)) == 0)
00076 
00077 UInt64 ResipClock::WinMonoClock::GTCInterlocked::GTC64(void)
00078 {
00079 #if defined(_MSC_VER) && (_MSC_VER >= 1400) //no atomic intrinsics on Visual Studio 2003 and below
00080    ULARGE_INTEGER timeVal;
00081 
00082    assert(IS_ALIGNED(&mBaseTime,8)); //if the implementation ever changes to use 64-bit atomic read/write then 64-bit alignment will be required.
00083 
00084    //InterlockedCompareExchange64 will issue a LOCK CMPXCHG8B to ensure atomic access to mBaseTime
00085    //Not the most efficient wat to do a 64-bit atomic read (see fild instruction), but no intrinsic for 64-bit atomic read.
00086    timeVal.QuadPart = _InterlockedCompareExchange64((LONGLONG volatile *)&mBaseTime,0,0);
00087 
00088    DWORD tickNow = ::timeGetTime();
00089 
00090    if (tickNow != timeVal.LowPart)
00091    {
00092       //the difference in the low 32-bits and the current 32-bit timeGetTime will be the time difference from
00093       //the base time till now.  Integer arithmentic will correctly handle cases where tickNow < timeVal.LowPart (rollover).
00094       //This diff cannot be greater than 0xFFFFFFFF, so this function must be called more frequently than once
00095       //every 49.7 days.
00096 
00097       DWORD diff = tickNow - timeVal.LowPart;
00098 
00099       //periodically update the basetime, only really necessary at least once every 49.7 days.
00100       if (diff > mBaseTimeUpdateInterval)
00101       {
00102          ULARGE_INTEGER newVal;
00103 
00104          newVal.QuadPart = timeVal.QuadPart + diff; //any 32-bit rollover is now part of the high 32-bits.
00105 
00106          //don't care if this CAS64 actually writes mBaseTime, as long as at least 1 thread updates mBaseTime.
00107          _InterlockedCompareExchange64((LONGLONG volatile *)&mBaseTime,(LONGLONG)newVal.QuadPart,(LONGLONG)timeVal.QuadPart);
00108       }
00109 
00110       timeVal.QuadPart += diff; //any 32-bit rollover is now part of the high 32-bits.
00111    }
00112 
00113    return timeVal.QuadPart;
00114 #else
00115    assert(0); //this counter only compiles on Visual Studio 2005 +
00116    return GTCLock::GTC64();
00117 #endif //#if (_MSC_VER >= 1400) //no atomic intrinsics on Visual Studio 2003 and below
00118 }
00119 
00120 volatile UInt64 ResipClock::WinMonoClock::GTCInterlocked::mBaseTime = 0;
00121 
00122 UInt64
00123 ResipClock::WinMonoClock::GTCLock::GTC64(void)
00124 {
00125    Lock lock(mMutex);
00126 
00127    DWORD tickNow = ::timeGetTime();
00128 
00129    if (tickNow != mBaseTime.LowPart)
00130    {
00131       mBaseTime.QuadPart += (tickNow - mBaseTime.LowPart);
00132    }
00133 
00134    return mBaseTime.QuadPart;
00135 }
00136 
00137 ULARGE_INTEGER ResipClock::WinMonoClock::GTCLock::mBaseTime = {0,0};
00138 Mutex ResipClock::WinMonoClock::GTCLock::mMutex;
00139 
00140 UInt64 ResipClock::WinMonoClock::GTCLockDuringRange::GTC64(void)
00141 {
00142    // we assume that this function is called reasonable often
00143    // monitor wrap around only in dangerous time range:
00144    // empiric constants
00145    const DWORD TIMER_BEGIN_SAFE_RANGE = 0xffff; // about one minute after
00146    const DWORD TIMER_END_SAFE_RANGE = 0xffff0000; // and before wrap around
00147   
00148    DWORD tick = ::timeGetTime();
00149 
00150    if ( ( tick > TIMER_BEGIN_SAFE_RANGE ) && ( tick < TIMER_END_SAFE_RANGE ) )
00151    {
00152       LARGE_INTEGER ret;
00153       ret.HighPart = mWrapCounter;
00154       ret.LowPart = tick;
00155       return (UInt64)ret.QuadPart;
00156    }
00157    // most application will never be here - only long running servers
00158    Lock lock(mWrapCounterMutex);
00159    if (mPrevTick > tick)
00160    {
00161       mWrapCounter++;
00162    }
00163    mPrevTick = tick;
00164    LARGE_INTEGER ret;
00165    ret.HighPart = mWrapCounter;
00166    ret.LowPart = tick;
00167    return (UInt64)ret.QuadPart;
00168 }
00169 
00170 UInt32   ResipClock::WinMonoClock::GTCLockDuringRange::mWrapCounter = 0;
00171 DWORD    ResipClock::WinMonoClock::GTCLockDuringRange::mPrevTick = 0;
00172 Mutex    ResipClock::WinMonoClock::GTCLockDuringRange::mWrapCounterMutex;
00173 #endif
00174 
00175 UInt64
00176 ResipClock::getSystemTime()
00177 {
00178    assert(sizeof(UInt64) == 64/8);
00179 
00180 #if defined(WIN32) || defined(UNDER_CE)
00181 #ifdef _RESIP_MONOTONIC_CLOCK
00182    static ResipClock::WinMonoClock clockInit;
00183    return WinMonoClock::GetClock64() * 1000;
00184 #else
00185    FILETIME ft;
00186 
00187 #ifdef UNDER_CE
00188    SYSTEMTIME st;
00189    ::GetSystemTime(&st);
00190    ::SystemTimeToFileTime(&st,&ft);
00191 #else
00192    ::GetSystemTimeAsFileTime(&ft);
00193 #endif
00194 
00195    ULARGE_INTEGER li;
00196    li.LowPart = ft.dwLowDateTime;
00197    li.HighPart = ft.dwHighDateTime;
00198    return li.QuadPart/10;
00199 
00200 #endif //_RESIP_MONOTONIC_CLOCK
00201 
00202 #else //#if defined(WIN32) || defined(UNDER_CE)
00203 
00204    UInt64 time=0;
00205 #ifdef _RESIP_MONOTONIC_CLOCK
00206    struct timespec now_monotonic;
00207    if (clock_gettime( CLOCK_MONOTONIC, &now_monotonic ) == 0)
00208 //   if ( syscall( __NR_clock_gettime, CLOCK_MONOTONIC, &now_monotonic ) == 0 )
00209    {
00210       time = now_monotonic.tv_sec;
00211       time = time*1000000;
00212       time += now_monotonic.tv_nsec/1000;
00213       return time;
00214    }
00215 #endif
00216    struct timeval now;
00217    gettimeofday( &now , NULL );
00218    //assert( now );
00219    time = now.tv_sec;
00220    time = time*1000000;
00221    time += now.tv_usec;
00222    return time;
00223 #endif
00224 }
00225 
00226 UInt64
00227 ResipClock::getForever()
00228 {
00229    assert( sizeof(UInt64) == 8 );
00230 #if defined(WIN32) && !defined(__GNUC__)
00231    return 18446744073709551615ui64;
00232 #else
00233    return 18446744073709551615ULL;
00234 #endif
00235 }
00236 
00237 UInt64
00238 ResipClock::getRandomFutureTimeMs( UInt64 futureMs )
00239 {
00240    UInt64 now = getTimeMs();
00241 
00242    // make r a random number between 5000 and 9000
00243    int r = Random::getRandom()%4000;
00244    r += 5000;
00245 
00246    UInt64 ret = now;
00247    ret += (futureMs*r)/10000;
00248 
00249    assert( ret >= now );
00250    assert( ret >= now+(futureMs/2) );
00251    assert( ret <= now+futureMs );
00252 
00253    return ret;
00254 }
00255 
00256 void 
00257 ResipClock::queryTimerInfo(unsigned &min, unsigned &max, unsigned &actual, bool &isMonotonic)
00258 {  
00259    min = max = actual = 0;
00260    isMonotonic = false;
00261 
00262 #if defined(WIN32) 
00263 #if defined(_RESIP_MONOTONIC_CLOCK)
00264 #if !defined(NTSTATUS)
00265 #define NTSTATUS DWORD
00266 #endif
00267    typedef NTSTATUS (WINAPI*PNTQTR)(PULONG,PULONG,PULONG);  
00268         
00269         HMODULE hm = ::LoadLibrary("ntdll");
00270         
00271    if (hm != NULL)
00272    {
00273       PNTQTR ntqtr = (PNTQTR)::GetProcAddress(hm,"NtQueryTimerResolution");
00274 
00275       if (ntqtr)
00276       {
00277          ntqtr((PULONG)&min,(PULONG)&max,(PULONG)&actual);
00278          min /= 10;
00279          max /= 10;
00280          actual /= 10;
00281       }
00282    
00283       ::FreeLibrary(hm);
00284       hm = NULL;
00285    }   
00286    isMonotonic = true;
00287 #else
00288    DWORD timeAdjustment=0;
00289         BOOL timeAdjustmentDisabled=0;
00290    //on Vista it looks like GetSystemTime has 1ms resolution, but GetSystemTimeAdjustment still returns 15ms
00291    //so just set the min resolution to whatever is reported, no actual resolution can be consistently found.
00292    ::GetSystemTimeAdjustment(&timeAdjustment,(PDWORD)&min,&timeAdjustmentDisabled);
00293    min /= 10;
00294    isMonotonic = false;   
00295 #endif
00296 #else //WIN32
00297 #ifdef __APPLE__
00298    //@TODO 
00299 #else
00300    clockid_t clock = CLOCK_REALTIME; //need to test/verify CLOCK_REALTIME returns the gettimeofday resolution.  
00301 #if defined(_RESIP_MONOTONIC_CLOCK)
00302    clock = CLOCK_MONOTONIC;
00303    isMonotonic = true;
00304 #endif
00305    struct timespec res;
00306    if (clock_getres(clock,&res) == 0)
00307    {
00308       actual = (res.tv_sec * 1000000) + (res.tv_nsec / 1000);
00309    } 
00310 #endif
00311 #endif
00312 }
00313 
00314 /* ====================================================================
00315  * The Vovida Software License, Version 1.0
00316  *
00317  * Copyright (c) 2000 Vovida Networks, Inc.  All rights reserved.
00318  *
00319  * Redistribution and use in source and binary forms, with or without
00320  * modification, are permitted provided that the following conditions
00321  * are met:
00322  *
00323  * 1. Redistributions of source code must retain the above copyright
00324  *    notice, this list of conditions and the following disclaimer.
00325  *
00326  * 2. Redistributions in binary form must reproduce the above copyright
00327  *    notice, this list of conditions and the following disclaimer in
00328  *    the documentation and/or other materials provided with the
00329  *    distribution.
00330  *
00331  * 3. The names "VOCAL", "Vovida Open Communication Application Library",
00332  *    and "Vovida Open Communication Application Library (VOCAL)" must
00333  *    not be used to endorse or promote products derived from this
00334  *    software without prior written permission. For written
00335  *    permission, please contact vocal@vovida.org.
00336  *
00337  * 4. Products derived from this software may not be called "VOCAL", nor
00338  *    may "VOCAL" appear in their name, without prior written
00339  *    permission of Vovida Networks, Inc.
00340  *
00341  * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
00342  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00343  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
00344  * NON-INFRINGEMENT ARE DISCLAIMED.  IN NO EVENT SHALL VOVIDA
00345  * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
00346  * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
00347  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
00348  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
00349  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
00350  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00351  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
00352  * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
00353  * DAMAGE.
00354  *
00355  * ====================================================================
00356  *
00357  * This software consists of voluntary contributions made by Vovida
00358  * Networks, Inc. and many individuals on behalf of Vovida Networks,
00359  * Inc.  For more information on Vovida Networks, Inc., please see
00360  * <http://www.vovida.org/>.
00361  *
00362  * vi: set shiftwidth=3 expandtab:
00363  */
00364