]> sjero.net Git - wget/blobdiff - src/ptimer.c
NEWS: cite --start-pos
[wget] / src / ptimer.c
index e6979216aa60399c7c97d7ccda883135cda24437..c53b5e7215cd08c6d4ed62763e01bd9325d504eb 100644 (file)
@@ -1,11 +1,12 @@
 /* Portable timers.
-   Copyright (C) 2005 Free Software Foundation, Inc.
+   Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software
+   Foundation, Inc.
 
 This file is part of GNU Wget.
 
 GNU Wget is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2 of the License, or
+the Free Software Foundation; either version 3 of the License, or
 (at your option) any later version.
 
 GNU Wget is distributed in the hope that it will be useful,
@@ -14,18 +15,18 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
-along with Wget; if not, write to the Free Software
-Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-In addition, as a special exception, the Free Software Foundation
-gives permission to link the code of its release of Wget with the
-OpenSSL project's "OpenSSL" library (or with modified versions of it
-that use the same license as the "OpenSSL" library), and distribute
-the linked executables.  You must obey the GNU General Public License
-in all respects for all of the code used other than "OpenSSL".  If you
-modify this file, you may extend this exception to your version of the
-file, but you are not obligated to do so.  If you do not wish to do
-so, delete this exception statement from your version.  */
+along with Wget.  If not, see <http://www.gnu.org/licenses/>.
+
+Additional permission under GNU GPL version 3 section 7
+
+If you modify this program, or any covered work, by linking or
+combining it with the OpenSSL project's OpenSSL library (or a
+modified version of that library), containing parts covered by the
+terms of the OpenSSL or SSLeay licenses, the Free Software Foundation
+grants you additional permission to convey the resulting work.
+Corresponding Source for a non-source form of such a combination
+shall include the source code for the parts of OpenSSL used as well
+as that of the covered work.  */
 
 /* This file implements "portable timers" (ptimers), objects that
    measure elapsed time using the primitives most appropriate for the
@@ -34,379 +35,379 @@ so, delete this exception statement from your version.  */
      ptimer_new     -- creates a timer.
      ptimer_reset   -- resets the timer's elapsed time to zero.
      ptimer_measure -- measure and return the time elapsed since
-                      creation or last reset.
+                       creation or last reset.
      ptimer_read    -- reads the last measured elapsed value.
      ptimer_destroy -- destroy the timer.
      ptimer_granularity -- returns the approximate granularity of the timers.
 
-   Timers operate in milliseconds, but return floating point values
-   that can be more precise.  For example, to measure the time it
-   takes to run a loop, you can use something like:
+   Timers measure time in seconds, returning the timings as floating
+   point numbers, so they can carry as much precision as the
+   underlying system timer supports.  For example, to measure the time
+   it takes to run a loop, you can use something like:
 
      ptimer *tmr = ptimer_new ();
      while (...)
        ... loop ...
-     double msecs = ptimer_measure ();
-     printf ("The loop took %.2f ms\n", msecs);  */
+     double secs = ptimer_measure ();
+     printf ("The loop took %.2fs\n", secs);  */
 
-#include <config.h>
+#include "wget.h"
 
 #include <stdio.h>
 #include <stdlib.h>
-#ifdef HAVE_STRING_H
-# include <string.h>
-#else  /* not HAVE_STRING_H */
-# include <strings.h>
-#endif /* not HAVE_STRING_H */
-#include <sys/types.h>
+#include <string.h>
 #include <errno.h>
-#ifdef HAVE_UNISTD_H
-# include <unistd.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/time.h>
+
+/* Cygwin currently (as of 2005-04-08, Cygwin 1.5.14) lacks clock_getres,
+   but still defines _POSIX_TIMERS!  Because of that we simply use the
+   Windows timers under Cygwin.  */
+#ifdef __CYGWIN__
+# include <windows.h>
 #endif
-#include <assert.h>
 
-#include "wget.h"
+#include "utils.h"
 #include "ptimer.h"
 
