]> sjero.net Git - wget/blobdiff - src/utils.c
[svn] Subsecond granularity in run_with_timeout.
[wget] / src / utils.c
index bb45665f745527d7e13832298f39fc35718c40b7..9586a8b7c6f9a512e7526bec8dd9ce4d7afcf4bc 100644 (file)
@@ -1879,6 +1879,22 @@ random_number (int max)
   return (int)bounded;
 }
 
+/* Return a random uniformly distributed floating point number in the
+   [0, 1) range.  The precision of returned numbers is 9 digits.
+
+   Modify this to use erand48() where available!  */
+
+double
+random_float (void)
+{
+  /* We can't rely on any specific value of RAND_MAX, but I'm pretty
+     sure it's greater than 1000.  */
+  int rnd1 = random_number (1000);
+  int rnd2 = random_number (1000);
+  int rnd3 = random_number (1000);
+  return rnd1 / 1000.0 + rnd2 / 1000000.0 + rnd3 / 1000000000.0;
+}
+
 #if 0
 /* A debugging function for checking whether an MD5 library works. */
 
@@ -1947,10 +1963,60 @@ abort_run_with_timeout (int sig)
   longjmp (run_with_timeout_env, -1);
 }
 # endif /* not HAVE_SIGSETJMP */
+
+/* Arrange for SIGALRM to be delivered in TIMEOUT seconds.  This uses
+   setitimer where available, alarm otherwise.
+
+   TIMEOUT should be non-zero.  If the timeout value is so small that
+   it would be rounded to zero, it is rounded to the least legal value
+   instead (1us for setitimer, 1s for alarm).  That ensures that
+   SIGALRM will be delivered in all cases.  */
+
+static void
+alarm_set (double timeout)
+{
+#ifdef ITIMER_REAL
+  /* Use the modern itimer interface. */
+  struct itimerval itv;
+  memset (&itv, 0, sizeof (itv));
+  itv.it_value.tv_sec = (long) timeout;
+  itv.it_value.tv_usec = 1000000L * (timeout - (long)timeout);
+  if (itv.it_value.tv_sec == 0 && itv.it_value.tv_usec == 0)
+    /* Ensure that we wait for at least the minimum interval.
+       Specifying zero would mean "wait forever".  */
+    itv.it_value.tv_usec = 1;
+  setitimer (ITIMER_REAL, &itv, NULL);
+#else  /* not ITIMER_REAL */
+  /* Use the old alarm() interface. */
+  int secs = (int) timeout;
+  if (secs == 0)
+    /* Round TIMEOUTs smaller than 1 to 1, not to zero.  This is
+       because alarm(0) means "never deliver the alarm", i.e. "wait
+       forever", which is not what someone who specifies a 0.5s
+       timeout would expect.  */
+    secs = 1;
+  alarm (secs);
+#endif /* not ITIMER_REAL */
+}
+
+/* Cancel the alarm set with alarm_set. */
+
+static void
+alarm_cancel (void)
+{
+#ifdef ITIMER_REAL
+  struct itimerval disable;
+  memset (&disable, 0, sizeof (disable));
+  setitimer (ITIMER_REAL, &disable, NULL);
+#else  /* not ITIMER_REAL */
+  alarm (0);
+#endif /* not ITIMER_REAL */
+}
+
 #endif /* USE_SIGNAL_TIMEOUT */
 
 int
-run_with_timeout (long timeout, void (*fun) (void *), void *arg)
+run_with_timeout (double timeout, void (*fun) (void *), void *arg)
 {
 #ifndef USE_SIGNAL_TIMEOUT
   fun (arg);
@@ -1971,16 +2037,15 @@ run_with_timeout (long timeout, void (*fun) (void *), void *arg)
       signal (SIGALRM, SIG_DFL);
       return 1;
     }
-  alarm (timeout);
+  alarm_set (timeout);
   fun (arg);
 
   /* Preserve errno in case alarm() or signal() modifies it. */
   saved_errno = errno;
-  alarm (0);
+  alarm_cancel ();
   signal (SIGALRM, SIG_DFL);
   errno = saved_errno;
 
   return 0;
 #endif
 }
-