Coverage Report

Created: 2023-09-25 06:23

/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)&GTCInterlocked::GTC64;
75
   DebugLog(<< "Using GTCInterlocked::GTC64 as the monotonic clock for time functions.");
76
#else                
77
   ResipClock::mMaxSystemTimeWaitMs  = GTCLockDuringRange::GetMaxWaitMs();
78
   mGTC64 = (PGTC64)&GTCLockDuringRange::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