/src/resiprocate/rutil/Time.cxx
Line | Count | Source (jump to first uncovered line) |
1 | | #include "Time.hxx" |
2 | | #if defined(WIN32) |
3 | | #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers |
4 | | #include <windows.h> |
5 | | #include <mmsystem.h> |
6 | | #if (_MSC_VER >= 1400) //no atomic intrinsics on Visual Studio 2003 and below |
7 | | #include <intrin.h> |
8 | | #pragma intrinsic(_InterlockedCompareExchange64) |
9 | | #endif |
10 | | #endif |
11 | | #include "rutil/Random.hxx" |
12 | | #include "rutil/Logger.hxx" |
13 | | #include "rutil/Lock.hxx" |
14 | | |
15 | | #define RESIPROCATE_SUBSYSTEM Subsystem::SIP |
16 | | |
17 | | using namespace resip; |
18 | | |
19 | | void resip::sleepMs(unsigned int ms) |
20 | 0 | { |
21 | | #ifdef WIN32 |
22 | | Sleep(ms); |
23 | | #else |
24 | 0 | usleep(ms*1000); |
25 | 0 | #endif |
26 | 0 | } |
27 | | |
28 | | void resip::sleepSeconds(unsigned int seconds) |
29 | 0 | { |
30 | | #ifdef WIN32 |
31 | | Sleep(seconds*1000); |
32 | | #else |
33 | 0 | sleep(seconds); |
34 | 0 | #endif |
35 | 0 | } |
36 | | |
37 | | ResipClock::ResipClock(void) |
38 | 0 | { |
39 | 0 | } |
40 | | |
41 | | ResipClock::~ResipClock(void) |
42 | 0 | { |
43 | 0 | } |
44 | | |
45 | | #if defined(WIN32) && defined(_RESIP_MONOTONIC_CLOCK) |
46 | | unsigned ResipClock::mMaxSystemTimeWaitMs = 60000; //set low initially, may get adjusted by actual timer chosen |
47 | | #else |
48 | | unsigned ResipClock::mMaxSystemTimeWaitMs = UINT_MAX; |
49 | | #endif |
50 | | |
51 | | #ifdef WIN32 |
52 | | ResipClock::WinMonoClock::PGTC64 ResipClock::WinMonoClock::mGTC64 = &ResipClock::WinMonoClock::GTCLockDuringRange::GTC64; |
53 | | |
54 | | ResipClock::WinMonoClock::WinMonoClock() |
55 | | { |
56 | | Initialize(); |
57 | | } |
58 | | |
59 | | void |
60 | | ResipClock::WinMonoClock::Initialize(void) |
61 | | { |
62 | | static Mutex mtxStart; |
63 | | static volatile bool isInitialized=false; |
64 | | |
65 | | Lock lock(mtxStart); |
66 | | |
67 | | if (isInitialized) |
68 | | { |
69 | | return; |
70 | | } |
71 | | |
72 | | #if defined(_MSC_VER) && (_MSC_VER >= 1400) |
73 | | ResipClock::mMaxSystemTimeWaitMs = GTCInterlocked::GetMaxWaitMs(); |
74 | | mGTC64 = (PGTC64)>CInterlocked::GTC64; |
75 | | DebugLog(<< "Using GTCInterlocked::GTC64 as the monotonic clock for time functions."); |
76 | | #else |
77 | | ResipClock::mMaxSystemTimeWaitMs = GTCLockDuringRange::GetMaxWaitMs(); |
78 | | mGTC64 = (PGTC64)>CLockDuringRange::GTC64; |
79 | | DebugLog(<< "Using GTCLockDuringRange::GTC64 as the monotonic clock for time functions."); |
80 | | #endif |
81 | | |
82 | | unsigned min=0,max=0,actual=0; |
83 | | bool isMono = false; |
84 | | |
85 | | ResipClock::queryTimerInfo(min,max,actual,isMono); |
86 | | |
87 | | InfoLog(<< "Timer resolution: (min/max/actual/isMonotonic) = " << min << '/' << max << '/' << actual << |
88 | | '/' << ((isMono)?("true"):("false"))); |
89 | | |
90 | | isInitialized = true; |
91 | | } |
92 | | |
93 | | #define IS_ALIGNED(_pointer, _alignment) ((((ULONG_PTR) (_pointer)) & ((_alignment) - 1)) == 0) |
94 | | |
95 | | uint64_t ResipClock::WinMonoClock::GTCInterlocked::GTC64(void) |
96 | | { |
97 | | #if defined(_MSC_VER) && (_MSC_VER >= 1400) //no atomic intrinsics on Visual Studio 2003 and below |
98 | | ULARGE_INTEGER timeVal; |
99 | | |
100 | | resip_assert(IS_ALIGNED(&mBaseTime,8)); //if the implementation ever changes to use 64-bit atomic read/write then 64-bit alignment will be required. |
101 | | |
102 | | //InterlockedCompareExchange64 will issue a LOCK CMPXCHG8B to ensure atomic access to mBaseTime |
103 | | //Not the most efficient wat to do a 64-bit atomic read (see fild instruction), but no intrinsic for 64-bit atomic read. |
104 | | timeVal.QuadPart = _InterlockedCompareExchange64((LONGLONG volatile *)&mBaseTime,0,0); |
105 | | |
106 | | DWORD tickNow = ::timeGetTime(); |
107 | | |
108 | | if (tickNow != timeVal.LowPart) |
109 | | { |
110 | | //the difference in the low 32-bits and the current 32-bit timeGetTime will be the time difference from |
111 | | //the base time till now. Integer arithmentic will correctly handle cases where tickNow < timeVal.LowPart (rollover). |
112 | | //This diff cannot be greater than 0xFFFFFFFF, so this function must be called more frequently than once |
113 | | //every 49.7 days. |
114 | | |
115 | | DWORD diff = tickNow - timeVal.LowPart; |
116 | | |
117 | | //periodically update the basetime, only really necessary at least once every 49.7 days. |
118 | | if (diff > mBaseTimeUpdateInterval) |
119 | | { |
120 | | ULARGE_INTEGER newVal; |
121 | | |
122 | | newVal.QuadPart = timeVal.QuadPart + diff; //any 32-bit rollover is now part of the high 32-bits. |
123 | | |
124 | | //don't care if this CAS64 actually writes mBaseTime, as long as at least 1 thread updates mBaseTime. |
125 | | _InterlockedCompareExchange64((LONGLONG volatile *)&mBaseTime,(LONGLONG)newVal.QuadPart,(LONGLONG)timeVal.QuadPart); |
126 | | } |
127 | | |
128 | | timeVal.QuadPart += diff; //any 32-bit rollover is now part of the high 32-bits. |
129 | | } |
130 | | |
131 | | return timeVal.QuadPart; |
132 | | #else |
133 | | resip_assert(0); //this counter only compiles on Visual Studio 2005 + |
134 | | return GTCLock::GTC64(); |
135 | | #endif //#if (_MSC_VER >= 1400) //no atomic intrinsics on Visual Studio 2003 and below |
136 | | } |
137 | | |
138 | | volatile uint64_t ResipClock::WinMonoClock::GTCInterlocked::mBaseTime = 0; |
139 | | |
140 | | uint64_t |
141 | | ResipClock::WinMonoClock::GTCLock::GTC64(void) |
142 | | { |
143 | | Lock lock(mMutex); |
144 | | |
145 | | DWORD tickNow = ::timeGetTime(); |
146 | | |
147 | | if (tickNow != mBaseTime.LowPart) |
148 | | { |
149 | | mBaseTime.QuadPart += (tickNow - mBaseTime.LowPart); |
150 | | } |
151 | | |
152 | | return mBaseTime.QuadPart; |
153 | | } |
154 | | |
155 | | ULARGE_INTEGER ResipClock::WinMonoClock::GTCLock::mBaseTime = {0,0}; |
156 | | Mutex ResipClock::WinMonoClock::GTCLock::mMutex; |
157 | | |
158 | | uint64_t ResipClock::WinMonoClock::GTCLockDuringRange::GTC64(void) |
159 | | { |
160 | | // we assume that this function is called reasonable often |
161 | | // monitor wrap around only in dangerous time range: |
162 | | // empiric constants |
163 | | const DWORD TIMER_BEGIN_SAFE_RANGE = 0xffff; // about one minute after |
164 | | const DWORD TIMER_END_SAFE_RANGE = 0xffff0000; // and before wrap around |
165 | | |
166 | | DWORD tick = ::timeGetTime(); |
167 | | |
168 | | if ( ( tick > TIMER_BEGIN_SAFE_RANGE ) && ( tick < TIMER_END_SAFE_RANGE ) ) |
169 | | { |
170 | | LARGE_INTEGER ret; |
171 | | ret.HighPart = mWrapCounter; |
172 | | ret.LowPart = tick; |
173 | | return (uint64_t)ret.QuadPart; |
174 | | } |
175 | | // most application will never be here - only long running servers |
176 | | Lock lock(mWrapCounterMutex); |
177 | | if (mPrevTick > tick) |
178 | | { |
179 | | mWrapCounter++; |
180 | | } |
181 | | mPrevTick = tick; |
182 | | LARGE_INTEGER ret; |
183 | | ret.HighPart = mWrapCounter; |
184 | | ret.LowPart = tick; |
185 | | return (uint64_t)ret.QuadPart; |
186 | | } |
187 | | |
188 | | uint32_t ResipClock::WinMonoClock::GTCLockDuringRange::mWrapCounter = 0; |
189 | | DWORD ResipClock::WinMonoClock::GTCLockDuringRange::mPrevTick = 0; |
190 | | Mutex ResipClock::WinMonoClock::GTCLockDuringRange::mWrapCounterMutex; |
191 | | #endif |
192 | | |
193 | | uint64_t |
194 | | ResipClock::getSystemTime() |
195 | 9.60k | { |
196 | 9.60k | resip_assert(sizeof(uint64_t) == 64/8); |
197 | | |
198 | | #if defined(WIN32) || defined(UNDER_CE) |
199 | | #ifdef _RESIP_MONOTONIC_CLOCK |
200 | | static ResipClock::WinMonoClock clockInit; |
201 | | return WinMonoClock::GetClock64() * 1000; |
202 | | #else |
203 | | FILETIME ft; |
204 | | |
205 | | #ifdef UNDER_CE |
206 | | SYSTEMTIME st; |
207 | | ::GetSystemTime(&st); |
208 | | ::SystemTimeToFileTime(&st,&ft); |
209 | | #else |
210 | | ::GetSystemTimeAsFileTime(&ft); |
211 | | #endif |
212 | | |
213 | | ULARGE_INTEGER li; |
214 | | li.LowPart = ft.dwLowDateTime; |
215 | | li.HighPart = ft.dwHighDateTime; |
216 | | return li.QuadPart/10; |
217 | | |
218 | | #endif //_RESIP_MONOTONIC_CLOCK |
219 | | |
220 | | #else //#if defined(WIN32) || defined(UNDER_CE) |
221 | | |
222 | 9.60k | uint64_t time=0; |
223 | | #ifdef _RESIP_MONOTONIC_CLOCK |
224 | | struct timespec now_monotonic; |
225 | | if (clock_gettime( CLOCK_MONOTONIC, &now_monotonic ) == 0) |
226 | | // if ( syscall( __NR_clock_gettime, CLOCK_MONOTONIC, &now_monotonic ) == 0 ) |
227 | | { |
228 | | time = now_monotonic.tv_sec; |
229 | | time = time*1000000; |
230 | | time += now_monotonic.tv_nsec/1000; |
231 | | return time; |
232 | | } |
233 | | #endif |
234 | 9.60k | struct timeval now; |
235 | 9.60k | gettimeofday( &now , NULL ); |
236 | | //assert( now ); |
237 | 9.60k | time = now.tv_sec; |
238 | 9.60k | time = time*1000000; |
239 | 9.60k | time += now.tv_usec; |
240 | 9.60k | return time; |
241 | 9.60k | #endif |
242 | 9.60k | } |
243 | | |
244 | | uint64_t |
245 | | ResipClock::getForever() |
246 | 0 | { |
247 | 0 | resip_assert( sizeof(uint64_t) == 8 ); |
248 | | #if defined(WIN32) && !defined(__GNUC__) |
249 | | return 18446744073709551615ui64; |
250 | | #else |
251 | 0 | return 18446744073709551615ULL; |
252 | 0 | #endif |
253 | 0 | } |
254 | | |
255 | | uint64_t |
256 | | ResipClock::getRandomFutureTimeMs( uint64_t futureMs ) |
257 | 0 | { |
258 | 0 | uint64_t now = getTimeMs(); |
259 | | |
260 | | // make r a random number between 5000 and 9000 |
261 | 0 | int r = Random::getRandom()%4000; |
262 | 0 | r += 5000; |
263 | |
|
264 | 0 | uint64_t ret = now; |
265 | 0 | ret += (futureMs*r)/10000; |
266 | |
|
267 | 0 | resip_assert( ret >= now ); |
268 | 0 | resip_assert( ret >= now+(futureMs/2) ); |
269 | 0 | resip_assert( ret <= now+futureMs ); |
270 | |
|
271 | 0 | return ret; |
272 | 0 | } |
273 | | |
274 | | void |
275 | | ResipClock::queryTimerInfo(unsigned &min, unsigned &max, unsigned &actual, bool &isMonotonic) |
276 | 0 | { |
277 | 0 | min = max = actual = 0; |
278 | 0 | isMonotonic = false; |
279 | |
|
280 | | #if defined(WIN32) |
281 | | #if defined(_RESIP_MONOTONIC_CLOCK) |
282 | | #if !defined(NTSTATUS) |
283 | | #define NTSTATUS DWORD |
284 | | #endif |
285 | | typedef NTSTATUS (WINAPI*PNTQTR)(PULONG,PULONG,PULONG); |
286 | | |
287 | | HMODULE hm = ::LoadLibrary("ntdll"); |
288 | | |
289 | | if (hm != NULL) |
290 | | { |
291 | | PNTQTR ntqtr = (PNTQTR)::GetProcAddress(hm,"NtQueryTimerResolution"); |
292 | | |
293 | | if (ntqtr) |
294 | | { |
295 | | ntqtr((PULONG)&min,(PULONG)&max,(PULONG)&actual); |
296 | | min /= 10; |
297 | | max /= 10; |
298 | | actual /= 10; |
299 | | } |
300 | | |
301 | | ::FreeLibrary(hm); |
302 | | hm = NULL; |
303 | | } |
304 | | isMonotonic = true; |
305 | | #else |
306 | | DWORD timeAdjustment=0; |
307 | | BOOL timeAdjustmentDisabled=0; |
308 | | //on Vista it looks like GetSystemTime has 1ms resolution, but GetSystemTimeAdjustment still returns 15ms |
309 | | //so just set the min resolution to whatever is reported, no actual resolution can be consistently found. |
310 | | ::GetSystemTimeAdjustment(&timeAdjustment,(PDWORD)&min,&timeAdjustmentDisabled); |
311 | | min /= 10; |
312 | | isMonotonic = false; |
313 | | #endif |
314 | | #else //WIN32 |
315 | | #ifdef __APPLE__ |
316 | | //@TODO |
317 | | #else |
318 | 0 | clockid_t clock = CLOCK_REALTIME; //need to test/verify CLOCK_REALTIME returns the gettimeofday resolution. |
319 | | #if defined(_RESIP_MONOTONIC_CLOCK) |
320 | | clock = CLOCK_MONOTONIC; |
321 | | isMonotonic = true; |
322 | | #endif |
323 | 0 | struct timespec res; |
324 | 0 | if (clock_getres(clock,&res) == 0) |
325 | 0 | { |
326 | 0 | actual = (res.tv_sec * 1000000) + (res.tv_nsec / 1000); |
327 | 0 | } |
328 | 0 | #endif |
329 | 0 | #endif |
330 | 0 | } |
331 | | |
332 | | /* ==================================================================== |
333 | | * The Vovida Software License, Version 1.0 |
334 | | * |
335 | | * Copyright (c) 2000 Vovida Networks, Inc. All rights reserved. |
336 | | * |
337 | | * Redistribution and use in source and binary forms, with or without |
338 | | * modification, are permitted provided that the following conditions |
339 | | * are met: |
340 | | * |
341 | | * 1. Redistributions of source code must retain the above copyright |
342 | | * notice, this list of conditions and the following disclaimer. |
343 | | * |
344 | | * 2. Redistributions in binary form must reproduce the above copyright |
345 | | * notice, this list of conditions and the following disclaimer in |
346 | | * the documentation and/or other materials provided with the |
347 | | * distribution. |
348 | | * |
349 | | * 3. The names "VOCAL", "Vovida Open Communication Application Library", |
350 | | * and "Vovida Open Communication Application Library (VOCAL)" must |
351 | | * not be used to endorse or promote products derived from this |
352 | | * software without prior written permission. For written |
353 | | * permission, please contact vocal@vovida.org. |
354 | | * |
355 | | * 4. Products derived from this software may not be called "VOCAL", nor |
356 | | * may "VOCAL" appear in their name, without prior written |
357 | | * permission of Vovida Networks, Inc. |
358 | | * |
359 | | * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED |
360 | | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
361 | | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND |
362 | | * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA |
363 | | * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES |
364 | | * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL, |
365 | | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
366 | | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
367 | | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
368 | | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
369 | | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE |
370 | | * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
371 | | * DAMAGE. |
372 | | * |
373 | | * ==================================================================== |
374 | | * |
375 | | * This software consists of voluntary contributions made by Vovida |
376 | | * Networks, Inc. and many individuals on behalf of Vovida Networks, |
377 | | * Inc. For more information on Vovida Networks, Inc., please see |
378 | | * <http://www.vovida.org/>. |
379 | | * |
380 | | * vi: set shiftwidth=3 expandtab: |
381 | | */ |
382 | | |