struct progress_implementation {
char *name;
+ int interactive;
void *(*create) PARAMS ((long, long));
void (*update) PARAMS ((void *, long, double));
void (*finish) PARAMS ((void *, double));
static void bar_set_params PARAMS ((const char *));
static struct progress_implementation implementations[] = {
- { "dot", dot_create, dot_update, dot_finish, dot_set_params },
- { "bar", bar_create, bar_update, bar_finish, bar_set_params }
+ { "dot", 0, dot_create, dot_update, dot_finish, dot_set_params },
+ { "bar", 1, bar_create, bar_update, bar_finish, bar_set_params }
};
static struct progress_implementation *current_impl;
static int current_impl_locked;
return current_impl->create (initial, total);
}
+/* Return non-zero if the progress gauge is "interactive", i.e. if it
+ can profit from being called regularly even in absence of data.
+ The progress bar is interactive because it regularly updates the
+ ETA and current update. */
+
+int
+progress_interactive_p (void *progress)
+{
+ return current_impl->interactive;
+}
+
/* Inform the progress gauge of newly received bytes. DLTIME is the
time in milliseconds since the beginning of the download. */
past. */
#define DLSPEED_SAMPLE_MIN 150
+/* The time after which the download starts to be considered
+ "stalled", i.e. the current bandwidth is not printed and the recent
+ download speeds are scratched. */
+#define STALL_START_TIME 5000
+
struct bar_progress {
long initial_length; /* how many bytes have been downloaded
previously. */
position. */
long recent_bytes; /* bytes downloaded so far. */
+ int stalled; /* set when no data arrives for longer
+ than STALL_START_TIME, then reset
+ when new data arrives. */
+
/* create_image() uses these to make sure that ETA information
- doesn't flash. */
+ doesn't flicker. */
double last_eta_time; /* time of the last update to download
speed and ETA, measured since the
beginning of download. */
if (recent_age < DLSPEED_SAMPLE_MIN)
return;
+ if (howmuch == 0)
+ {
+ /* If we're not downloading anything, we might be stalling,
+ i.e. not downloading anything for an extended period of time.
+ Since 0-reads do not enter the history ring, recent_age
+ effectively measures the time since last read. */
+ if (recent_age >= STALL_START_TIME)
+ {
+ /* If we're stalling, reset the ring contents because it's
+ stale and because it will make bar_update stop printing
+ the (bogus) current bandwidth. */
+ bp->stalled = 1;
+ xzero (*hist);
+ bp->recent_bytes = 0;
+ }
+ return;
+ }
+
+ /* We now have a non-zero amount of to store to the speed ring. */
+
+ /* If the stall status was acquired, reset it. */
+ if (bp->stalled)
+ {
+ bp->stalled = 0;
+ /* "recent_age" includes the the entired stalled period, which
+ could be very long. Don't update the speed ring with that
+ value because the current bandwidth would start too small.
+ Start with an arbitrary (but more reasonable) time value and
+ let it level out. */
+ recent_age = 1000;
+ }
+
/* Store "recent" bytes and download time to history ring at the
position POS. */
if (++hist->pos == DLSPEED_HISTORY_SIZE)
hist->pos = 0;
-#if 0
+#if 1
/* Sledgehammer check to verify that the totals are accurate. */
{
int i;
static char dlbuf[16384];
int dlbufsize = sizeof (dlbuf);
- void *progress = NULL;
struct wget_timer *timer = wtimer_allocate ();
+ double last_successful_read_tm;
+
+ /* The progress gauge, set according to the user preferences. */
+ void *progress = NULL;
+
+ /* Non-zero if the progress gauge is interactive, i.e. if it can
+ continually update the display. When true, smaller timeout
+ values are used so that the gauge can update the display when
+ data arrives slowly. */
+ int progress_interactive = 0;
*len = restval;
if (opt.verbose)
- progress = progress_create (restval, expected);
+ {
+ progress = progress_create (restval, expected);
+ progress_interactive = progress_interactive_p (progress);
+ }
if (rbuf && RBUF_FD (rbuf) == fd)
{
if (opt.limit_rate)
limit_bandwidth_reset ();
wtimer_reset (timer);
+ last_successful_read_tm = 0;
/* Use a smaller buffer for low requested bandwidths. For example,
with --limit-rate=2k, it doesn't make sense to slurp in 16K of
{
int amount_to_read = (use_expected
? MIN (expected - *len, dlbufsize) : dlbufsize);
- res = xread (fd, dlbuf, amount_to_read, -1);
+ double tmout = opt.read_timeout;
+ if (progress_interactive)
+ {
+ double waittm;
+ /* For interactive progress gauges, always specify a ~1s
+ timeout, so that the gauge can be updated regularly even
+ when the data arrives very slowly or stalls. */
+ tmout = 0.95;
+ waittm = (wtimer_read (timer) - last_successful_read_tm) / 1000;
+ if (waittm + tmout > opt.read_timeout)
+ {
+ /* Don't allow waiting for data to exceed read timeout. */
+ tmout = opt.read_timeout - waittm;
+ if (tmout < 0)
+ {
+ /* We've already exceeded the timeout. */
+ res = -1;
+ errno = ETIMEDOUT;
+ break;
+ }
+ }
+ }
+ res = xread (fd, dlbuf, amount_to_read, tmout);
- if (res <= 0)
+ if (res == 0 || (res < 0 && errno != ETIMEDOUT))
break;
+ else if (res < 0)
+ res = 0; /* timeout */
- fwrite (dlbuf, 1, res, fp);
- /* Always flush the contents of the network packet. This should
- not hinder performance: fast downloads will be received in
- 16K chunks (which stdio would write out anyway), and slow
- downloads won't be limited by disk performance. */
- fflush (fp);
- if (ferror (fp))
+ wtimer_update (timer);
+ if (res > 0)
{
- res = -2;
- goto out;
+ fwrite (dlbuf, 1, res, fp);
+ /* Always flush the contents of the network packet. This
+ should not hinder performance: fast downloads will be
+ received in 16K chunks (which stdio would write out
+ anyway), and slow downloads won't be limited by disk
+ performance. */
+ fflush (fp);
+ if (ferror (fp))
+ {
+ res = -2;
+ goto out;
+ }
+ last_successful_read_tm = wtimer_read (timer);
}
- wtimer_update (timer);
if (opt.limit_rate)
limit_bandwidth (res, timer);