]> git.ipfire.org Git - thirdparty/coreutils.git/commitdiff
timeout: round timeouts up
authorPaul Eggert <eggert@cs.ucla.edu>
Sat, 5 Apr 2025 07:58:22 +0000 (00:58 -0700)
committerPaul Eggert <eggert@cs.ucla.edu>
Sat, 5 Apr 2025 07:58:58 +0000 (00:58 -0700)
This handles timeouts like 16777216.000000001 correctly;
formerly the subsecond part of that timeout was ignored.
* bootstrap.conf (gnulib_modules): Add fenv-rounding, signbit.
* src/local.mk (src_timeout_LDADD): Append $(FENV_ROUNDING_LIBM).
* src/timeout.c: Include fenv.h, math.h.
Don’t include xstrtod.h, as xstrtod’s checking now gets in the way.
(parse_duration): Round up when calling cl_strtod.
Check for -1e-1000.  Don’t double-round 1e-9.
* tests/timeout/timeout-parameters.sh: Test for -0.1,
-1e-1000, 1e-1000.

bootstrap.conf
src/local.mk
src/timeout.c
tests/timeout/timeout-parameters.sh

index c99838e95a2e4a59cd6b9d25f567f0622dab45f5..3c133ef7148b39b2eb04b3039557246276e6cb9d 100644 (file)
@@ -99,6 +99,7 @@ gnulib_modules="
   fdatasync
   fdopen
   fdutimensat
+  fenv-rounding
   file-has-acl
   file-type
   fileblocks
@@ -242,6 +243,7 @@ gnulib_modules="
   settime
   sig2str
   sigaction
+  signbit
   skipchars
   smack
   ssize_t
index fd9dc81c278f76b097eb280098cdce35b12e404d..0a4e3af432ad866cc3be21f241fc87e7ef516236 100644 (file)
@@ -257,6 +257,9 @@ src_stat_LDADD += $(LIB_SELINUX)
 # for nvlist_lookup_uint64_array
 src_stat_LDADD += $(LIB_NVPAIR)
 
+# for fegetround, fesetround
+src_timeout_LDADD += $(FENV_ROUNDING_LIBM)
+
 # for gettime, settime, tempname, utimecmp, utimens
 copy_ldadd += $(CLOCK_TIME_LIB)
 src_date_LDADD += $(CLOCK_TIME_LIB)
index 6756cd8881695a41db49d8fd45033bcfc429d201..5947c19421ae006c93804af3b3ebd9c635d328fc 100644 (file)
@@ -45,7 +45,9 @@
    Written by Pádraig Brady.  */
 
 #include <config.h>
+#include <fenv.h>
 #include <getopt.h>
+#include <math.h>
 #include <stdio.h>
 #include <sys/types.h>
 #include <signal.h>
@@ -56,7 +58,6 @@
 
 #include "system.h"
 #include "cl-strtod.h"
-#include "xstrtod.h"
 #include "sig2str.h"
 #include "operand2sig.h"
 #include "quote.h"
@@ -356,12 +357,29 @@ apply_time_suffix (double *x, char suffix_char)
 static double
 parse_duration (char const *str)
 {
-  double duration;
-  char const *ep;
+  /* If possible tell strtod to round up, so that we always wait at
+     least as long as STR specifies.  Although Standard C requires
+     FENV_ACCESS ON, don't bother if using GCC as it warns.  */
+#ifdef FE_UPWARD
+# if !defined __GNUC__ && 199901 <= __STDC_VERSION__
+#  pragma STDC FENV_ACCESS ON
+# endif
+  int round = fegetround ();
+  fesetround (FE_UPWARD);
+#endif
+
+  char *ep;
+  double duration = cl_strtod (str, &ep);
+
+#ifdef FE_UPWARD
+  fesetround (round);
+#endif
 
-  if (! (xstrtod (str, &ep, &duration, cl_strtod) || errno == ERANGE)
+  if (ep == str
       /* Nonnegative interval.  */
       || ! (0 <= duration)
+      /* The interval did not underflow to -0.  */
+      || (signbit (duration) && errno == ERANGE)
       /* No extra chars after the number and an optional s,m,h,d char.  */
       || (*ep && *(ep + 1))
       /* Check any suffix char and update timeout based on the suffix.  */
@@ -371,9 +389,18 @@ parse_duration (char const *str)
       usage (EXIT_CANCELED);
     }
 
-  /* Clamp underflow to 1ns, as 0 disables the timeout.  */
+  /* Do not let the duration underflow to 0, as 0 disables the timeout.
+     Use 2**-30 instead of 0; settimeout will round it up to 1 ns,
+     whereas 1e-9 might double-round to 2 ns.
+
+     If FE_UPWARD is defined, this code is not needed on glibc as its
+     strtod rounds upward correctly.  Although this code might not be
+     needed on non-glibc platforms too, it's too much trouble to
+     worry about that.  */
+#if !defined FE_UPWARD || !defined __GLIBC__
   if (duration == 0 && errno == ERANGE)
-    duration = 1e-9;
+    duration = 9.313225746154785e-10;
+#endif
 
   return duration;
 }
index 84b450e90753e8c5802ed94fa23574bde263cde9..408846262215c8e3c1189fbfc36d86eb15db93c6 100755 (executable)
@@ -23,8 +23,10 @@ getlimits_
 
 # internal errors are 125, distinct from execution failure
 
-# invalid timeout
+# invalid timeouts
 returns_ 125 timeout invalid sleep 0 || fail=1
+returns_ 125 timeout ' -0.1' sleep 0 || fail=1
+returns_ 125 timeout ' -1e-10000' sleep 0 || fail=1
 
 # invalid kill delay
 returns_ 125 timeout --kill-after=invalid 1 sleep 0 || fail=1
@@ -38,6 +40,9 @@ timeout 10.34 sleep 0 || fail=1
 # nanoseconds potentially supported
 timeout 9.999999999 sleep 0 || fail=1
 
+# round underflow up to 1 ns
+returns_ 124 timeout 1e-10000 sleep 1 || fail
+
 # invalid signal spec
 returns_ 125 timeout --signal=invalid 1 sleep 0 || fail=1