]> sjero.net Git - wget/commitdiff
[svn] Better selection of POSIX clocks.
authorhniksic <devnull@localhost>
Fri, 8 Apr 2005 18:19:20 +0000 (11:19 -0700)
committerhniksic <devnull@localhost>
Fri, 8 Apr 2005 18:19:20 +0000 (11:19 -0700)
src/ChangeLog
src/ptimer.c
src/ptimer.h
src/retr.c

index 1b74862d61b16aca07515d8e4dfb0a11c658f0a8..01fcb962a76519a317da7c527d8f0fb5831d059a 100644 (file)
@@ -1,3 +1,20 @@
+2005-04-08  Hrvoje Niksic  <hniksic@xemacs.org>
+
+       * ptimer.c (posix_init): Be smarter about choosing clocks.  In
+       decreasing order of preference, use CLOCK_MONOTONIC,
+       CLOCK_HIGHRES, and CLOCK_REALTIME.
+       (ptimer_allocate): Removed.
+
+       * ptimer.c: Refactor the code by cleanly separating the
+       architecture-dependent code from the architecture-independent
+       code.
+
+2005-04-08  Hrvoje Niksic  <hniksic@xemacs.org>
+
+       * ptimer.c (ptimer_init): Explicitly check that
+       _POSIX_MONOTONIC_CLOCK is *both* defined and >=0.  (Undefined
+       symbols are >=0.)
+
 2005-04-08  Hrvoje Niksic  <hniksic@xemacs.org>
 
        * ptimer.c (ptimer_diff): Fix typo affecting Windows build.
 2005-04-08  Hrvoje Niksic  <hniksic@xemacs.org>
 
        * ptimer.c (ptimer_diff): Fix typo affecting Windows build.
index 4fb54cf81c2d502e0e38fb37d326c5bb2322c148..d63e69e27821b11432aa1285fd00ebfbb3a390fc 100644 (file)
@@ -73,13 +73,8 @@ extern int errno;
 #endif
 
 /* Depending on the OS and availability of gettimeofday(), one and
 #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.  */
+   only one of PTIMER_POSIX, PTIMER_GETTIMEOFDAY, PTIMER_WINDOWS, or
+   PTIMER_TIME will be defined.  */
 
 #undef PTIMER_POSIX
 #undef PTIMER_GETTIMEOFDAY
 
 #undef PTIMER_POSIX
 #undef PTIMER_GETTIMEOFDAY
@@ -100,59 +95,202 @@ extern int errno;
 # endif
 #endif
 
 # endif
 #endif
 
-/* The type ptimer_system_time holds system time. */
-
 #ifdef PTIMER_POSIX
 #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;
 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, in milliseconds. */
+static double posix_millisec_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
+    { CLOCK_MONOTONIC, _SC_MONOTONIC_CLOCK },
+#endif
+#ifdef CLOCK_HIGHRES
+    { CLOCK_HIGHRES, NO_SYSCONF_CHECK },
 #endif
 #endif
+    { CLOCK_REALTIME, NO_SYSCONF_CHECK },
+  };
+  int 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_millisec_resolution = r.tv_sec * 1000.0 + r.tv_nsec / 1000000.0;
+      /* Guard against broken clock_getres returning nonsensical
+        values.  */
+      if (posix_millisec_resolution == 0)
+       posix_millisec_resolution = 1;
+      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_millisec_resolution = 1;
+    }
+}
+
+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) * 1000.0
+         + (pst1->tv_nsec - pst2->tv_nsec) / 1000000.0);
+}
+
+static inline double
+posix_resolution (void)
+{
+  return posix_millisec_resolution;
+}
+#endif /* PTIMER_POSIX */
 
 #ifdef PTIMER_GETTIMEOFDAY
 
 #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;
 typedef struct timeval 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) * 1000.0
+         + (pst1->tv_usec - pst2->tv_usec) / 1000.0);
+}
+
+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_TIME
 
 #ifdef PTIMER_TIME
+/* Elapsed time measurement using the time(2) call: system time is
+   held in time_t, retrieved using time, and resolution is 1 second.
+
+   This method is a catch-all for non-Windows systems without
+   gettimeofday -- e.g. DOS or really old or non-standard Unix
+   systems.  */
+
 typedef time_t ptimer_system_time;
 typedef time_t ptimer_system_time;
