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 milliseconds, but the timings they return
+ are 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 (...)
#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>
#endif
#include <assert.h>
+#include <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 "wget.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. */
+ only one of PTIMER_POSIX, PTIMER_GETTIMEOFDAY, PTIMER_WINDOWS, or
+ PTIMER_TIME will be defined. */
#undef PTIMER_POSIX
#undef PTIMER_GETTIMEOFDAY
#undef PTIMER_TIME
#undef PTIMER_WINDOWS
-#ifdef WINDOWS
+#if defined(WINDOWS) || defined(__CYGWIN__)
# define PTIMER_WINDOWS /* use Windows timers */
#else
-# if _POSIX_TIMERS > 0
+# if _POSIX_TIMERS - 0 > 0
# define PTIMER_POSIX /* use POSIX timers (clock_gettime) */
# else
# ifdef HAVE_GETTIMEOFDAY
# endif
#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, 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 >= 0
+ { CLOCK_MONOTONIC, _SC_MONOTONIC_CLOCK },
+#endif
+#ifdef CLOCK_HIGHRES
+ { CLOCK_HIGHRES, NO_SYSCONF_CHECK },
#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
+/* 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
+
+#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
+/* 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;
-#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
+/* 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. */
+ the first time ptimer_new is called. */
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;
-/* The first time a timer is created, determine whether to use
- high-resolution timers. */
-
static void
-ptimer_init (void)
+windows_init (void)
{
LARGE_INTEGER freq;
freq.QuadPart = 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
-#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 {
+ /* The starting point in time which, subtracted from the current
+ time, yields elapsed time. */
+ ptimer_system_time start;
- 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 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 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. */
+/* Allocate a new timer and reset it. Return the new timer. */
struct ptimer *
-ptimer_allocate (void)
+ptimer_new (void)
{
- struct ptimer *wt;
-
-#ifdef PTIMER_INIT_DEFINED
+ struct ptimer *pt = xnew0 (struct ptimer);
+#ifdef IMPL_init
static int init_done;
if (!init_done)
{
init_done = 1;
- ptimer_init ();
+ IMPL_init ();
}
#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 (pt);
+ return pt;
}
/* Free the resources associated with the timer. Its further use is
prohibited. */
void
-ptimer_destroy (struct ptimer *wt)
-{
- xfree (wt);
-}
-
-/* Store system time to PST. */
-
-static void
-ptimer_sys_set (ptimer_system_time *pst)
+ptimer_destroy (struct ptimer *pt)
{
-#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
+/* Reset timer PT. 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. */
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;
-}
-
-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
+ IMPL_measure (&pt->start);
+ pt->elapsed_last = 0;
+ pt->elapsed_pre_start = 0;
}
/* Measure the elapsed time since timer creation/reset and return it
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;
}
ptimer_reset and the last call to ptimer_update. */
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
+/* 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
-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 ();
}