mirror of
https://git.savannah.gnu.org/git/coreutils.git
synced 2025-09-10 07:59:52 +02:00
timeout: don’t sleep less than requested
Also, change sleep and tail to not sleep less than requested. * bootstrap.conf (gnulib_modules): Add dtimespec-bound. * gl/lib/dtimespec-bound.c, gl/lib/dtimespec-bound.h: * gl/modules/dtimespec-bound: New files. * src/sleep.c, src/tail.c, src/timeout.c: Include dtimespec-bound.h. * src/sleep.c, src/tail.c: Don’t include xstrtod.h. * src/sleep.c (apply_suffix, main): * src/tail.c (parse_options): * src/timeout.c (apply_time_suffix): Don’t sleep less than the true number of seconds. * src/timeout.c: Don’t include ctype.h. (is_negative): Remove; no longer needed. (parse_duration): Use a slightly looser bound on the timeout, one that doesn’t need -lm on GNU/Linux. Clear errno before calling cl_strtod.
This commit is contained in:
4
NEWS
4
NEWS
@@ -27,6 +27,10 @@ GNU coreutils NEWS -*- outline -*-
|
||||
For example `timeout 1e-5000 sleep inf` would never timeout.
|
||||
[bug introduced with timeout in coreutils-7.0]
|
||||
|
||||
sleep, tail, and timeout would sometimes sleep for slightly less
|
||||
time than requested.
|
||||
[bug introduced in coreutils-5.0]
|
||||
|
||||
'who -m' now outputs entries for remote logins. Previously login
|
||||
entries prefixed with the service (like "sshd") were not matched.
|
||||
[bug introduced in coreutils-9.4]
|
||||
|
||||
@@ -78,6 +78,7 @@ gnulib_modules="
|
||||
dirfd
|
||||
dirname
|
||||
do-release-commit-and-tag
|
||||
dtimespec-bound
|
||||
dtoastr
|
||||
dup2
|
||||
endian
|
||||
|
||||
3
gl/lib/dtimespec-bound.c
Normal file
3
gl/lib/dtimespec-bound.c
Normal file
@@ -0,0 +1,3 @@
|
||||
#include <config.h>
|
||||
#define DTIMESPEC_BOUND_INLINE _GL_EXTERN_INLINE
|
||||
#include "dtimespec-bound.h"
|
||||
76
gl/lib/dtimespec-bound.h
Normal file
76
gl/lib/dtimespec-bound.h
Normal file
@@ -0,0 +1,76 @@
|
||||
/* Compute a timespec-related bound for floating point.
|
||||
|
||||
Copyright 2025 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* written by Paul Eggert */
|
||||
|
||||
#ifndef DTIMESPEC_BOUND_H
|
||||
#define DTIMESPEC_BOUND_H 1
|
||||
|
||||
/* This file uses _GL_INLINE_HEADER_BEGIN, _GL_INLINE. */
|
||||
#if !_GL_CONFIG_H_INCLUDED
|
||||
#error "Please include config.h first."
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <float.h>
|
||||
#include <math.h>
|
||||
|
||||
_GL_INLINE_HEADER_BEGIN
|
||||
#ifndef DTIMESPEC_BOUND_INLINE
|
||||
# define DTIMESPEC_BOUND_INLINE _GL_INLINE
|
||||
#endif
|
||||
|
||||
/* If C is positive and finite, return the least floating point value
|
||||
greater than C. However, if 0 < C < (2 * DBL_TRUE_MIN) / (DBL_EPSILON**2),
|
||||
return a positive value less than 1e-9.
|
||||
|
||||
If C is +0.0, return a positive value < 1e-9 if ERR == ERANGE, C otherwise.
|
||||
If C is +Inf, return C.
|
||||
If C is negative, return -timespec_roundup(-C).
|
||||
If C is a NaN, return a NaN.
|
||||
|
||||
Assume round-to-even.
|
||||
|
||||
This function can be useful if some floating point operation
|
||||
rounded to C but we want a nearby bound on the true value, where
|
||||
the bound can be converted to struct timespec. If the operation
|
||||
underflowed to zero, ERR should be ERANGE a la strtod. */
|
||||
|
||||
DTIMESPEC_BOUND_INLINE double
|
||||
dtimespec_bound (double c, int err)
|
||||
{
|
||||
/* Do not use copysign or nextafter, as they link to -lm in GNU/Linux. */
|
||||
|
||||
/* Use DBL_TRUE_MIN for the special case of underflowing to zero;
|
||||
any positive value less than 1e-9 will do. */
|
||||
if (err == ERANGE && c == 0)
|
||||
return signbit (c) ? -DBL_TRUE_MIN : DBL_TRUE_MIN;
|
||||
|
||||
/* This is the first part of Algorithm 2 of:
|
||||
Rump SM, Zimmermann P, Boldo S, Melquiond G.
|
||||
Computing predecessor and successor in rounding to nearest.
|
||||
BIT Numer Math. 2009;49(419-431).
|
||||
<https://doi.org/10.1007/s10543-009-0218-z>
|
||||
The rest of Algorithm 2 is not needed because numbers less than
|
||||
the predecessor of 1e-9 merely need to stay less than 1e-9. */
|
||||
double phi = DBL_EPSILON / 2 * (1 + DBL_EPSILON);
|
||||
return c + phi * c;
|
||||
}
|
||||
|
||||
_GL_INLINE_HEADER_END
|
||||
|
||||
#endif
|
||||
24
gl/modules/dtimespec-bound
Normal file
24
gl/modules/dtimespec-bound
Normal file
@@ -0,0 +1,24 @@
|
||||
Description:
|
||||
Adjust a double to provide a timespec bound.
|
||||
|
||||
Files:
|
||||
lib/dtimespec-bound.c
|
||||
lib/dtimespec-bound.h
|
||||
|
||||
Depends-on:
|
||||
float-h
|
||||
signbit
|
||||
|
||||
configure.ac:
|
||||
|
||||
Makefile.am:
|
||||
lib_SOURCES += dtimespec-bound.c
|
||||
|
||||
Include:
|
||||
"dtimespec-bound.h"
|
||||
|
||||
License:
|
||||
GPL
|
||||
|
||||
Maintainer:
|
||||
all
|
||||
14
src/sleep.c
14
src/sleep.c
@@ -20,10 +20,10 @@
|
||||
|
||||
#include "system.h"
|
||||
#include "cl-strtod.h"
|
||||
#include "dtimespec-bound.h"
|
||||
#include "long-options.h"
|
||||
#include "quote.h"
|
||||
#include "xnanosleep.h"
|
||||
#include "xstrtod.h"
|
||||
|
||||
/* The official name of this program (e.g., no 'g' prefix). */
|
||||
#define PROGRAM_NAME "sleep"
|
||||
@@ -85,7 +85,7 @@ apply_suffix (double *x, char suffix_char)
|
||||
return false;
|
||||
}
|
||||
|
||||
*x *= multiplier;
|
||||
*x = dtimespec_bound (*x * multiplier, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -116,9 +116,11 @@ main (int argc, char **argv)
|
||||
|
||||
for (int i = optind; i < argc; i++)
|
||||
{
|
||||
double s;
|
||||
char const *p;
|
||||
if (! (xstrtod (argv[i], &p, &s, cl_strtod) || errno == ERANGE)
|
||||
char *p;
|
||||
errno = 0;
|
||||
double duration = cl_strtod (argv[i], &p);
|
||||
double s = dtimespec_bound (duration, errno);
|
||||
if (argv[i] == p
|
||||
/* Nonnegative interval. */
|
||||
|| ! (0 <= s)
|
||||
/* No extra chars after the number and an optional s,m,h,d char. */
|
||||
@@ -130,7 +132,7 @@ main (int argc, char **argv)
|
||||
ok = false;
|
||||
}
|
||||
|
||||
seconds += s;
|
||||
seconds = dtimespec_bound (seconds + s, 0);
|
||||
}
|
||||
|
||||
if (!ok)
|
||||
|
||||
10
src/tail.c
10
src/tail.c
@@ -35,6 +35,7 @@
|
||||
#include "assure.h"
|
||||
#include "c-ctype.h"
|
||||
#include "cl-strtod.h"
|
||||
#include "dtimespec-bound.h"
|
||||
#include "fcntl--.h"
|
||||
#include "iopoll.h"
|
||||
#include "isapipe.h"
|
||||
@@ -47,7 +48,6 @@
|
||||
#include "xdectoint.h"
|
||||
#include "xnanosleep.h"
|
||||
#include "xstrtol.h"
|
||||
#include "xstrtod.h"
|
||||
|
||||
#if HAVE_INOTIFY
|
||||
# include "hash.h"
|
||||
@@ -2276,11 +2276,13 @@ parse_options (int argc, char **argv,
|
||||
|
||||
case 's':
|
||||
{
|
||||
double s;
|
||||
if (! (xstrtod (optarg, nullptr, &s, cl_strtod) && 0 <= s))
|
||||
char *ep;
|
||||
errno = 0;
|
||||
double s = cl_strtod (optarg, &ep);
|
||||
if (optarg == ep || *ep || ! (0 <= s))
|
||||
error (EXIT_FAILURE, 0,
|
||||
_("invalid number of seconds: %s"), quote (optarg));
|
||||
*sleep_interval = s;
|
||||
*sleep_interval = dtimespec_bound (s, errno);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
@@ -45,7 +45,6 @@
|
||||
Written by Pádraig Brady. */
|
||||
|
||||
#include <config.h>
|
||||
#include <ctype.h>
|
||||
#include <getopt.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
@@ -57,6 +56,7 @@
|
||||
|
||||
#include "system.h"
|
||||
#include "cl-strtod.h"
|
||||
#include "dtimespec-bound.h"
|
||||
#include "sig2str.h"
|
||||
#include "operand2sig.h"
|
||||
#include "quote.h"
|
||||
@@ -348,47 +348,32 @@ apply_time_suffix (double *x, char suffix_char)
|
||||
return false;
|
||||
}
|
||||
|
||||
*x *= multiplier;
|
||||
*x = dtimespec_bound (*x * multiplier, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ATTRIBUTE_PURE static bool
|
||||
is_negative (char const *num)
|
||||
{
|
||||
while (*num && isspace (to_uchar (*num)))
|
||||
num++;
|
||||
return *num == '-';
|
||||
}
|
||||
|
||||
static double
|
||||
parse_duration (char const *str)
|
||||
{
|
||||
char *ep;
|
||||
errno = 0;
|
||||
double duration = cl_strtod (str, &ep);
|
||||
double s = dtimespec_bound (duration, errno);
|
||||
|
||||
if (ep == str
|
||||
/* Nonnegative interval. */
|
||||
|| ! (0 <= duration)
|
||||
/* The interval did not underflow to -0. */
|
||||
|| (errno == ERANGE && is_negative (str))
|
||||
|| ! (0 <= s)
|
||||
/* 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. */
|
||||
|| !apply_time_suffix (&duration, *ep))
|
||||
|| !apply_time_suffix (&s, *ep))
|
||||
{
|
||||
error (0, 0, _("invalid time interval %s"), quote (str));
|
||||
usage (EXIT_CANCELED);
|
||||
}
|
||||
|
||||
/* 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 (duration == 0 && errno == ERANGE)
|
||||
duration = 9.313225746154785e-10;
|
||||
|
||||
return duration;
|
||||
return s;
|
||||
}
|
||||
|
||||
static void
|
||||
|
||||
Reference in New Issue
Block a user