-#ifndef errno
-extern int errno;
-#endif
-
-/* Depending on the OS and availability of gettimeofday(), one and
-   only one of PTIMER_WINDOWS, PTIMER_GETTIMEOFDAY, or PTIMER_TIME will
-   be defined.
-
-   Virtually all modern Unix systems will define PTIMER_GETTIMEOFDAY;
-   Windows will use PTIMER_WINDOWS.  PTIMER_TIME is a catch-all method
-   for non-Windows systems without gettimeofday, such as DOS or really
-   old Unix-like systems.  */
+/* Depending on the OS, one and only one of PTIMER_POSIX,
+   PTIMER_GETTIMEOFDAY, or PTIMER_WINDOWS will be defined.  */
 
 #undef PTIMER_POSIX
 #undef PTIMER_GETTIMEOFDAY
-#undef PTIMER_TIME
 #undef PTIMER_WINDOWS
 
-#ifdef WINDOWS
-# define PTIMER_WINDOWS                /* use Windows timers */
+#if defined(WINDOWS) || defined(__CYGWIN__)
+# define PTIMER_WINDOWS         /* use Windows timers */
+#elif _POSIX_TIMERS - 0 > 0
+# define PTIMER_POSIX           /* use POSIX timers (clock_gettime) */
 #else
-# if _POSIX_TIMERS > 0
-#  define PTIMER_POSIX         /* use POSIX timers (clock_gettime) */
-# else
-#  ifdef HAVE_GETTIMEOFDAY
-#   define PTIMER_GETTIMEOFDAY /* use gettimeofday */
-#  else
-#   define PTIMER_TIME
-#  endif
-# endif
+# define PTIMER_GETTIMEOFDAY    /* use gettimeofday */
 #endif
 
-/* The type ptimer_system_time holds system time. */
-
 #ifdef PTIMER_POSIX
+/* Elapsed time measurement using POSIX timers: system time is held in
+   struct timespec, time is retrieved using clock_gettime, and
+   resolution using clock_getres.
+
+   This method is used on Unix systems that implement POSIX
+   timers.  */
+
 typedef struct timespec ptimer_system_time;
+
+#define IMPL_init posix_init
+#define IMPL_measure posix_measure
+#define IMPL_diff posix_diff
+#define IMPL_resolution posix_resolution
+
+/* clock_id to use for POSIX clocks.  This tries to use
+   CLOCK_MONOTONIC where available, CLOCK_REALTIME otherwise.  */
+static int posix_clock_id;
+
+/* Resolution of the clock, initialized in posix_init. */
+static double posix_clock_resolution;
+
+/* Decide which clock_id to use.  */
+
+static void
+posix_init (void)
+{
+  /* List of clocks we want to support: some systems support monotonic
+     clocks, Solaris has "high resolution" clock (sometimes
+     unavailable except to superuser), and all should support the
+     real-time clock.  */
+#define NO_SYSCONF_CHECK -1
+  static const struct {
+    int id;
+    int sysconf_name;
+  } clocks[] = {
+#if defined(_POSIX_MONOTONIC_CLOCK) && _POSIX_MONOTONIC_CLOCK - 0 >= 0
+    { CLOCK_MONOTONIC, _SC_MONOTONIC_CLOCK },
 #endif
+#ifdef CLOCK_HIGHRES
+    { CLOCK_HIGHRES, NO_SYSCONF_CHECK },
+#endif
+    { CLOCK_REALTIME, NO_SYSCONF_CHECK },
+  };
+  size_t i;
+
+  /* Determine the clock we can use.  For a clock to be usable, it
+     must be confirmed with sysconf (where applicable) and with
+     clock_getres.  If no clock is found, CLOCK_REALTIME is used.  */
+
+  for (i = 0; i < countof (clocks); i++)
+    {
+      struct timespec r;
+      if (clocks[i].sysconf_name != NO_SYSCONF_CHECK)
+        if (sysconf (clocks[i].sysconf_name) < 0)
+          continue;             /* sysconf claims this clock is unavailable */
+      if (clock_getres (clocks[i].id, &r) < 0)
+        continue;               /* clock_getres doesn't work for this clock */
+      posix_clock_id = clocks[i].id;
+      posix_clock_resolution = (double) r.tv_sec + r.tv_nsec / 1e9;
+      /* Guard against nonsense returned by a broken clock_getres.  */
+      if (posix_clock_resolution == 0)
+        posix_clock_resolution = 1e-3;
+      break;
+    }
+  if (i == countof (clocks))
+    {
+      /* If no clock was found, it means that clock_getres failed for
+         the realtime clock.  */
+      logprintf (LOG_NOTQUIET, _("Cannot get REALTIME clock frequency: %s\n"),
+                 strerror (errno));
+      /* Use CLOCK_REALTIME, but invent a plausible resolution. */
+      posix_clock_id = CLOCK_REALTIME;
+      posix_clock_resolution = 1e-3;
+    }
+}
+
+static inline void
+posix_measure (ptimer_system_time *pst)
+{
+  clock_gettime (posix_clock_id, pst);
+}
+
+static inline double
+posix_diff (ptimer_system_time *pst1, ptimer_system_time *pst2)
+{
+  return ((pst1->tv_sec - pst2->tv_sec)
+          + (pst1->tv_nsec - pst2->tv_nsec) / 1e9);
+}
+
+static inline double
+posix_resolution (void)
+{
+  return posix_clock_resolution;
+}
+#endif  /* PTIMER_POSIX */
 
 #ifdef PTIMER_GETTIMEOFDAY
