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