Merge pull request #2721 from ludovic-henry/fix-mono_ms_ticks
[mono.git] / mono / utils / mono-time.c
1 /*
2  * Time utility functions.
3  * Author: Paolo Molaro (<lupus@ximian.com>)
4  * Copyright (C) 2008 Novell, Inc.
5  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
6  */
7
8 #include <config.h>
9 #include <stdlib.h>
10 #include <stdio.h>
11
12 #ifdef HAVE_SYS_TIME_H
13 #include <sys/time.h>
14 #endif
15
16 #include <utils/mono-time.h>
17
18
19 #define MTICKS_PER_SEC (10 * 1000 * 1000)
20
21 gint64
22 mono_msec_ticks (void)
23 {
24         return mono_100ns_ticks () / 10 / 1000;
25 }
26
27 #ifdef HOST_WIN32
28 #include <windows.h>
29
30 #ifndef _MSC_VER
31 /* we get "error: implicit declaration of function 'GetTickCount64'" */
32 ULONGLONG GetTickCount64(void);
33 #endif
34
35 gint64
36 mono_msec_boottime (void)
37 {
38         /* GetTickCount () is reportedly monotonic */
39         return GetTickCount64 ();
40 }
41
42 /* Returns the number of 100ns ticks from unspecified time: this should be monotonic */
43 gint64
44 mono_100ns_ticks (void)
45 {
46         static LARGE_INTEGER freq;
47         static UINT64 start_time;
48         UINT64 cur_time;
49         LARGE_INTEGER value;
50
51         if (!freq.QuadPart) {
52                 if (!QueryPerformanceFrequency (&freq))
53                         return mono_100ns_datetime ();
54                 QueryPerformanceCounter (&value);
55                 start_time = value.QuadPart;
56         }
57         QueryPerformanceCounter (&value);
58         cur_time = value.QuadPart;
59         /* we use unsigned numbers and return the difference to avoid overflows */
60         return (cur_time - start_time) * (double)MTICKS_PER_SEC / freq.QuadPart;
61 }
62
63 /* Returns the number of 100ns ticks since Jan 1, 1601, UTC timezone */
64 gint64
65 mono_100ns_datetime (void)
66 {
67         ULARGE_INTEGER ft;
68
69         if (sizeof(ft) != sizeof(FILETIME))
70                 g_assert_not_reached ();
71
72         GetSystemTimeAsFileTime ((FILETIME*) &ft);
73         return ft.QuadPart;
74 }
75
76 #else
77
78
79 #if defined (HAVE_SYS_PARAM_H)
80 #include <sys/param.h>
81 #endif
82 #if defined(HAVE_SYS_SYSCTL_H)
83 #include <sys/sysctl.h>
84 #endif
85
86 #if defined(PLATFORM_MACOSX)
87 #include <mach/mach.h>
88 #include <mach/mach_time.h>
89 #endif
90
91 #include <time.h>
92
93 static gint64
94 get_boot_time (void)
95 {
96 #if defined (HAVE_SYS_PARAM_H) && defined (KERN_BOOTTIME)
97         int mib [2];
98         size_t size;
99         time_t now;
100         struct timeval boottime;
101
102         (void)time(&now);
103
104         mib [0] = CTL_KERN;
105         mib [1] = KERN_BOOTTIME;
106
107         size = sizeof(boottime);
108
109         if (sysctl(mib, 2, &boottime, &size, NULL, 0) != -1)
110                 return (gint64)((now - boottime.tv_sec) * MTICKS_PER_SEC);
111 #else
112         FILE *uptime = fopen ("/proc/uptime", "r");
113         if (uptime) {
114                 double upt;
115                 if (fscanf (uptime, "%lf", &upt) == 1) {
116                         gint64 now = mono_100ns_ticks ();
117                         fclose (uptime);
118                         return now - (gint64)(upt * MTICKS_PER_SEC);
119                 }
120                 fclose (uptime);
121         }
122 #endif
123         /* a made up uptime of 300 seconds */
124         return (gint64)300 * MTICKS_PER_SEC;
125 }
126
127 /* Returns the number of milliseconds from boot time: this should be monotonic */
128 gint64
129 mono_msec_boottime (void)
130 {
131         static gint64 boot_time = 0;
132         gint64 now;
133         if (!boot_time)
134                 boot_time = get_boot_time ();
135         now = mono_100ns_ticks ();
136         /*printf ("now: %llu (boot: %llu) ticks: %llu\n", (gint64)now, (gint64)boot_time, (gint64)(now - boot_time));*/
137         g_assert (now > boot_time);
138         return (now - boot_time)/10000;
139 }
140
141 /* Returns the number of 100ns ticks from unspecified time: this should be monotonic */
142 gint64
143 mono_100ns_ticks (void)
144 {
145         struct timeval tv;
146 #ifdef CLOCK_MONOTONIC
147         struct timespec tspec;
148         static struct timespec tspec_freq = {0};
149         static int can_use_clock = 0;
150         if (!tspec_freq.tv_nsec) {
151                 can_use_clock = clock_getres (CLOCK_MONOTONIC, &tspec_freq) == 0;
152                 /*printf ("resolution: %lu.%lu\n", tspec_freq.tv_sec, tspec_freq.tv_nsec);*/
153         }
154         if (can_use_clock) {
155                 if (clock_gettime (CLOCK_MONOTONIC, &tspec) == 0) {
156                         /*printf ("time: %lu.%lu\n", tspec.tv_sec, tspec.tv_nsec); */
157                         return ((gint64)tspec.tv_sec * MTICKS_PER_SEC + tspec.tv_nsec / 100);
158                 }
159         }
160         
161 #elif defined(PLATFORM_MACOSX)
162         /* http://developer.apple.com/library/mac/#qa/qa1398/_index.html */
163         static mach_timebase_info_data_t timebase;
164         guint64 now = mach_absolute_time ();
165         if (timebase.denom == 0) {
166                 mach_timebase_info (&timebase);
167                 timebase.denom *= 100; /* we return 100ns ticks */
168         }
169         return now * timebase.numer / timebase.denom;
170 #endif
171         if (gettimeofday (&tv, NULL) == 0)
172                 return ((gint64)tv.tv_sec * 1000000 + tv.tv_usec) * 10;
173         return 0;
174 }
175
176 /*
177  * Magic number to convert unix epoch start to windows epoch start
178  * Jan 1, 1970 into a value which is relative to Jan 1, 1601.
179  */
180 #define EPOCH_ADJUST    ((guint64)11644473600LL)
181
182 /* Returns the number of 100ns ticks since 1/1/1601, UTC timezone */
183 gint64
184 mono_100ns_datetime (void)
185 {
186         struct timeval tv;
187         if (gettimeofday (&tv, NULL) == 0)
188                 return mono_100ns_datetime_from_timeval (tv);
189         return 0;
190 }
191
192 gint64
193 mono_100ns_datetime_from_timeval (struct timeval tv)
194 {
195         return (((gint64)tv.tv_sec + EPOCH_ADJUST) * 1000000 + tv.tv_usec) * 10;
196 }
197
198 #endif
199