+/* Elapsed time measurement using gettimeofday: system time is held in
+   struct timeval, retrieved using gettimeofday, and resolution is
+   unknown.
+
+   This method is used Unix systems without POSIX timers.  */
+
 typedef struct timeval ptimer_system_time;
-#endif
 
-#ifdef PTIMER_TIME
-typedef time_t ptimer_system_time;
-#endif
+#define IMPL_measure gettimeofday_measure
+#define IMPL_diff gettimeofday_diff
+#define IMPL_resolution gettimeofday_resolution
+
+static inline void
+gettimeofday_measure (ptimer_system_time *pst)
+{
+  gettimeofday (pst, NULL);
+}
+
+static inline double
+gettimeofday_diff (ptimer_system_time *pst1, ptimer_system_time *pst2)
+{
+  return ((pst1->tv_sec - pst2->tv_sec)
+          + (pst1->tv_usec - pst2->tv_usec) / 1e6);
+}
+
+static inline double
+gettimeofday_resolution (void)
+{
+  /* Granularity of gettimeofday varies wildly between architectures.
+     However, it appears that on modern machines it tends to be better
+     than 1ms.  Assume 100 usecs.  */
+  return 0.1;
+}
+#endif  /* PTIMER_GETTIMEOFDAY */
 
 #ifdef PTIMER_WINDOWS
+/* Elapsed time measurement on Windows: where high-resolution timers
+   are available, time is stored in a LARGE_INTEGER and retrieved
+   using QueryPerformanceCounter.  Otherwise, it is stored in a DWORD
+   and retrieved using GetTickCount.
+
+   This method is used on Windows.  */
+
 typedef union {
   DWORD lores;          /* In case GetTickCount is used */
   LARGE_INTEGER hires;  /* In case high-resolution timer is used */
 } ptimer_system_time;
-#endif
-
-struct ptimer {
-  /* Whether the start time has been set. */
-  int initialized;
 
-  /* The starting point in time which, subtracted from the current
-     time, yields elapsed time. */
-  ptimer_system_time start;
-
-  /* The most recent elapsed time, calculated by ptimer_measure().
-     Measured in milliseconds.  */
-  double elapsed_last;
+#define IMPL_init windows_init
+#define IMPL_measure windows_measure
+#define IMPL_diff windows_diff
+#define IMPL_resolution windows_resolution
 
-  /* Approximately, the time elapsed between the true start of the
-     measurement and the time represented by START.  */
-  double elapsed_pre_start;
-};
-
-#ifdef PTIMER_WINDOWS
 /* Whether high-resolution timers are used.  Set by ptimer_initialize_once
-   the first time ptimer_allocate is called. */
-static int windows_hires_timers;
+   the first time ptimer_new is called. */
+static bool windows_hires_timers;
 
 /* Frequency of high-resolution timers -- number of updates per
-   millisecond.  Calculated the first time ptimer_allocate is called
-   provided that high-resolution timers are available. */
-static double windows_hires_msfreq;
-
-/* The first time a timer is created, determine whether to use
-   high-resolution timers. */
+   second.  Calculated the first time ptimer_new is called provided
+   that high-resolution timers are available. */
+static double windows_hires_freq;
 
 static void
