|
reSIProcate/rutil
9694
|
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)>CInterlocked::GTC64; 00057 DebugLog(<< "Using GTCInterlocked::GTC64 as the monotonic clock for time functions."); 00058 #else 00059 ResipClock::mMaxSystemTimeWaitMs = GTCLockDuringRange::GetMaxWaitMs(); 00060 mGTC64 = (PGTC64)>CLockDuringRange::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
1.7.5.1