xref: /third_party/curl/lib/timeval.c (revision 13498266)
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 */
33struct 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
63struct 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
137struct 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
165struct 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
182struct 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 */
201timediff_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 */
215timediff_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 */
229timediff_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