-ptimer_init (void)
+windows_init (void)
 {
   LARGE_INTEGER freq;
   freq.QuadPart = 0;
   QueryPerformanceFrequency (&freq);
   if (freq.QuadPart != 0)
     {
-      windows_hires_timers = 1;
-      windows_hires_msfreq = (double) freq.QuadPart / 1000.0;
+      windows_hires_timers = true;
+      windows_hires_freq = (double) freq.QuadPart;
     }
 }
-#define PTIMER_INIT_DEFINED
-#endif /* PTIMER_WINDOWS */
 
-#ifdef PTIMER_POSIX
-
-/* clock_id to use for POSIX clocks.  This tries to use
-   CLOCK_MONOTONIC where available, CLOCK_REALTIME otherwise.  */
-static int posix_clock_id;
-
-/* Resolution of the clock, in milliseconds. */
-static double posix_resolution;
-
-/* Check whether the monotonic clock is available, and retrieve POSIX
-   timer resolution.  */
-
-static void
-ptimer_init (void)
+static inline void
+windows_measure (ptimer_system_time *pst)
 {
-  struct timespec res;
-
-#if _POSIX_MONOTONIC_CLOCK > 0
-  if (sysconf (_SC_MONOTONIC_CLOCK) > 0)
-    posix_clock_id = CLOCK_MONOTONIC;
+  if (windows_hires_timers)
+    QueryPerformanceCounter (&pst->hires);
   else
-#endif
-    posix_clock_id = CLOCK_REALTIME;
-
-  if (clock_getres (posix_clock_id, &res) < 0)
-    {
-      logprintf (LOG_NOTQUIET, _("Cannot read clock resolution: %s\n"),
-                strerror (errno));
-      /* Assume 1 ms resolution */
-      res.tv_sec = 0;
-      res.tv_nsec = 1000000;
-    }
-
-  posix_resolution = res.tv_sec * 1000.0 + res.tv_nsec / 1000000.0;
-  /* Guard against clock_getres reporting 0 resolution; after all, it
-     can be used for division.  */
-  if (posix_resolution == 0)
-    posix_resolution = 1;
+    /* Where hires counters are not available, use GetTickCount rather
+       GetSystemTime, because it is unaffected by clock skew and
+       simpler to use.  Note that overflows don't affect us because we
+       never use absolute values of the ticker, only the
+       differences.  */
+    pst->lores = GetTickCount ();
 }
-#define PTIMER_INIT_DEFINED
-#endif
 
-/* Allocate a timer.  Calling ptimer_read on the timer will return
-   zero.  It is not legal to call ptimer_measure with a freshly
-   allocated timer -- use ptimer_reset first.  */
+static inline double
+windows_diff (ptimer_system_time *pst1, ptimer_system_time *pst2)
+{
+  if (windows_hires_timers)
+    return (pst1->hires.QuadPart - pst2->hires.QuadPart) / windows_hires_freq;
+  else
+    return pst1->lores - pst2->lores;
+}
 
-struct ptimer *
-ptimer_allocate (void)
+static double
+windows_resolution (void)
 {
-  struct ptimer *wt;
+  if (windows_hires_timers)
+    return 1.0 / windows_hires_freq;
+  else
+    return 10;                  /* according to MSDN */
+}
+#endif  /* PTIMER_WINDOWS */
+\f
+/* The code below this point is independent of timer implementation. */
 
