mach_absolute_time as the primary clock source on macOS (corefx#30391) (#18505) ...
[platform/upstream/coreclr.git] / src / pal / src / misc / time.cpp
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4
5 /*++
6
7
8
9 Module Name:
10
11     time.c
12
13 Abstract:
14
15     Implementation of time related WIN API functions.
16
17
18
19 --*/
20
21 #include "pal/palinternal.h"
22 #include "pal/dbgmsg.h"
23 #include "pal/misc.h"
24
25 #include <time.h>
26 #include <sys/time.h>
27 #include <errno.h>
28 #include <string.h>
29 #include <sched.h>
30
31 #if HAVE_MACH_ABSOLUTE_TIME
32 #include <mach/mach_time.h>
33 static mach_timebase_info_data_t s_TimebaseInfo;
34 #endif
35
36 using namespace CorUnix;
37
38 SET_DEFAULT_DEBUG_CHANNEL(MISC);
39
40 /*++
41 Function :
42 TIMEInitialize
43
44 Initialize all Time-related stuff related
45
46 (no parameters)
47
48 Return value :
49 TRUE  if Time support initialization succeeded
50 FALSE otherwise
51 --*/
52 BOOL TIMEInitialize(void)
53 {
54 #if HAVE_MACH_ABSOLUTE_TIME
55     kern_return_t machRet;
56     if ((machRet = mach_timebase_info(&s_TimebaseInfo)) != KERN_SUCCESS)
57     {
58         ASSERT("mach_timebase_info() failed: %s\n", mach_error_string(machRet));
59         return FALSE;
60     }
61 #endif
62
63     return TRUE;
64 }
65
66
67 /*++
68 Function:
69   GetSystemTime
70
71 The GetSystemTime function retrieves the current system date and
72 time. The system time is expressed in Coordinated Universal Time
73 (UTC).
74
75 Parameters
76
77 lpSystemTime 
78        [out] Pointer to a SYSTEMTIME structure to receive the current system date and time. 
79
80 Return Values
81
82 This function does not return a value.
83
84 --*/
85 VOID
86 PALAPI
87 GetSystemTime(
88           OUT LPSYSTEMTIME lpSystemTime)
89 {
90     time_t tt;
91 #if HAVE_GMTIME_R
92     struct tm ut;
93 #endif  /* HAVE_GMTIME_R */
94     struct tm *utPtr;
95     struct timeval timeval;
96     int timeofday_retval;
97
98     PERF_ENTRY(GetSystemTime);
99     ENTRY("GetSystemTime (lpSystemTime=%p)\n", lpSystemTime);
100
101     tt = time(NULL);
102
103     /* We can't get millisecond resolution from time(), so we get it from 
104        gettimeofday() */
105     timeofday_retval = gettimeofday(&timeval,NULL);
106     
107 #if HAVE_GMTIME_R
108     utPtr = &ut;
109     if (gmtime_r(&tt, utPtr) == NULL)
110 #else   /* HAVE_GMTIME_R */
111     if ((utPtr = gmtime(&tt)) == NULL)
112 #endif  /* HAVE_GMTIME_R */
113     {
114         ASSERT("gmtime() failed; errno is %d (%s)\n", errno, strerror(errno));
115         goto EXIT;
116     }
117
118     lpSystemTime->wYear = 1900 + utPtr->tm_year;
119     lpSystemTime->wMonth = utPtr->tm_mon + 1;
120     lpSystemTime->wDayOfWeek = utPtr->tm_wday;
121     lpSystemTime->wDay = utPtr->tm_mday;
122     lpSystemTime->wHour = utPtr->tm_hour;
123     lpSystemTime->wMinute = utPtr->tm_min;
124     lpSystemTime->wSecond = utPtr->tm_sec;
125
126     if(-1 == timeofday_retval)
127     {
128         ASSERT("gettimeofday() failed; errno is %d (%s)\n",
129                errno, strerror(errno));
130         lpSystemTime->wMilliseconds = 0;
131     }
132     else
133     {
134         int old_seconds;
135         int new_seconds;
136     
137         lpSystemTime->wMilliseconds = timeval.tv_usec/tccMillieSecondsToMicroSeconds;
138     
139         old_seconds = utPtr->tm_sec;
140         new_seconds = timeval.tv_sec%60;
141    
142         /* just in case we reached the next second in the interval between 
143            time() and gettimeofday() */
144         if( old_seconds!=new_seconds )
145         {
146             TRACE("crossed seconds boundary; setting milliseconds to 999\n");
147             lpSystemTime->wMilliseconds = 999;
148         }  
149     }                        
150 EXIT:
151     LOGEXIT("GetSystemTime returns void\n");
152     PERF_EXIT(GetSystemTime);
153 }
154
155 /*++
156 Function:
157   GetTickCount
158
159 The GetTickCount function retrieves the number of milliseconds that
160 have elapsed since the system was started. It is limited to the
161 resolution of the system timer. To obtain the system timer resolution,
162 use the GetSystemTimeAdjustment function.
163
164 Parameters
165
166 This function has no parameters. 
167
168 Return Values
169
170 The return value is the number of milliseconds that have elapsed since
171 the system was started.
172
173 In the ROTOR implementation the return value is the elapsed time since
174 the start of the epoch.
175
176 --*/
177 DWORD
178 PALAPI
179 GetTickCount(
180          VOID)
181 {
182     DWORD retval = 0;
183     PERF_ENTRY(GetTickCount);
184     ENTRY("GetTickCount ()\n");
185
186     // Get the 64-bit count from GetTickCount64 and truncate the results.
187     retval = (DWORD) GetTickCount64();
188
189     LOGEXIT("GetTickCount returns DWORD %u\n", retval);
190     PERF_EXIT(GetTickCount);
191     return retval;
192 }
193
194 BOOL
195 PALAPI
196 QueryPerformanceCounter(
197     OUT LARGE_INTEGER *lpPerformanceCount
198     )
199 {
200     BOOL retval = TRUE;
201
202     PERF_ENTRY(QueryPerformanceCounter);
203     ENTRY("QueryPerformanceCounter()\n");
204     do
205 #if HAVE_MACH_ABSOLUTE_TIME
206     {
207         lpPerformanceCount->QuadPart = (LONGLONG)mach_absolute_time();
208     }
209 #elif HAVE_CLOCK_MONOTONIC
210     {
211         struct timespec ts;
212         if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
213         {
214             ASSERT("clock_gettime(CLOCK_MONOTONIC) failed; errno is %d (%s)\n", errno, strerror(errno));
215             retval = FALSE;
216             break;
217         }
218         lpPerformanceCount->QuadPart = 
219             (LONGLONG)ts.tv_sec * (LONGLONG)tccSecondsToNanoSeconds + (LONGLONG)ts.tv_nsec;
220     }
221 #elif HAVE_GETHRTIME
222     {
223         lpPerformanceCount->QuadPart = (LONGLONG)gethrtime();
224     }
225 #elif HAVE_READ_REAL_TIME
226     {
227         timebasestruct_t tb;
228         read_real_time(&tb, TIMEBASE_SZ);
229         if (time_base_to_time(&tb, TIMEBASE_SZ) != 0)
230         {
231             ASSERT("time_base_to_time() failed; errno is %d (%s)\n", errno, strerror(errno));
232             retval = FALSE;
233             break;
234         }
235         lpPerformanceCount->QuadPart = 
236             (LONGLONG)tb.tb_high * (LONGLONG)tccSecondsToNanoSeconds + (LONGLONG)tb.tb_low;
237     }
238 #else
239     {
240         struct timeval tv;    
241         if (gettimeofday(&tv, NULL) == -1)
242         {
243             ASSERT("gettimeofday() failed; errno is %d (%s)\n", errno, strerror(errno));
244             retval = FALSE;
245             break;
246         }
247         lpPerformanceCount->QuadPart = 
248             (LONGLONG)tv.tv_sec * (LONGLONG)tccSecondsToMicroSeconds + (LONGLONG)tv.tv_usec;    
249     }
250 #endif // HAVE_CLOCK_MONOTONIC 
251     while (false);
252
253     LOGEXIT("QueryPerformanceCounter\n");
254     PERF_EXIT(QueryPerformanceCounter);
255     return retval;
256 }
257
258 BOOL
259 PALAPI
260 QueryPerformanceFrequency(
261     OUT LARGE_INTEGER *lpFrequency
262     )
263 {
264     BOOL retval = TRUE;
265     PERF_ENTRY(QueryPerformanceFrequency);
266     ENTRY("QueryPerformanceFrequency()\n");
267 #if HAVE_MACH_ABSOLUTE_TIME
268     // use denom == 0 to indicate that s_TimebaseInfo is uninitialised.
269     if (s_TimebaseInfo.denom == 0)
270     {
271         ASSERT("s_TimebaseInfo is uninitialized.\n");
272         retval = FALSE;
273     }
274     else
275     {
276         lpFrequency->QuadPart = (LONGLONG)tccSecondsToNanoSeconds * ((LONGLONG)s_TimebaseInfo.denom / (LONGLONG)s_TimebaseInfo.numer);
277     }
278 #elif HAVE_GETHRTIME || HAVE_READ_REAL_TIME || HAVE_CLOCK_MONOTONIC
279     lpFrequency->QuadPart = (LONGLONG)tccSecondsToNanoSeconds;
280 #else
281     lpFrequency->QuadPart = (LONGLONG)tccSecondsToMicroSeconds;
282 #endif // HAVE_MACH_ABSOLUTE_TIME
283     LOGEXIT("QueryPerformanceFrequency\n");
284     PERF_EXIT(QueryPerformanceFrequency);
285     return retval;
286 }
287
288 /*++
289 Function:
290   QueryThreadCycleTime
291
292 Puts the execution time (in nanoseconds) for the thread pointed to by ThreadHandle, into the unsigned long
293 pointed to by CycleTime. ThreadHandle must refer to the current thread. Returns TRUE on success, FALSE on
294 failure.
295 --*/
296
297 BOOL
298 PALAPI
299 QueryThreadCycleTime(
300     IN HANDLE ThreadHandle,
301     OUT PULONG64 CycleTime
302     )
303 {
304
305     ULONG64 calcTime;
306     FILETIME kernelTime, userTime;
307     BOOL retval = TRUE;
308
309     if(!GetThreadTimesInternal(ThreadHandle, &kernelTime, &userTime))
310     {
311         ASSERT("Could not get cycle time for current thread");
312         retval = FALSE;
313         goto EXIT;
314     }
315
316     calcTime = ((ULONG64)kernelTime.dwHighDateTime << 32);
317     calcTime += (ULONG64)kernelTime.dwLowDateTime;
318     calcTime += ((ULONG64)userTime.dwHighDateTime << 32);
319     calcTime += (ULONG64)userTime.dwLowDateTime;
320     *CycleTime = calcTime;
321
322 EXIT:
323     return retval;
324 }
325
326 /*++
327 Function:
328   GetTickCount64
329
330 Returns a 64-bit tick count with a millisecond resolution. It tries its best
331 to return monotonically increasing counts and avoid being affected by changes
332 to the system clock (either due to drift or due to explicit changes to system
333 time).
334 --*/
335 PALAPI
336 ULONGLONG
337 GetTickCount64()
338 {
339     ULONGLONG retval = 0;
340
341 #if HAVE_MACH_ABSOLUTE_TIME
342     {
343         // use denom == 0 to indicate that s_TimebaseInfo is uninitialised.
344         if (s_TimebaseInfo.denom == 0)
345         {
346             ASSERT("s_TimebaseInfo is uninitialized.\n");
347             goto EXIT;
348         }
349         retval = (mach_absolute_time() * s_TimebaseInfo.numer / s_TimebaseInfo.denom) / tccMillieSecondsToNanoSeconds;
350     }
351 #elif HAVE_CLOCK_MONOTONIC_COARSE || HAVE_CLOCK_MONOTONIC
352     {
353         clockid_t clockType = 
354 #if HAVE_CLOCK_MONOTONIC_COARSE
355             CLOCK_MONOTONIC_COARSE; // good enough resolution, fastest speed
356 #else
357             CLOCK_MONOTONIC;
358 #endif
359         struct timespec ts;
360         if (clock_gettime(clockType, &ts) != 0)
361         {
362             ASSERT("clock_gettime(CLOCK_MONOTONIC*) failed; errno is %d (%s)\n", errno, strerror(errno));
363             goto EXIT;
364         }
365         retval = (ts.tv_sec * tccSecondsToMillieSeconds)+(ts.tv_nsec / tccMillieSecondsToNanoSeconds);
366     }
367 #elif HAVE_GETHRTIME
368     {
369         retval = (ULONGLONG)(gethrtime() / tccMillieSecondsToNanoSeconds);
370     }
371 #elif HAVE_READ_REAL_TIME
372     {
373         timebasestruct_t tb;
374         read_real_time(&tb, TIMEBASE_SZ);
375         if (time_base_to_time(&tb, TIMEBASE_SZ) != 0)
376         {
377             ASSERT("time_base_to_time() failed; errno is %d (%s)\n", errno, strerror(errno));
378             goto EXIT;
379         }
380         retval = (tb.tb_high * tccSecondsToMillieSeconds)+(tb.tb_low / tccMillieSecondsToNanoSeconds);
381     }
382 #else
383     {
384         struct timeval tv;    
385         if (gettimeofday(&tv, NULL) == -1)
386         {
387             ASSERT("gettimeofday() failed; errno is %d (%s)\n", errno, strerror(errno));
388             goto EXIT;
389         }
390         retval = (tv.tv_sec * tccSecondsToMillieSeconds) + (tv.tv_usec / tccMillieSecondsToMicroSeconds);
391     }
392 #endif // HAVE_CLOCK_MONOTONIC 
393 EXIT:    
394     return retval;
395 }
396
397 /*++
398 Function:
399   PAL_nanosleep
400
401 Sleeps for the time specified in timeInNs.
402 Returns 0 on successful completion of the operation.
403 --*/
404 PALAPI
405 INT
406 PAL_nanosleep(
407     IN long timeInNs
408     )
409 {
410     struct timespec req;
411     struct timespec rem;
412     int result;
413
414     req.tv_sec = 0;
415     req.tv_nsec = timeInNs;
416
417     do
418     {
419         // Sleep for the requested time.
420         result = nanosleep(&req, &rem);
421
422         // Save the remaining time (used if the loop runs another iteration).
423         req = rem;
424     }
425     while(result == -1 && errno == EINTR);
426
427     return result;
428 }