Coverage Report

Created: 2026-01-09 07:25

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