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
9Module Name:
10
11 time.c
12
13Abstract:
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>
33static mach_timebase_info_data_t s_TimebaseInfo;
34#endif
35
36using namespace CorUnix;
37
38SET_DEFAULT_DEBUG_CHANNEL(MISC);
39
40/*++
41Function :
42TIMEInitialize
43
44Initialize all Time-related stuff related
45
46(no parameters)
47
48Return value :
49TRUE if Time support initialization succeeded
50FALSE otherwise
51--*/
52BOOL 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/*++
68Function:
69 GetSystemTime
70
71The GetSystemTime function retrieves the current system date and
72time. The system time is expressed in Coordinated Universal Time
73(UTC).
74
75Parameters
76
77lpSystemTime
78 [out] Pointer to a SYSTEMTIME structure to receive the current system date and time.
79
80Return Values
81
82This function does not return a value.
83
84--*/
85VOID
86PALAPI
87GetSystemTime(
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 }
150EXIT:
151 LOGEXIT("GetSystemTime returns void\n");
152 PERF_EXIT(GetSystemTime);
153}
154
155/*++
156Function:
157 GetTickCount
158
159The GetTickCount function retrieves the number of milliseconds that
160have elapsed since the system was started. It is limited to the
161resolution of the system timer. To obtain the system timer resolution,
162use the GetSystemTimeAdjustment function.
163
164Parameters
165
166This function has no parameters.
167
168Return Values
169
170The return value is the number of milliseconds that have elapsed since
171the system was started.
172
173In the PAL implementation the return value is the elapsed time since
174the start of the epoch.
175
176--*/
177DWORD
178PALAPI
179GetTickCount(
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
194BOOL
195PALAPI
196QueryPerformanceCounter(
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
258BOOL
259PALAPI
260QueryPerformanceFrequency(
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/*++
289Function:
290 QueryThreadCycleTime
291
292Puts the execution time (in nanoseconds) for the thread pointed to by ThreadHandle, into the unsigned long
293pointed to by CycleTime. ThreadHandle must refer to the current thread. Returns TRUE on success, FALSE on
294failure.
295--*/
296
297BOOL
298PALAPI
299QueryThreadCycleTime(
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
322EXIT:
323 return retval;
324}
325
326/*++
327Function:
328 GetTickCount64
329
330Returns a 64-bit tick count with a millisecond resolution. It tries its best
331to return monotonically increasing counts and avoid being affected by changes
332to the system clock (either due to drift or due to explicit changes to system
333time).
334--*/
335PALAPI
336ULONGLONG
337GetTickCount64()
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
393EXIT:
394 return retval;
395}
396
397/*++
398Function:
399 PAL_nanosleep
400
401Sleeps for the time specified in timeInNs.
402Returns 0 on successful completion of the operation.
403--*/
404PALAPI
405INT
406PAL_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}
429