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