mirror of
https://github.com/moparisthebest/wget
synced 2024-07-03 16:38:41 -04:00
[svn] Better selection of POSIX clocks.
This commit is contained in:
parent
ab05800310
commit
14a2d25b2e
@ -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.
|
||||
|
441
src/ptimer.c
441
src/ptimer.c
@ -73,13 +73,8 @@ 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
|
||||
@ -100,26 +95,247 @@ extern int errno;
|
||||
# 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
|
||||
{ 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
|
||||
|
||||
#define IMPL_init windows_init
|
||||
#define IMPL_measure windows_measure
|
||||
#define IMPL_diff windows_diff
|
||||
#define IMPL_resolution windows_resolution
|
||||
|
||||
/* Whether high-resolution timers are used. Set by ptimer_initialize_once
|
||||
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_new is called
|
||||
provided that high-resolution timers are available. */
|
||||
static double windows_hires_msfreq;
|
||||
|
||||
static 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;
|
||||
}
|
||||
}
|
||||
|
||||
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 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_msfreq;
|
||||
else
|
||||
return pst1->lores - pst2->lores;
|
||||
}
|
||||
|
||||
static double
|
||||
windows_resolution (void)
|
||||
{
|
||||
if (windows_hires_timers)
|
||||
return 1.0 / windows_hires_msfreq;
|
||||
else
|
||||
return 10; /* according to MSDN */
|
||||
}
|
||||
#endif /* PTIMER_WINDOWS */
|
||||
|
||||
/* The code below this point is independent of timer implementation. */
|
||||
|
||||
struct ptimer {
|
||||
/* Whether the start time has been set. */
|
||||
@ -134,108 +350,25 @@ struct ptimer {
|
||||
double elapsed_last;
|
||||
|
||||
/* Approximately, the time elapsed between the true start of the
|
||||
measurement and the time represented by START. */
|
||||
measurement and the time represented by START. This is used for
|
||||
adjustment when clock skew is detected. */
|
||||
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;
|
||||
|
||||
/* 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. */
|
||||
|
||||
static void
|
||||
ptimer_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;
|
||||
}
|
||||
}
|
||||
#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)
|
||||
{
|
||||
struct timespec res;
|
||||
|
||||
#if _POSIX_MONOTONIC_CLOCK >= 0 /* -1 means not supported */
|
||||
if (sysconf (_SC_MONOTONIC_CLOCK) > 0)
|
||||
posix_clock_id = CLOCK_MONOTONIC;
|
||||
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;
|
||||
}
|
||||
#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. */
|
||||
|
||||
struct ptimer *
|
||||
ptimer_allocate (void)
|
||||
{
|
||||
struct ptimer *wt;
|
||||
|
||||
#ifdef PTIMER_INIT_DEFINED
|
||||
static int init_done;
|
||||
if (!init_done)
|
||||
{
|
||||
init_done = 1;
|
||||
ptimer_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 ();
|
||||
struct ptimer *wt = xnew0 (struct ptimer);
|
||||
#ifdef IMPL_init
|
||||
static int init_done;
|
||||
if (!init_done)
|
||||
{
|
||||
init_done = 1;
|
||||
IMPL_init ();
|
||||
}
|
||||
#endif
|
||||
ptimer_reset (wt);
|
||||
return wt;
|
||||
}
|
||||
@ -249,39 +382,6 @@ ptimer_destroy (struct ptimer *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. */
|
||||
@ -290,37 +390,12 @@ void
|
||||
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;
|
||||
}
|
||||
|
||||
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.
|
||||
@ -340,8 +415,8 @@ ptimer_measure (struct ptimer *wt)
|
||||
|
||||
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
|
||||
@ -356,8 +431,8 @@ ptimer_measure (struct ptimer *wt)
|
||||
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)
|
||||
{
|
||||
@ -379,34 +454,12 @@ ptimer_read (const struct ptimer *wt)
|
||||
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
|
||||
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 ();
|
||||
}
|
||||
|
@ -33,7 +33,6 @@ so, delete this exception statement from your version. */
|
||||
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 *));
|
||||
|
||||
@ -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_granularity PARAMS ((void));
|
||||
double ptimer_resolution PARAMS ((void));
|
||||
|
||||
#endif /* PTIMER_H */
|
||||
|
@ -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
|
||||
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
|
||||
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)
|
||||
|
Loading…
Reference in New Issue
Block a user