-#ifdef PTIMER_INIT_DEFINED
-  static int init_done;
-  if (!init_done)
-    {
-      init_done = 1;
-      ptimer_init ();
-    }
-#endif
+struct ptimer {
+  /* The starting point in time which, subtracted from the current
+     time, yields elapsed time. */
+  ptimer_system_time start;
 
-  wt = xnew0 (struct ptimer);
-  return wt;
-}
+  /* The most recent elapsed time, calculated by ptimer_measure().  */
+  double elapsed_last;
+
+  /* Approximately, the time elapsed between the true start of the
+     measurement and the time represented by START.  This is used for
+     adjustment when clock skew is detected.  */
+  double elapsed_pre_start;
+};
 
 /* Allocate a new timer and reset it.  Return the new timer. */
 
 struct ptimer *
 ptimer_new (void)
 {
-  struct ptimer *wt = ptimer_allocate ();
-  ptimer_reset (wt);
-  return wt;
+  struct ptimer *pt = xnew0 (struct ptimer);
+#ifdef IMPL_init
+  static bool init_done;
+  if (!init_done)
+    {
+      init_done = true;
+      IMPL_init ();
+    }
+#endif
+  ptimer_reset (pt);
+  return pt;
 }
 
 /* Free the resources associated with the timer.  Its further use is
    prohibited.  */
 
 void
-ptimer_destroy (struct ptimer *wt)
+ptimer_destroy (struct ptimer *pt)
 {
-  xfree (wt);
-}
-
-/* Store system time to PST.  */
-
-static void
-ptimer_sys_set (ptimer_system_time *pst)
-{
-#ifdef PTIMER_POSIX
-  clock_gettime (posix_clock_id, pst);
-#endif
-
-#ifdef PTIMER_GETTIMEOFDAY
-  gettimeofday (pst, NULL);
-#endif
-
-#ifdef PTIMER_TIME
-  time (pst);
-#endif
-
-#ifdef PTIMER_WINDOWS
-  if (windows_hires_timers)
-    {
-      QueryPerformanceCounter (&pst->hires);
-    }
-  else
-    {
-      /* Where hires counters are not available, use GetTickCount rather
-         GetSystemTime, because it is unaffected by clock skew and simpler
-         to use.  Note that overflows don't affect us because we never use
-         absolute values of the ticker, only the differences.  */
-      pst->lores = GetTickCount ();
-    }
-#endif
+  xfree (pt);
 }
 
-/* Reset timer WT.  This establishes the starting point from which
-   ptimer_read() will return the number of elapsed milliseconds.
-   It is allowed to reset a previously used timer.  */
+/* Reset timer PT.  This establishes the starting point from which
+   ptimer_measure() will return the elapsed time in seconds.  It is
+   allowed to reset a previously used timer.  */
 
 void
-ptimer_reset (struct ptimer *wt)
+ptimer_reset (struct ptimer *pt)
 {
   /* Set the start time to the current time. */
-  ptimer_sys_set (&wt->start);
-  wt->elapsed_last = 0;
-  wt->elapsed_pre_start = 0;
-  wt->initialized = 1;
+  IMPL_measure (&pt->start);
+  pt->elapsed_last = 0;
+  pt->elapsed_pre_start = 0;
 }
 
-static double
-ptimer_diff (ptimer_system_time *pst1, ptimer_system_time *pst2)
-{
-#ifdef PTIMER_POSIX
-  return ((pst1->tv_sec - pst2->tv_sec) * 1000.0
-         + (pst1->tv_nsec - pst2->tv_nsec) / 1000000.0);
-#endif
-
-#ifdef PTIMER_GETTIMEOFDAY
-  return ((pst1->tv_sec - pst2->tv_sec) * 1000.0
-         + (pst1->tv_usec - pst2->tv_usec) / 1000.0);
-#endif
-
-#ifdef PTIMER_TIME
-  return 1000 * (*pst1 - *pst2);
-#endif
-
-#ifdef WINDOWS
-  if (using_hires_timers)
-    return (pst1->hires.QuadPart - pst2->hires.QuadPart) / windows_hires_msfreq;
-  else
-    return pst1->lores - pst2->lores;
-#endif
-}
-
-/* Measure the elapsed time since timer creation/reset and return it
-   to the caller.  The value remains stored for further reads by
-   ptimer_read.
-
-   This function causes the timer to call gettimeofday (or time(),
-   etc.) to update its idea of current time.  To get the elapsed
-   interval in milliseconds, use ptimer_read.
+/* Measure the elapsed time since timer creation/reset.  This causes
+   the timer to internally call clock_gettime (or gettimeofday, etc.)
+   to update its idea of current time.  The time is returned, but is
+   also stored for later access through ptimer_read().
 
    This function handles clock skew, i.e. time that moves backwards is
    ignored.  */
 
 double
