]> sjero.net Git - wget/commitdiff
[svn] Update the progress gauge even when the data does not arrive.
authorhniksic <devnull@localhost>
Sat, 8 Nov 2003 16:56:52 +0000 (08:56 -0800)
committerhniksic <devnull@localhost>
Sat, 8 Nov 2003 16:56:52 +0000 (08:56 -0800)
src/ChangeLog
src/progress.c
src/progress.h
src/retr.c

index 5af1bd4ad6201a038aa90d1925b4205d4143eca9..8bd9f243cf3b203b952bfda3b6a4a605516c5d1a 100644 (file)
@@ -1,3 +1,12 @@
+2003-11-08  Hrvoje Niksic  <hniksic@xemacs.org>
+
+       * progress.c (update_speed_ring): Clear the speed ring when the
+       download stalls.
+
+       * retr.c (get_contents): Specify 0.95s read timeout, so that the
+       progress gauge can be updated even when data arrives very slowly
+       or stalls.
+
 2003-11-08  Hrvoje Niksic  <hniksic@xemacs.org>
 
        * utils.c (wtimer_allocate): Bless the use of wtimer_read on a
index 8ae09c32689df0492222116eee374749080aebab..0f2e84d98a550d055989f688221a7c121e0b2b6d 100644 (file)
@@ -51,6 +51,7 @@ so, delete this exception statement from your version.  */
 
 struct progress_implementation {
   char *name;
+  int interactive;
   void *(*create) PARAMS ((long, long));
   void (*update) PARAMS ((void *, long, double));
   void (*finish) PARAMS ((void *, double));
@@ -70,8 +71,8 @@ static void bar_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;
@@ -168,6 +169,17 @@ progress_create (long initial, long total)
   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.  */
 
@@ -425,6 +437,11 @@ static volatile sig_atomic_t received_sigwinch;
    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. */
@@ -466,8 +483,12 @@ struct bar_progress {
                                   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. */
@@ -615,6 +636,38 @@ update_speed_ring (struct bar_progress *bp, long howmuch, double dltime)
   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.  */
 
@@ -637,7 +690,7 @@ update_speed_ring (struct bar_progress *bp, long howmuch, double dltime)
   if (++hist->pos == DLSPEED_HISTORY_SIZE)
     hist->pos = 0;
 
-#if 0
+#if 1
   /* Sledgehammer check to verify that the totals are accurate. */
   {
     int i;
index dad06f59c0e0d233bef7ac9905f3c0aa8ca5094b..5437a1d5a27e1e9261e60893a72624c4876dc5f7 100644 (file)
@@ -35,6 +35,7 @@ void set_progress_implementation PARAMS ((const char *));
 void progress_schedule_redirect PARAMS ((void));
 
 void *progress_create PARAMS ((long, long));
+int progress_interactive_p PARAMS ((void *));
 void progress_update PARAMS ((void *, long, double));
 void progress_finish PARAMS ((void *, double));
 
index 5e4212f88e94b2049acb8f49e072de939cea8868..4d03fd4e5e58ddb4b291b88cd600875854e2632f 100644 (file)
@@ -161,13 +161,25 @@ get_contents (int fd, FILE *fp, long *len, long restval, long expected,
   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)
     {
@@ -192,6 +204,7 @@ get_contents (int fd, FILE *fp, long *len, long restval, long expected,
   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
@@ -209,24 +222,53 @@ get_contents (int fd, FILE *fp, long *len, long restval, long expected,
     {
       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);