xref: /curl/lib/timeval.c (revision e9a7d4a1)
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 = milliseconds / 1000;
55     now.tv_usec = (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 run-time 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 = (unsigned 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 = (unsigned 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   ** run-time. 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 = (unsigned 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 Mac OS 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