-#endif
+
+#define IMPL_measure time_measure
+#define IMPL_diff time_diff
+#define IMPL_resolution time_resolution
+
+static inline void
+time_measure (ptimer_system_time *pst)
+{
+  time (pst);
+}
+
+static inline double
+time_diff (ptimer_system_time *pst1, ptimer_system_time *pst2)
+{
+  return 1000.0 * (*pst1 - *pst2);
+}
+
+static inline double
+time_resolution (void)
+{
+  return 1;
+}
+#endif /* PTIMER_TIME */
 
 #ifdef PTIMER_WINDOWS
 
 #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;
 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;
+#define IMPL_init windows_init
+#define IMPL_measure windows_measure
+#define IMPL_diff windows_diff
+#define IMPL_resolution windows_resolution
 
 
-  /* The most recent elapsed time, calculated by ptimer_measure().
-     Measured in milliseconds.  */
-  double elapsed_last;
-
-  /* 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
 /* Whether high-resolution timers are used.  Set by ptimer_initialize_once
-   the first time ptimer_allocate is called. */
+   the first time ptimer_new is called. */
 static int windows_hires_timers;
 
 /* Frequency of high-resolution timers -- number of updates per
 static int windows_hires_timers;
 
 /* Frequency of high-resolution timers -- number of updates per
-   millisecond.  Calculated the first time ptimer_allocate is called
+   millisecond.  Calculated the first time ptimer_new is called
    provided that high-resolution timers are available. */
 static double windows_hires_msfreq;
 
    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. */
-
 static void
 static void
-ptimer_init (void)
+windows_init (void)
 {
   LARGE_INTEGER freq;
   freq.QuadPart = 0;
 {
   LARGE_INTEGER freq;
   freq.QuadPart = 0;
@@ -163,79 +301,74 @@ ptimer_init (void)
       windows_hires_msfreq = (double) freq.QuadPart / 1000.0;
     }
 }
       windows_hires_msfreq = (double) freq.QuadPart / 1000.0;
     }
 }
-#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 inline void
+windows_measure (ptimer_system_time *pst)
+{
+  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 ();
+}
 
 
-static void
-ptimer_init (void)
+static inline double
+windows_diff (ptimer_system_time *pst1, ptimer_system_time *pst2)
 {
 {
-  struct timespec res;
+  if (windows_hires_timers)
+    return (pst1->hires.QuadPart - pst2->hires.QuadPart) / windows_hires_msfreq;
+  else
+    return pst1->lores - pst2->lores;
+}
 
 
-#if _POSIX_MONOTONIC_CLOCK >= 0                   /* -1 means not supported */
-  if (sysconf (_SC_MONOTONIC_CLOCK) > 0)
-    posix_clock_id = CLOCK_MONOTONIC;
+static double
+windows_resolution (void)
+{
+  if (windows_hires_timers)
+    return 1.0 / windows_hires_msfreq;
   else
   else
-#endif
-    posix_clock_id = CLOCK_REALTIME;
+    return 10;                 /* according to MSDN */
+}
+#endif /* PTIMER_WINDOWS */
+\f
+/* The code below this point is independent of timer implementation. */
 
 
-  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;
-    }
+struct ptimer {
+  /* Whether the start time has been set. */
+  int initialized;
 
 
-  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;
-}
-#define PTIMER_INIT_DEFINED
-#endif
+  /* The starting point in time which, subtracted from the current
+     time, yields elapsed time. */
+  ptimer_system_time start;
 
 
-/* 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.  */
+  /* The most recent elapsed time, calculated by ptimer_measure().
+     Measured in milliseconds.  */
+  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 *
 
 struct ptimer *
