1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at https://curl.se/docs/copyright.html.
13 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 * SPDX-License-Identifier: curl
22 *
23 ***************************************************************************/
24
25 #include "timeval.h"
26
27 #if defined(_WIN32)
28
29 #include <curl/curl.h>
30 #include "system_win32.h"
31
32 /* In case of bug fix this function has a counterpart in tool_util.c */
Curl_now(void)33 struct curltime Curl_now(void)
34 {
35 struct curltime now;
36 if(Curl_isVistaOrGreater) { /* QPC timer might have issues pre-Vista */
37 LARGE_INTEGER count;
38 QueryPerformanceCounter(&count);
39 now.tv_sec = (time_t)(count.QuadPart / Curl_freq.QuadPart);
40 now.tv_usec = (int)((count.QuadPart % Curl_freq.QuadPart) * 1000000 /
41 Curl_freq.QuadPart);
42 }
43 else {
44 /* Disable /analyze warning that GetTickCount64 is preferred */
45 #if defined(_MSC_VER)
46 #pragma warning(push)
47 #pragma warning(disable:28159)
48 #endif
49 DWORD milliseconds = GetTickCount();
50 #if defined(_MSC_VER)
51 #pragma warning(pop)
52 #endif
53
54 now.tv_sec = (time_t)(milliseconds / 1000);
55 now.tv_usec = (int)((milliseconds % 1000) * 1000);
56 }
57 return now;
58 }
59
60 #elif defined(HAVE_CLOCK_GETTIME_MONOTONIC) || \
61 defined(HAVE_CLOCK_GETTIME_MONOTONIC_RAW)
62
Curl_now(void)63 struct curltime Curl_now(void)
64 {
65 /*
66 ** clock_gettime() is granted to be increased monotonically when the
67 ** monotonic clock is queried. Time starting point is unspecified, it
68 ** could be the system start-up time, the Epoch, or something else,
69 ** in any case the time starting point does not change once that the
70 ** system has started up.
71 */
72 #ifdef HAVE_GETTIMEOFDAY
73 struct timeval now;
74 #endif
75 struct curltime cnow;
76 struct timespec tsnow;
77
78 /*
79 ** clock_gettime() may be defined by Apple's SDK as weak symbol thus
80 ** code compiles but fails during runtime if clock_gettime() is
81 ** called on unsupported OS version.
82 */
83 #if defined(__APPLE__) && defined(HAVE_BUILTIN_AVAILABLE) && \
84 (HAVE_BUILTIN_AVAILABLE == 1)
85 bool have_clock_gettime = FALSE;
86 if(__builtin_available(macOS 10.12, iOS 10, tvOS 10, watchOS 3, *))
87 have_clock_gettime = TRUE;
88 #endif
89
90 #ifdef HAVE_CLOCK_GETTIME_MONOTONIC_RAW
91 if(
92 #if defined(__APPLE__) && defined(HAVE_BUILTIN_AVAILABLE) && \
93 (HAVE_BUILTIN_AVAILABLE == 1)
94 have_clock_gettime &&
95 #endif
96 (0 == clock_gettime(CLOCK_MONOTONIC_RAW, &tsnow))) {
97 cnow.tv_sec = tsnow.tv_sec;
98 cnow.tv_usec = (int)(tsnow.tv_nsec / 1000);
99 }
100 else
101 #endif
102
103 if(
104 #if defined(__APPLE__) && defined(HAVE_BUILTIN_AVAILABLE) && \
105 (HAVE_BUILTIN_AVAILABLE == 1)
106 have_clock_gettime &&
107 #endif
108 (0 == clock_gettime(CLOCK_MONOTONIC, &tsnow))) {
109 cnow.tv_sec = tsnow.tv_sec;
110 cnow.tv_usec = (int)(tsnow.tv_nsec / 1000);
111 }
112 /*
113 ** Even when the configure process has truly detected monotonic clock
114 ** availability, it might happen that it is not actually available at
115 ** runtime. When this occurs simply fallback to other time source.
116 */
117 #ifdef HAVE_GETTIMEOFDAY
118 else {
119 (void)gettimeofday(&now, NULL);
120 cnow.tv_sec = now.tv_sec;
121 cnow.tv_usec = (int)now.tv_usec;
122 }
123 #else
124 else {
125 cnow.tv_sec = time(NULL);
126 cnow.tv_usec = 0;
127 }
128 #endif
129 return cnow;
130 }
131
132 #elif defined(HAVE_MACH_ABSOLUTE_TIME)
133
134 #include <stdint.h>
135 #include <mach/mach_time.h>
136
Curl_now(void)137 struct curltime Curl_now(void)
138 {
139 /*
140 ** Monotonic timer on macOS is provided by mach_absolute_time(), which
141 ** returns time in Mach "absolute time units," which are platform-dependent.
142 ** To convert to nanoseconds, one must use conversion factors specified by
143 ** mach_timebase_info().
144 */
145 static mach_timebase_info_data_t timebase;
146 struct curltime cnow;
147 uint64_t usecs;
148
149 if(0 == timebase.denom)
150 (void) mach_timebase_info(&timebase);
151
152 usecs = mach_absolute_time();
153 usecs *= timebase.numer;
154 usecs /= timebase.denom;
155 usecs /= 1000;
156
157 cnow.tv_sec = usecs / 1000000;
158 cnow.tv_usec = (int)(usecs % 1000000);
159
160 return cnow;
161 }
162
163 #elif defined(HAVE_GETTIMEOFDAY)
164
Curl_now(void)165 struct curltime Curl_now(void)
166 {
167 /*
168 ** gettimeofday() is not granted to be increased monotonically, due to
169 ** clock drifting and external source time synchronization it can jump
170 ** forward or backward in time.
171 */
172 struct timeval now;
173 struct curltime ret;
174 (void)gettimeofday(&now, NULL);
175 ret.tv_sec = now.tv_sec;
176 ret.tv_usec = (int)now.tv_usec;
177 return ret;
178 }
179
180 #else
181
Curl_now(void)182 struct curltime Curl_now(void)
183 {
184 /*
185 ** time() returns the value of time in seconds since the Epoch.
186 */
187 struct curltime now;
188 now.tv_sec = time(NULL);
189 now.tv_usec = 0;
190 return now;
191 }
192
193 #endif
194
195 /*
196 * Returns: time difference in number of milliseconds. For too large diffs it
197 * returns max value.
198 *
199 * @unittest: 1323
200 */
Curl_timediff(struct curltime newer,struct curltime older)201 timediff_t Curl_timediff(struct curltime newer, struct curltime older)
202 {
203 timediff_t diff = (timediff_t)newer.tv_sec-older.tv_sec;
204 if(diff >= (TIMEDIFF_T_MAX/1000))
205 return TIMEDIFF_T_MAX;
206 else if(diff <= (TIMEDIFF_T_MIN/1000))
207 return TIMEDIFF_T_MIN;
208 return diff * 1000 + (newer.tv_usec-older.tv_usec)/1000;
209 }
210
211 /*
212 * Returns: time difference in number of milliseconds, rounded up.
213 * For too large diffs it returns max value.
214 */
Curl_timediff_ceil(struct curltime newer,struct curltime older)215 timediff_t Curl_timediff_ceil(struct curltime newer, struct curltime older)
216 {
217 timediff_t diff = (timediff_t)newer.tv_sec-older.tv_sec;
218 if(diff >= (TIMEDIFF_T_MAX/1000))
219 return TIMEDIFF_T_MAX;
220 else if(diff <= (TIMEDIFF_T_MIN/1000))
221 return TIMEDIFF_T_MIN;
222 return diff * 1000 + (newer.tv_usec - older.tv_usec + 999)/1000;
223 }
224
225 /*
226 * Returns: time difference in number of microseconds. For too large diffs it
227 * returns max value.
228 */
Curl_timediff_us(struct curltime newer,struct curltime older)229 timediff_t Curl_timediff_us(struct curltime newer, struct curltime older)
230 {
231 timediff_t diff = (timediff_t)newer.tv_sec-older.tv_sec;
232 if(diff >= (TIMEDIFF_T_MAX/1000000))
233 return TIMEDIFF_T_MAX;
234 else if(diff <= (TIMEDIFF_T_MIN/1000000))
235 return TIMEDIFF_T_MIN;
236 return diff * 1000000 + newer.tv_usec-older.tv_usec;
237 }
238