]> sjero.net Git - wget/blob - src/ptimer.c
[svn] Extract timers to a separate file.
[wget] / src / ptimer.c
1 /* Portable timers.
2    Copyright (C) 2005 Free Software Foundation, Inc.
3
4 This file is part of GNU Wget.
5
6 GNU Wget is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 GNU Wget is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Wget; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19
20 In addition, as a special exception, the Free Software Foundation
21 gives permission to link the code of its release of Wget with the
22 OpenSSL project's "OpenSSL" library (or with modified versions of it
23 that use the same license as the "OpenSSL" library), and distribute
24 the linked executables.  You must obey the GNU General Public License
25 in all respects for all of the code used other than "OpenSSL".  If you
26 modify this file, you may extend this exception to your version of the
27 file, but you are not obligated to do so.  If you do not wish to do
28 so, delete this exception statement from your version.  */
29
30 /* This file implements "portable timers" (ptimers), objects that
31    measure elapsed time using the primitives most appropriate for the
32    underlying operating system.  The entry points are:
33
34      ptimer_new     -- creates a timer.
35      ptimer_reset   -- resets the timer's elapsed time to zero.
36      ptimer_measure -- measure and return the time elapsed since
37                        creation or last reset.
38      ptimer_read    -- reads the last measured elapsed value.
39      ptimer_destroy -- destroy the timer.
40      ptimer_granularity -- returns the approximate granularity of the timers.
41
42    Timers operate in milliseconds, but return floating point values
43    that can be more precise.  For example, to measure the time it
44    takes to run a loop, you can use something like:
45
46      ptimer *tmr = ptimer_new ();
47      while (...)
48        ... loop ...
49      double msecs = ptimer_measure ();
50      printf ("The loop took %.2f ms\n", msecs);  */
51
52 #include <config.h>
53
54 #include <stdio.h>
55 #include <stdlib.h>
56 #ifdef HAVE_STRING_H
57 # include <string.h>
58 #else  /* not HAVE_STRING_H */
59 # include <strings.h>
60 #endif /* not HAVE_STRING_H */
61 #include <sys/types.h>
62 #include <errno.h>
63 #ifdef HAVE_UNISTD_H
64 # include <unistd.h>
65 #endif
66 #include <assert.h>
67
68 #include "wget.h"
69 #include "ptimer.h"
70
71 #ifndef errno
72 extern int errno;
73 #endif
74
75 /* Depending on the OS and availability of gettimeofday(), one and
76    only one of PTIMER_WINDOWS, PTIMER_GETTIMEOFDAY, or PTIMER_TIME will
77    be defined.
78
79    Virtually all modern Unix systems will define PTIMER_GETTIMEOFDAY;
80    Windows will use PTIMER_WINDOWS.  PTIMER_TIME is a catch-all method
81    for non-Windows systems without gettimeofday, such as DOS or really
82    old Unix-like systems.  */
83
84 #undef PTIMER_POSIX
85 #undef PTIMER_GETTIMEOFDAY
86 #undef PTIMER_TIME
87 #undef PTIMER_WINDOWS
88
89 #ifdef WINDOWS
90 # define PTIMER_WINDOWS         /* use Windows timers */
91 #else
92 # if _POSIX_TIMERS > 0
93 #  define PTIMER_POSIX          /* use POSIX timers (clock_gettime) */
94 # else
95 #  ifdef HAVE_GETTIMEOFDAY
96 #   define PTIMER_GETTIMEOFDAY  /* use gettimeofday */
97 #  else
98 #   define PTIMER_TIME
99 #  endif
100 # endif
101 #endif
102
103 /* The type ptimer_system_time holds system time. */
104
105 #ifdef PTIMER_POSIX
106 typedef struct timespec ptimer_system_time;
107 #endif
108
109 #ifdef PTIMER_GETTIMEOFDAY
110 typedef struct timeval ptimer_system_time;
111 #endif
112
113 #ifdef PTIMER_TIME
114 typedef time_t ptimer_system_time;
115 #endif
116
117 #ifdef PTIMER_WINDOWS
118 typedef union {
119   DWORD lores;          /* In case GetTickCount is used */
120   LARGE_INTEGER hires;  /* In case high-resolution timer is used */
121 } ptimer_system_time;
122 #endif
123
124 struct ptimer {
125   /* Whether the start time has been set. */
126   int initialized;
127
128   /* The starting point in time which, subtracted from the current
129      time, yields elapsed time. */
130   ptimer_system_time start;
131
132   /* The most recent elapsed time, calculated by ptimer_measure().
133      Measured in milliseconds.  */
134   double elapsed_last;
135
136   /* Approximately, the time elapsed between the true start of the
137      measurement and the time represented by START.  */
138   double elapsed_pre_start;
139 };
140
141 #ifdef PTIMER_WINDOWS
142 /* Whether high-resolution timers are used.  Set by ptimer_initialize_once
143    the first time ptimer_allocate is called. */
144 static int windows_hires_timers;
145
146 /* Frequency of high-resolution timers -- number of updates per
147    millisecond.  Calculated the first time ptimer_allocate is called
148    provided that high-resolution timers are available. */
149 static double windows_hires_msfreq;
150
151 /* The first time a timer is created, determine whether to use
152    high-resolution timers. */
153
154 static void
155 ptimer_init (void)
156 {
157   LARGE_INTEGER freq;
158   freq.QuadPart = 0;
159   QueryPerformanceFrequency (&freq);
160   if (freq.QuadPart != 0)
161     {
162       windows_hires_timers = 1;
163       windows_hires_msfreq = (double) freq.QuadPart / 1000.0;
164     }
165 }
166 #define PTIMER_INIT_DEFINED
167 #endif /* PTIMER_WINDOWS */
168
169 #ifdef PTIMER_POSIX
170
171 /* clock_id to use for POSIX clocks.  This tries to use
172    CLOCK_MONOTONIC where available, CLOCK_REALTIME otherwise.  */
173 static int posix_clock_id;
174
175 /* Resolution of the clock, in milliseconds. */
176 static double posix_resolution;
177
178 /* Check whether the monotonic clock is available, and retrieve POSIX
179    timer resolution.  */
180
181 static void
182 ptimer_init (void)
183 {
184   struct timespec res;
185
186 #if _POSIX_MONOTONIC_CLOCK > 0
187   if (sysconf (_SC_MONOTONIC_CLOCK) > 0)
188     posix_clock_id = CLOCK_MONOTONIC;
189   else
190 #endif
191     posix_clock_id = CLOCK_REALTIME;
192
193   if (clock_getres (posix_clock_id, &res) < 0)
194     {
195       logprintf (LOG_NOTQUIET, _("Cannot read clock resolution: %s\n"),
196                  strerror (errno));
197       /* Assume 1 ms resolution */
198       res.tv_sec = 0;
199       res.tv_nsec = 1000000;
200     }
201
202   posix_resolution = res.tv_sec * 1000.0 + res.tv_nsec / 1000000.0;
203   /* Guard against clock_getres reporting 0 resolution; after all, it
204      can be used for division.  */
205   if (posix_resolution == 0)
206     posix_resolution = 1;
207 }
208 #define PTIMER_INIT_DEFINED
209 #endif
210
211 /* Allocate a timer.  Calling ptimer_read on the timer will return
212    zero.  It is not legal to call ptimer_measure with a freshly
213    allocated timer -- use ptimer_reset first.  */
214
215 struct ptimer *
216 ptimer_allocate (void)
217 {
218   struct ptimer *wt;
219
220 #ifdef PTIMER_INIT_DEFINED
221   static int init_done;
222   if (!init_done)
223     {
224       init_done = 1;
225       ptimer_init ();
226     }
227 #endif
228
229   wt = xnew0 (struct ptimer);
230   return wt;
231 }
232
233 /* Allocate a new timer and reset it.  Return the new timer. */
234
235 struct ptimer *
236 ptimer_new (void)
237 {
238   struct ptimer *wt = ptimer_allocate ();
239   ptimer_reset (wt);
240   return wt;
241 }
242
243 /* Free the resources associated with the timer.  Its further use is
244    prohibited.  */
245
246 void
247 ptimer_destroy (struct ptimer *wt)
248 {
249   xfree (wt);
250 }
251
252 /* Store system time to PST.  */
253
254 static void
255 ptimer_sys_set (ptimer_system_time *pst)
256 {
257 #ifdef PTIMER_POSIX
258   clock_gettime (posix_clock_id, pst);
259 #endif
260
261 #ifdef PTIMER_GETTIMEOFDAY
262   gettimeofday (pst, NULL);
263 #endif
264
265 #ifdef PTIMER_TIME
266   time (pst);
267 #endif
268
269 #ifdef PTIMER_WINDOWS
270   if (windows_hires_timers)
271     {
272       QueryPerformanceCounter (&pst->hires);
273     }
274   else
275     {
276       /* Where hires counters are not available, use GetTickCount rather
277          GetSystemTime, because it is unaffected by clock skew and simpler
278          to use.  Note that overflows don't affect us because we never use
279          absolute values of the ticker, only the differences.  */
280       pst->lores = GetTickCount ();
281     }
282 #endif
283 }
284
285 /* Reset timer WT.  This establishes the starting point from which
286    ptimer_read() will return the number of elapsed milliseconds.
287    It is allowed to reset a previously used timer.  */
288
289 void
290 ptimer_reset (struct ptimer *wt)
291 {
292   /* Set the start time to the current time. */
293   ptimer_sys_set (&wt->start);
294   wt->elapsed_last = 0;
295   wt->elapsed_pre_start = 0;
296   wt->initialized = 1;
297 }
298
299 static double
300 ptimer_diff (ptimer_system_time *pst1, ptimer_system_time *pst2)
301 {
302 #ifdef PTIMER_POSIX
303   return ((pst1->tv_sec - pst2->tv_sec) * 1000.0
304           + (pst1->tv_nsec - pst2->tv_nsec) / 1000000.0);
305 #endif
306
307 #ifdef PTIMER_GETTIMEOFDAY
308   return ((pst1->tv_sec - pst2->tv_sec) * 1000.0
309           + (pst1->tv_usec - pst2->tv_usec) / 1000.0);
310 #endif
311
312 #ifdef PTIMER_TIME
313   return 1000 * (*pst1 - *pst2);
314 #endif
315
316 #ifdef WINDOWS
317   if (using_hires_timers)
318     return (pst1->hires.QuadPart - pst2->hires.QuadPart) / windows_hires_msfreq;
319   else
320     return pst1->lores - pst2->lores;
321 #endif
322 }
323
324 /* Measure the elapsed time since timer creation/reset and return it
325    to the caller.  The value remains stored for further reads by
326    ptimer_read.
327
328    This function causes the timer to call gettimeofday (or time(),
329    etc.) to update its idea of current time.  To get the elapsed
330    interval in milliseconds, use ptimer_read.
331
332    This function handles clock skew, i.e. time that moves backwards is
333    ignored.  */
334
335 double
336 ptimer_measure (struct ptimer *wt)
337 {
338   ptimer_system_time now;
339   double elapsed;
340
341   assert (wt->initialized != 0);
342
343   ptimer_sys_set (&now);
344   elapsed = wt->elapsed_pre_start + ptimer_diff (&now, &wt->start);
345
346   /* Ideally we'd just return the difference between NOW and
347      wt->start.  However, the system timer can be set back, and we
348      could return a value smaller than when we were last called, even
349      a negative value.  Both of these would confuse the callers, which
350      expect us to return monotonically nondecreasing values.
351
352      Therefore: if ELAPSED is smaller than its previous known value,
353      we reset wt->start to the current time and effectively start
354      measuring from this point.  But since we don't want the elapsed
355      value to start from zero, we set elapsed_pre_start to the last
356      elapsed time and increment all future calculations by that
357      amount.
358
359      This cannot happen with Windows and CLOCK_MONOTONIC timers, but
360      the check is not expensive.  */
361
362   if (elapsed < wt->elapsed_last)
363     {
364       wt->start = now;
365       wt->elapsed_pre_start = wt->elapsed_last;
366       elapsed = wt->elapsed_last;
367     }
368
369   wt->elapsed_last = elapsed;
370   return elapsed;
371 }
372
373 /* Return the elapsed time in milliseconds between the last call to
374    ptimer_reset and the last call to ptimer_update.  */
375
376 double
377 ptimer_read (const struct ptimer *wt)
378 {
379   return wt->elapsed_last;
380 }
381
382 /* Return the assessed granularity of the timer implementation, in
383    milliseconds.  This is used by code that tries to substitute a
384    better value for timers that have returned zero.  */
385
386 double
387 ptimer_granularity (void)
388 {
389 #ifdef PTIMER_POSIX
390   /* POSIX timers give us a way to measure granularity. */
391   assert (posix_resolution != 0);
392   return posix_resolution;
393 #endif
394
395 #ifdef PTIMER_GETTIMEOFDAY
396   /* Granularity of gettimeofday varies wildly between architectures.
397      However, it appears that on modern machines it tends to be better
398      than 1ms.  Assume 100 usecs.  */
399   return 0.1;
400 #endif
401
402 #ifdef PTIMER_TIME
403   return 1000;
404 #endif
405
406 #ifdef PTIMER_WINDOWS
407   if (windows_hires_timers)
408     return 1.0 / windows_hires_msfreq;
409   else
410     return 10;  /* according to MSDN */
411 #endif
412 }