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