-ptimer_allocate (void)
+ptimer_new (void)
 {
 {
-  struct ptimer *wt;
-
-#ifdef PTIMER_INIT_DEFINED
+  struct ptimer *wt = xnew0 (struct ptimer);
+#ifdef IMPL_init
   static int init_done;
   if (!init_done)
     {
       init_done = 1;
   static int init_done;
   if (!init_done)
     {
       init_done = 1;
-      ptimer_init ();
+      IMPL_init ();
     }
 #endif
     }
 #endif
-
-  wt = xnew0 (struct ptimer);
-  return wt;
-}
-
-/* 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;
 }
   ptimer_reset (wt);
   return wt;
 }
@@ -249,39 +382,6 @@ ptimer_destroy (struct ptimer *wt)
   xfree (wt);
 }
 
   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
-}
-
 /* 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 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.  */
@@ -290,37 +390,12 @@ void
 ptimer_reset (struct ptimer *wt)
 {
   /* Set the start time to the current time. */
 ptimer_reset (struct ptimer *wt)
 {
   /* Set the start time to the current time. */
-  ptimer_sys_set (&wt->start);
+  IMPL_measure (&wt->start);
   wt->elapsed_last = 0;
   wt->elapsed_pre_start = 0;
   wt->initialized = 1;
 }
 
   wt->elapsed_last = 0;
   wt->elapsed_pre_start = 0;
   wt->initialized = 1;
 }
 
-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 (windows_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.
 /* Measure the elapsed time since timer creation/reset and return it
    to the caller.  The value remains stored for further reads by
    ptimer_read.
@@ -340,8 +415,8 @@ ptimer_measure (struct ptimer *wt)
 
   assert (wt->initialized != 0);
 
 
   assert (wt->initialized != 0);
 
-  ptimer_sys_set (&now);
-  elapsed = wt->elapsed_pre_start + ptimer_diff (&now, &wt->start);
+  IMPL_measure (&now);
+  elapsed = wt->elapsed_pre_start + IMPL_diff (&now, &wt->start);
 
   /* Ideally we'd just return the difference between NOW and
      wt->start.  However, the system timer can be set back, and we
 
   /* Ideally we'd just return the difference between NOW and
      wt->start.  However, the system timer can be set back, and we
@@ -356,8 +431,8 @@ ptimer_measure (struct ptimer *wt)
      elapsed time and increment all future calculations by that
      amount.
 
      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 < wt->elapsed_last)
     {
@@ -379,34 +454,12 @@ ptimer_read (const struct ptimer *wt)
   return wt->elapsed_last;
 }
 
   return wt->elapsed_last;
 }
 
-/* Return the assessed granularity of the timer implementation, in
+/* Return the assessed resolution of the timer implementation, in
    milliseconds.  This is used by code that tries to substitute a
    better value for timers that have returned zero.  */
 
 double
    milliseconds.  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 ();
 }
 }
index 244c18367c8869445b4d9426b4b857d9c6f9ca63..481c0f1b6a29c5ac1175749e86c8df4ba033c8c3 100644 (file)
@@ -33,7 +33,6 @@ so, delete this exception statement from your version.  */
 struct ptimer;                 /* forward declaration; all struct
                                    members are private */
 
 struct ptimer;                 /* forward declaration; all struct
                                    members are private */
 
-struct ptimer *ptimer_allocate PARAMS ((void));
 struct ptimer *ptimer_new PARAMS ((void));
 void ptimer_destroy PARAMS ((struct ptimer *));
 
 struct ptimer *ptimer_new PARAMS ((void));
 void ptimer_destroy PARAMS ((struct ptimer *));
 
@@ -41,6 +40,6 @@ void ptimer_reset PARAMS ((struct ptimer *));
 double ptimer_measure PARAMS ((struct ptimer *));
 double ptimer_read PARAMS ((const struct ptimer *));
 
 double ptimer_measure PARAMS ((struct ptimer *));
 double ptimer_read PARAMS ((const struct ptimer *));
 
-double ptimer_granularity PARAMS ((void));
+double ptimer_resolution PARAMS ((void));
 
 #endif /* PTIMER_H */
 
 #endif /* PTIMER_H */
index a5198b7e591171792bf8b11360003d01c8dc4fed..59a1af3886d88f4aa6e325ac064d615e57dc44e3 100644 (file)
@@ -534,10 +534,10 @@ calc_rate (wgint bytes, double msecs, int *units)
 
   if (msecs == 0)
     /* If elapsed time is exactly zero, it means we're under the
 
   if (msecs == 0)
     /* If elapsed time is exactly zero, it means we're under the
-       granularity of the timer.  This can easily happen on systems
+       resolution of the timer.  This can easily happen on systems
        that use time() for the timer.  Since the interval lies between
        that use time() for the timer.  Since the interval lies between
-       0 and the timer's granularity, assume half the granularity.  */
-    msecs = ptimer_granularity () / 2.0;
+       0 and the timer's resolution, assume half the resolution.  */
+    msecs = ptimer_resolution () / 2.0;
 
   dlrate = 1000.0 * bytes / msecs;
   if (dlrate < 1024.0)
 
   dlrate = 1000.0 * bytes / msecs;
   if (dlrate < 1024.0)