-ptimer_measure (struct ptimer *wt)
+ptimer_measure (struct ptimer *pt)
 {
   ptimer_system_time now;
   double elapsed;
 
-  assert (wt->initialized != 0);
-
-  ptimer_sys_set (&now);
-  elapsed = wt->elapsed_pre_start + ptimer_diff (&now, &wt->start);
+  IMPL_measure (&now);
+  elapsed = pt->elapsed_pre_start + IMPL_diff (&now, &pt->start);
 
   /* Ideally we'd just return the difference between NOW and
-     wt->start.  However, the system timer can be set back, and we
+     pt->start.  However, the system timer can be set back, and we
      could return a value smaller than when we were last called, even
      a negative value.  Both of these would confuse the callers, which
      expect us to return monotonically nondecreasing values.
 
      Therefore: if ELAPSED is smaller than its previous known value,
-     we reset wt->start to the current time and effectively start
+     we reset pt->start to the current time and effectively start
      measuring from this point.  But since we don't want the elapsed
      value to start from zero, we set elapsed_pre_start to the last
      elapsed time and increment all future calculations by that
      amount.
 
-     This cannot happen with Windows and CLOCK_MONOTONIC timers, but
-     the check is not expensive.  */
+     This cannot happen with Windows and POSIX monotonic/highres
+     timers, but the check is not expensive.  */
 
-  if (elapsed < wt->elapsed_last)
+  if (elapsed < pt->elapsed_last)
     {
-      wt->start = now;
-      wt->elapsed_pre_start = wt->elapsed_last;
-      elapsed = wt->elapsed_last;
+      pt->start = now;
+      pt->elapsed_pre_start = pt->elapsed_last;
+      elapsed = pt->elapsed_last;
     }
 
-  wt->elapsed_last = elapsed;
+  pt->elapsed_last = elapsed;
   return elapsed;
 }
 
-/* Return the elapsed time in milliseconds between the last call to
-   ptimer_reset and the last call to ptimer_update.  */
+/* Return the most recent elapsed time measured with ptimer_measure.
+   If ptimer_measure has not yet been called since the timer was
+   created or reset, this returns 0.  */
 
 double
-ptimer_read (const struct ptimer *wt)
+ptimer_read (const struct ptimer *pt)
 {
-  return wt->elapsed_last;
+  return pt->elapsed_last;
 }
 
-/* Return the assessed granularity of the timer implementation, in
-   milliseconds.  This is used by code that tries to substitute a
-   better value for timers that have returned zero.  */
+/* Return the assessed resolution of the timer implementation, in
+   seconds.  This is used by code that tries to substitute a better
+   value for timers that have returned zero.  */
 
 double
-ptimer_granularity (void)
+ptimer_resolution (void)
 {
-#ifdef PTIMER_POSIX
-  /* POSIX timers give us a way to measure granularity. */
-  assert (posix_resolution != 0);
-  return posix_resolution;
-#endif
-
-#ifdef PTIMER_GETTIMEOFDAY
-  /* Granularity of gettimeofday varies wildly between architectures.
-     However, it appears that on modern machines it tends to be better
-     than 1ms.  Assume 100 usecs.  */
-  return 0.1;
-#endif
-
-#ifdef PTIMER_TIME
-  return 1000;
-#endif
-
-#ifdef PTIMER_WINDOWS
-  if (windows_hires_timers)
-    return 1.0 / windows_hires_msfreq;
-  else
-    return 10;  /* according to MSDN */
-#endif
+  return IMPL_resolution ();
 }