/src/curl/lib/curlx/timeval.c
Line | Count | Source |
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 | | #include "timeval.h" |
25 | | |
26 | | #ifdef _WIN32 |
27 | | |
28 | | #include "version_win32.h" |
29 | | #include "../system_win32.h" |
30 | | |
31 | | LARGE_INTEGER Curl_freq; |
32 | | bool Curl_isVistaOrGreater; |
33 | | |
34 | | /* For tool or tests, we must initialize before calling curlx_now(). |
35 | | Providing this function here is wrong. */ |
36 | | void curlx_now_init(void) |
37 | | { |
38 | | if(curlx_verify_windows_version(6, 0, 0, PLATFORM_WINNT, |
39 | | VERSION_GREATER_THAN_EQUAL)) |
40 | | Curl_isVistaOrGreater = true; |
41 | | else |
42 | | Curl_isVistaOrGreater = false; |
43 | | |
44 | | QueryPerformanceFrequency(&Curl_freq); |
45 | | } |
46 | | |
47 | | /* In case of bug fix this function has a counterpart in tool_util.c */ |
48 | | void curlx_pnow(struct curltime *pnow) |
49 | | { |
50 | | bool isVistaOrGreater; |
51 | | isVistaOrGreater = Curl_isVistaOrGreater; |
52 | | if(isVistaOrGreater) { /* QPC timer might have issues pre-Vista */ |
53 | | LARGE_INTEGER count; |
54 | | LARGE_INTEGER freq; |
55 | | freq = Curl_freq; |
56 | | DEBUGASSERT(freq.QuadPart); |
57 | | QueryPerformanceCounter(&count); |
58 | | pnow->tv_sec = (time_t)(count.QuadPart / freq.QuadPart); |
59 | | pnow->tv_usec = (int)((count.QuadPart % freq.QuadPart) * 1000000 / |
60 | | freq.QuadPart); |
61 | | } |
62 | | else { |
63 | | /* Disable /analyze warning that GetTickCount64 is preferred */ |
64 | | #ifdef _MSC_VER |
65 | | #pragma warning(push) |
66 | | #pragma warning(disable:28159) |
67 | | #endif |
68 | | DWORD milliseconds = GetTickCount(); |
69 | | #ifdef _MSC_VER |
70 | | #pragma warning(pop) |
71 | | #endif |
72 | | |
73 | | pnow->tv_sec = (time_t)(milliseconds / 1000); |
74 | | pnow->tv_usec = (int)((milliseconds % 1000) * 1000); |
75 | | } |
76 | | } |
77 | | |
78 | | #elif defined(HAVE_CLOCK_GETTIME_MONOTONIC) || \ |
79 | | defined(HAVE_CLOCK_GETTIME_MONOTONIC_RAW) |
80 | | |
81 | | void curlx_pnow(struct curltime *pnow) |
82 | 195M | { |
83 | | /* |
84 | | ** clock_gettime() is granted to be increased monotonically when the |
85 | | ** monotonic clock is queried. Time starting point is unspecified, it |
86 | | ** could be the system start-up time, the Epoch, or something else, |
87 | | ** in any case the time starting point does not change once that the |
88 | | ** system has started up. |
89 | | */ |
90 | 195M | struct timespec tsnow; |
91 | | |
92 | | /* |
93 | | ** clock_gettime() may be defined by Apple's SDK as weak symbol thus |
94 | | ** code compiles but fails during runtime if clock_gettime() is |
95 | | ** called on unsupported OS version. |
96 | | */ |
97 | | #if defined(__APPLE__) && defined(HAVE_BUILTIN_AVAILABLE) && \ |
98 | | (HAVE_BUILTIN_AVAILABLE == 1) |
99 | | bool have_clock_gettime = FALSE; |
100 | | if(__builtin_available(macOS 10.12, iOS 10, tvOS 10, watchOS 3, *)) |
101 | | have_clock_gettime = TRUE; |
102 | | #endif |
103 | | |
104 | 195M | #ifdef HAVE_CLOCK_GETTIME_MONOTONIC_RAW |
105 | 195M | if( |
106 | | #if defined(__APPLE__) && defined(HAVE_BUILTIN_AVAILABLE) && \ |
107 | | (HAVE_BUILTIN_AVAILABLE == 1) |
108 | | have_clock_gettime && |
109 | | #endif |
110 | 195M | (clock_gettime(CLOCK_MONOTONIC_RAW, &tsnow) == 0)) { |
111 | 195M | pnow->tv_sec = tsnow.tv_sec; |
112 | 195M | pnow->tv_usec = (int)(tsnow.tv_nsec / 1000); |
113 | 195M | } |
114 | 0 | else |
115 | 0 | #endif |
116 | | |
117 | 0 | if( |
118 | | #if defined(__APPLE__) && defined(HAVE_BUILTIN_AVAILABLE) && \ |
119 | | (HAVE_BUILTIN_AVAILABLE == 1) |
120 | | have_clock_gettime && |
121 | | #endif |
122 | 0 | (clock_gettime(CLOCK_MONOTONIC, &tsnow) == 0)) { |
123 | 0 | pnow->tv_sec = tsnow.tv_sec; |
124 | 0 | pnow->tv_usec = (int)(tsnow.tv_nsec / 1000); |
125 | 0 | } |
126 | | /* |
127 | | ** Even when the configure process has truly detected monotonic clock |
128 | | ** availability, it might happen that it is not actually available at |
129 | | ** runtime. When this occurs simply fallback to other time source. |
130 | | */ |
131 | 0 | #ifdef HAVE_GETTIMEOFDAY |
132 | 0 | else { |
133 | 0 | struct timeval now; |
134 | 0 | (void)gettimeofday(&now, NULL); |
135 | 0 | pnow->tv_sec = now.tv_sec; |
136 | 0 | pnow->tv_usec = (int)now.tv_usec; |
137 | 0 | } |
138 | | #else |
139 | | else { |
140 | | pnow->tv_sec = time(NULL); |
141 | | pnow->tv_usec = 0; |
142 | | } |
143 | | #endif |
144 | 195M | } |
145 | | |
146 | | #elif defined(HAVE_MACH_ABSOLUTE_TIME) |
147 | | |
148 | | #include <stdint.h> |
149 | | #include <mach/mach_time.h> |
150 | | |
151 | | void curlx_pnow(struct curltime *pnow) |
152 | | { |
153 | | /* |
154 | | ** Monotonic timer on macOS is provided by mach_absolute_time(), which |
155 | | ** returns time in Mach "absolute time units," which are platform-dependent. |
156 | | ** To convert to nanoseconds, one must use conversion factors specified by |
157 | | ** mach_timebase_info(). |
158 | | */ |
159 | | static mach_timebase_info_data_t timebase; |
160 | | uint64_t usecs; |
161 | | |
162 | | if(timebase.denom == 0) |
163 | | (void)mach_timebase_info(&timebase); |
164 | | |
165 | | usecs = mach_absolute_time(); |
166 | | usecs *= timebase.numer; /* spellchecker:disable-line */ |
167 | | usecs /= timebase.denom; |
168 | | usecs /= 1000; |
169 | | |
170 | | pnow->tv_sec = usecs / 1000000; |
171 | | pnow->tv_usec = (int)(usecs % 1000000); |
172 | | } |
173 | | |
174 | | #elif defined(HAVE_GETTIMEOFDAY) |
175 | | |
176 | | void curlx_pnow(struct curltime *pnow) |
177 | | { |
178 | | /* |
179 | | ** gettimeofday() is not granted to be increased monotonically, due to |
180 | | ** clock drifting and external source time synchronization it can jump |
181 | | ** forward or backward in time. |
182 | | */ |
183 | | struct timeval now; |
184 | | (void)gettimeofday(&now, NULL); |
185 | | pnow->tv_sec = now.tv_sec; |
186 | | pnow->tv_usec = (int)now.tv_usec; |
187 | | } |
188 | | |
189 | | #else |
190 | | |
191 | | void curlx_pnow(struct curltime *pnow) |
192 | | { |
193 | | /* |
194 | | ** time() returns the value of time in seconds since the Epoch. |
195 | | */ |
196 | | pnow->tv_sec = time(NULL); |
197 | | pnow->tv_usec = 0; |
198 | | } |
199 | | |
200 | | #endif |
201 | | |
202 | | struct curltime curlx_now(void) |
203 | 0 | { |
204 | 0 | struct curltime now; |
205 | 0 | curlx_pnow(&now); |
206 | 0 | return now; |
207 | 0 | } |
208 | | |
209 | | /* |
210 | | * Returns: time difference in number of milliseconds. For too large diffs it |
211 | | * returns max value. |
212 | | * |
213 | | * @unittest: 1323 |
214 | | */ |
215 | | timediff_t curlx_ptimediff_ms(const struct curltime *newer, |
216 | | const struct curltime *older) |
217 | 137M | { |
218 | 137M | timediff_t diff = (timediff_t)newer->tv_sec - older->tv_sec; |
219 | 137M | if(diff >= (TIMEDIFF_T_MAX / 1000)) |
220 | 0 | return TIMEDIFF_T_MAX; |
221 | 137M | else if(diff <= (TIMEDIFF_T_MIN / 1000)) |
222 | 0 | return TIMEDIFF_T_MIN; |
223 | 137M | return diff * 1000 + (newer->tv_usec - older->tv_usec) / 1000; |
224 | 137M | } |
225 | | |
226 | | |
227 | | timediff_t curlx_timediff_ms(struct curltime newer, struct curltime older) |
228 | 0 | { |
229 | 0 | return curlx_ptimediff_ms(&newer, &older); |
230 | 0 | } |
231 | | |
232 | | /* |
233 | | * Returns: time difference in number of milliseconds, rounded up. |
234 | | * For too large diffs it returns max value. |
235 | | */ |
236 | | timediff_t curlx_timediff_ceil_ms(struct curltime newer, |
237 | | struct curltime older) |
238 | 0 | { |
239 | 0 | timediff_t diff = (timediff_t)newer.tv_sec - older.tv_sec; |
240 | 0 | if(diff >= (TIMEDIFF_T_MAX / 1000)) |
241 | 0 | return TIMEDIFF_T_MAX; |
242 | 0 | else if(diff <= (TIMEDIFF_T_MIN / 1000)) |
243 | 0 | return TIMEDIFF_T_MIN; |
244 | 0 | return diff * 1000 + (newer.tv_usec - older.tv_usec + 999) / 1000; |
245 | 0 | } |
246 | | |
247 | | /* |
248 | | * Returns: time difference in number of microseconds. For too large diffs it |
249 | | * returns max value. |
250 | | */ |
251 | | timediff_t curlx_ptimediff_us(const struct curltime *newer, |
252 | | const struct curltime *older) |
253 | 115M | { |
254 | 115M | timediff_t diff = (timediff_t)newer->tv_sec - older->tv_sec; |
255 | 115M | if(diff >= (TIMEDIFF_T_MAX / 1000000)) |
256 | 0 | return TIMEDIFF_T_MAX; |
257 | 115M | else if(diff <= (TIMEDIFF_T_MIN / 1000000)) |
258 | 0 | return TIMEDIFF_T_MIN; |
259 | 115M | return diff * 1000000 + newer->tv_usec - older->tv_usec; |
260 | 115M | } |
261 | | |
262 | | timediff_t curlx_timediff_us(struct curltime newer, struct curltime older) |
263 | 0 | { |
264 | 0 | return curlx_ptimediff_us(&newer, &older); |
265 | 0 | } |
266 | | |
267 | | #if defined(__MINGW32__) && (__MINGW64_VERSION_MAJOR <= 3) |
268 | | #include <sec_api/time_s.h> /* for _gmtime32_s(), _gmtime64_s() */ |
269 | | #ifdef _USE_32BIT_TIME_T |
270 | | #define gmtime_s _gmtime32_s |
271 | | #else |
272 | | #define gmtime_s _gmtime64_s |
273 | | #endif |
274 | | #endif |
275 | | |
276 | | /* |
277 | | * curlx_gmtime() is a gmtime() replacement for portability. Do not use |
278 | | * the gmtime_s(), gmtime_r() or gmtime() functions anywhere else but here. |
279 | | */ |
280 | | CURLcode curlx_gmtime(time_t intime, struct tm *store) |
281 | 15.2k | { |
282 | | #ifdef _WIN32 |
283 | | if(gmtime_s(store, &intime)) /* thread-safe */ |
284 | | return CURLE_BAD_FUNCTION_ARGUMENT; |
285 | | #elif defined(HAVE_GMTIME_R) |
286 | | const struct tm *tm; |
287 | 15.2k | tm = gmtime_r(&intime, store); /* thread-safe */ |
288 | 15.2k | if(!tm) |
289 | 59 | return CURLE_BAD_FUNCTION_ARGUMENT; |
290 | | #else |
291 | | const struct tm *tm; |
292 | | /* !checksrc! disable BANNEDFUNC 1 */ |
293 | | tm = gmtime(&intime); /* not thread-safe */ |
294 | | if(tm) |
295 | | *store = *tm; /* copy the pointed struct to the local copy */ |
296 | | else |
297 | | return CURLE_BAD_FUNCTION_ARGUMENT; |
298 | | #endif |
299 | | |
300 | 15.1k | return CURLE_OK; |
301 | 15.2k | } |