GNU bug report logs - #77535
timeout treats very short durations as `0`

Previous Next

Package: coreutils;

Reported by: Nicolas Boichat <nicolas <at> boichat.ch>

Date: Fri, 4 Apr 2025 16:13:02 UTC

Severity: normal

Done: Pádraig Brady <P <at> draigBrady.com>

Bug is archived. No further changes may be made.

To add a comment to this bug, you must first unarchive it, by sending
a message to control AT debbugs.gnu.org, with unarchive 77535 in the body.
You can then email your comments to 77535 AT debbugs.gnu.org in the normal way.

Toggle the display of automated, internal messages from the tracker.

View this report as an mbox folder, status mbox, maintainer mbox


Report forwarded to bug-coreutils <at> gnu.org:
bug#77535; Package coreutils. (Fri, 04 Apr 2025 16:13:02 GMT) Full text and rfc822 format available.

Acknowledgement sent to Nicolas Boichat <nicolas <at> boichat.ch>:
New bug report received and forwarded. Copy sent to bug-coreutils <at> gnu.org. (Fri, 04 Apr 2025 16:13:02 GMT) Full text and rfc822 format available.

Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):

From: Nicolas Boichat <nicolas <at> boichat.ch>
To: bug-coreutils <at> gnu.org
Subject: timeout treats very short durations as `0`
Date: Fri, 4 Apr 2025 17:26:56 +0200
Hi,

Version: timeout (GNU coreutils) 9.6, Archlinux, x86-64.

While playing with different duration parameters to `timeout`, I
noticed that extremely short durations, like `1e-3000`, are rounded
down to `0`. The problem is that `0` has a special meaning (disabling
the timeout), so I don't think this is desired.

All of these commands exit immediately:
```
timeout 0.0001 cat
timeout 1e-100 cat
timeout 1e-300 cat
timeout 1e-323 cat
```

But these never exits:
```
timeout 1e-324 cat
timeout 1e-3000 cat
```

As if we had typed:
```
timeout 0 cat
```

I think there is some logic in `printf` to handle float parsing
underflow, so maybe this can be reused in `timeout` as well.

Thanks,




Reply sent to Pádraig Brady <P <at> draigBrady.com>:
You have taken responsibility. (Fri, 04 Apr 2025 19:29:02 GMT) Full text and rfc822 format available.

Notification sent to Nicolas Boichat <nicolas <at> boichat.ch>:
bug acknowledged by developer. (Fri, 04 Apr 2025 19:29:02 GMT) Full text and rfc822 format available.

Message #10 received at 77535-done <at> debbugs.gnu.org (full text, mbox):

From: Pádraig Brady <P <at> draigBrady.com>
To: Nicolas Boichat <nicolas <at> boichat.ch>, 77535-done <at> debbugs.gnu.org
Subject: Re: bug#77535: timeout treats very short durations as `0`
Date: Fri, 4 Apr 2025 20:27:55 +0100
On 04/04/2025 16:26, Nicolas Boichat wrote:
> Hi,
> 
> Version: timeout (GNU coreutils) 9.6, Archlinux, x86-64.
> 
> While playing with different duration parameters to `timeout`, I
> noticed that extremely short durations, like `1e-3000`, are rounded
> down to `0`. The problem is that `0` has a special meaning (disabling
> the timeout), so I don't think this is desired.
> 
> All of these commands exit immediately:
> ```
> timeout 0.0001 cat
> timeout 1e-100 cat
> timeout 1e-300 cat
> timeout 1e-323 cat
> ```
> 
> But these never exits:
> ```
> timeout 1e-324 cat
> timeout 1e-3000 cat
> ```
> 
> As if we had typed:
> ```
> timeout 0 cat
> ```
> 
> I think there is some logic in `printf` to handle float parsing
> underflow, so maybe this can be reused in `timeout` as well.

The following should avoid this issue.

Marking this as done.

thanks!
Pádraig

diff --git a/src/timeout.c b/src/timeout.c
index 578d71070..6756cd888 100644
--- a/src/timeout.c
+++ b/src/timeout.c
@@ -371,6 +371,10 @@ parse_duration (char const *str)
       usage (EXIT_CANCELED);
     }

+  /* Clamp underflow to 1ns, as 0 disables the timeout.  */
+  if (duration == 0 && errno == ERANGE)
+    duration = 1e-9;
+
   return duration;
 }





Information forwarded to bug-coreutils <at> gnu.org:
bug#77535; Package coreutils. (Sat, 05 Apr 2025 05:41:03 GMT) Full text and rfc822 format available.

Message #13 received at 77535-done <at> debbugs.gnu.org (full text, mbox):

From: Nicolas Boichat <nicolas <at> boichat.ch>
To: Pádraig Brady <P <at> draigbrady.com>
Cc: 77535-done <at> debbugs.gnu.org
Subject: Re: bug#77535: timeout treats very short durations as `0`
Date: Fri, 4 Apr 2025 21:53:53 +0200
On Fri, 4 Apr 2025 at 21:27, Pádraig Brady <P <at> draigbrady.com> wrote:
>
> On 04/04/2025 16:26, Nicolas Boichat wrote:
> > Hi,
> >
> > Version: timeout (GNU coreutils) 9.6, Archlinux, x86-64.
> >
> > While playing with different duration parameters to `timeout`, I
> > noticed that extremely short durations, like `1e-3000`, are rounded
> > down to `0`. The problem is that `0` has a special meaning (disabling
> > the timeout), so I don't think this is desired.
> >
> > All of these commands exit immediately:
> > ```
> > timeout 0.0001 cat
> > timeout 1e-100 cat
> > timeout 1e-300 cat
> > timeout 1e-323 cat
> > ```
> >
> > But these never exits:
> > ```
> > timeout 1e-324 cat
> > timeout 1e-3000 cat
> > ```
> >
> > As if we had typed:
> > ```
> > timeout 0 cat
> > ```
> >
> > I think there is some logic in `printf` to handle float parsing
> > underflow, so maybe this can be reused in `timeout` as well.
>
> The following should avoid this issue.
>
> Marking this as done.

That was fast! Thanks!

>
> thanks!
> Pádraig
>
> diff --git a/src/timeout.c b/src/timeout.c
> index 578d71070..6756cd888 100644
> --- a/src/timeout.c
> +++ b/src/timeout.c
> @@ -371,6 +371,10 @@ parse_duration (char const *str)
>         usage (EXIT_CANCELED);
>       }
>
> +  /* Clamp underflow to 1ns, as 0 disables the timeout.  */
> +  if (duration == 0 && errno == ERANGE)
> +    duration = 1e-9;
> +
>     return duration;
>   }
>




Information forwarded to bug-coreutils <at> gnu.org:
bug#77535; Package coreutils. (Sat, 05 Apr 2025 08:04:01 GMT) Full text and rfc822 format available.

Message #16 received at 77535 <at> debbugs.gnu.org (full text, mbox):

From: Paul Eggert <eggert <at> cs.ucla.edu>
To: 77535 <at> debbugs.gnu.org
Cc: nicolas <at> boichat.ch, P <at> draigBrady.com
Subject: Re: bug#77535: timeout treats very short durations as `0`
Date: Sat, 5 Apr 2025 01:03:48 -0700
[Message part 1 (text/plain, inline)]
On 2025-04-04 12:27, Pádraig Brady wrote:
> +  /* Clamp underflow to 1ns, as 0 disables the timeout.  */
> +  if (duration == 0 && errno == ERANGE)
> +    duration = 1e-9;

That isn't exactly right as the 1e-9 double-rounds to 2e-9 when we 
compute the struct timespec. Also, even with the patch the code 
mishandles 16777216.000000001 (2**24 + 10**-9) by treating it as if it 
were just 16777216. These are tiny bugs, I know, but I took up the 
challenge of doing this things more correctly by installing the attached.
[0001-timeout-round-timeouts-up.patch (text/x-patch, attachment)]

Information forwarded to bug-coreutils <at> gnu.org:
bug#77535; Package coreutils. (Sat, 05 Apr 2025 11:04:02 GMT) Full text and rfc822 format available.

Message #19 received at 77535 <at> debbugs.gnu.org (full text, mbox):

From: Pádraig Brady <P <at> draigBrady.com>
To: Paul Eggert <eggert <at> cs.ucla.edu>, 77535 <at> debbugs.gnu.org
Cc: nicolas <at> boichat.ch
Subject: Re: bug#77535: timeout treats very short durations as `0`
Date: Sat, 5 Apr 2025 12:03:49 +0100
[Message part 1 (text/plain, inline)]
On 05/04/2025 09:03, Paul Eggert wrote:
> On 2025-04-04 12:27, Pádraig Brady wrote:
>> +  /* Clamp underflow to 1ns, as 0 disables the timeout.  */
>> +  if (duration == 0 && errno == ERANGE)
>> +    duration = 1e-9;
> 
> That isn't exactly right as the 1e-9 double-rounds to 2e-9 when we
> compute the struct timespec. Also, even with the patch the code
> mishandles 16777216.000000001 (2**24 + 10**-9) by treating it as if it
> were just 16777216. These are tiny bugs, I know, but I took up the
> challenge of doing this things more correctly by installing the attached.

This is the first time coreutils is linking with libm.
So practically that does add measurable overhead:

  $ time seq 10000 | xargs -n1 src/timeout-old >/dev/null
  real    0m7.139s

  $ time seq 10000 | xargs -n1 src/timeout >/dev/null
  real    0m8.161s

So on my 2.6GHz i7-5600U we're adding 102,200 ns to the startup time,
to address theoretical single nanosecond issues.
Given the code is also more complex, I'm thinking using libm is not worth it?

BTW I'll apply the attached cleanups if we keep the libm dependency.

cheers,
Pádraig
[timeout-round-tweaks.patch (text/x-patch, attachment)]

Information forwarded to bug-coreutils <at> gnu.org:
bug#77535; Package coreutils. (Sat, 05 Apr 2025 18:03:02 GMT) Full text and rfc822 format available.

Message #22 received at 77535 <at> debbugs.gnu.org (full text, mbox):

From: "Philip Rowlands" <phr+coreutils <at> dimebar.com>
To: Pádraig Brady <P <at> draigbrady.com>,
 "Paul Eggert" <eggert <at> cs.ucla.edu>, 77535 <at> debbugs.gnu.org
Cc: nicolas <at> boichat.ch
Subject: Re: bug#77535: timeout treats very short durations as `0`
Date: Sat, 05 Apr 2025 19:02:24 +0100
I was interested to see that the code makes uses of strtod, and how this interacts with the minute / hour / day suffix.

$ strace -e trace=clock_nanosleep sleep 3
clock_nanosleep(CLOCK_REALTIME, 0, {tv_sec=3, tv_nsec=0}, 

$ strace -e trace=clock_nanosleep sleep 0xas
clock_nanosleep(CLOCK_REALTIME, 0, {tv_sec=10, tv_nsec=0},

$ strace -e trace=clock_nanosleep sleep 0xam
clock_nanosleep(CLOCK_REALTIME, 0, {tv_sec=600, tv_nsec=0},

$ strace -e trace=clock_nanosleep sleep 0xad
clock_nanosleep(CLOCK_REALTIME, 0, {tv_sec=173, tv_nsec=0},

Here 0xad is interpreted as 173 seconds, not 10 days.

It's a corner case, but should sleep(1) support or tolerate hex input, and if so, how should it handle the "d" suffix? FWIW I'd just vote to return an error for hex intervals.


Cheers,
Phil




Information forwarded to bug-coreutils <at> gnu.org:
bug#77535; Package coreutils. (Sat, 05 Apr 2025 18:46:02 GMT) Full text and rfc822 format available.

Message #25 received at 77535 <at> debbugs.gnu.org (full text, mbox):

From: Pádraig Brady <P <at> draigBrady.com>
To: Philip Rowlands <phr+coreutils <at> dimebar.com>,
 Paul Eggert <eggert <at> cs.ucla.edu>, 77535 <at> debbugs.gnu.org
Cc: nicolas <at> boichat.ch
Subject: Re: bug#77535: timeout treats very short durations as `0`
Date: Sat, 5 Apr 2025 19:45:23 +0100
On 05/04/2025 19:02, Philip Rowlands wrote:
> I was interested to see that the code makes uses of strtod, and how this interacts with the minute / hour / day suffix.
> 
> $ strace -e trace=clock_nanosleep sleep 3
> clock_nanosleep(CLOCK_REALTIME, 0, {tv_sec=3, tv_nsec=0},
> 
> $ strace -e trace=clock_nanosleep sleep 0xas
> clock_nanosleep(CLOCK_REALTIME, 0, {tv_sec=10, tv_nsec=0},
> 
> $ strace -e trace=clock_nanosleep sleep 0xam
> clock_nanosleep(CLOCK_REALTIME, 0, {tv_sec=600, tv_nsec=0},
> 
> $ strace -e trace=clock_nanosleep sleep 0xad
> clock_nanosleep(CLOCK_REALTIME, 0, {tv_sec=173, tv_nsec=0},
> 
> Here 0xad is interpreted as 173 seconds, not 10 days.
> 
> It's a corner case, but should sleep(1) support or tolerate hex input, and if so, how should it handle the "d" suffix? FWIW I'd just vote to return an error for hex intervals.

Good point :)
Given FreeBSD and ksh sleep implementations also behave like GNU,
we should probably just document the edge case that
(day) suffixes are best avoided with hex inputs.

  ~$ ksh
  $ type sleep
  sleep is a shell builtin
  $ time sleep 0x10d
  real	4m29.00s

cheers,
Pádraig




Information forwarded to bug-coreutils <at> gnu.org:
bug#77535; Package coreutils. (Sat, 05 Apr 2025 19:43:02 GMT) Full text and rfc822 format available.

Message #28 received at 77535 <at> debbugs.gnu.org (full text, mbox):

From: Pádraig Brady <P <at> draigBrady.com>
To: Philip Rowlands <phr+coreutils <at> dimebar.com>, 77535 <at> debbugs.gnu.org
Cc: nicolas <at> boichat.ch
Subject: Re: bug#77535: timeout treats very short durations as `0`
Date: Sat, 5 Apr 2025 20:41:58 +0100
[Message part 1 (text/plain, inline)]
On 05/04/2025 19:45, Pádraig Brady wrote:
> On 05/04/2025 19:02, Philip Rowlands wrote:
>> I was interested to see that the code makes uses of strtod, and how this interacts with the minute / hour / day suffix.
>>
>> $ strace -e trace=clock_nanosleep sleep 3
>> clock_nanosleep(CLOCK_REALTIME, 0, {tv_sec=3, tv_nsec=0},
>>
>> $ strace -e trace=clock_nanosleep sleep 0xas
>> clock_nanosleep(CLOCK_REALTIME, 0, {tv_sec=10, tv_nsec=0},
>>
>> $ strace -e trace=clock_nanosleep sleep 0xam
>> clock_nanosleep(CLOCK_REALTIME, 0, {tv_sec=600, tv_nsec=0},
>>
>> $ strace -e trace=clock_nanosleep sleep 0xad
>> clock_nanosleep(CLOCK_REALTIME, 0, {tv_sec=173, tv_nsec=0},
>>
>> Here 0xad is interpreted as 173 seconds, not 10 days.
>>
>> It's a corner case, but should sleep(1) support or tolerate hex input, and if so, how should it handle the "d" suffix? FWIW I'd just vote to return an error for hex intervals.
> 
> Good point :)
> Given FreeBSD and ksh sleep implementations also behave like GNU,
> we should probably just document the edge case that
> (day) suffixes are best avoided with hex inputs.
> 
>     ~$ ksh
>     $ type sleep
>     sleep is a shell builtin
>     $ time sleep 0x10d
>     real	4m29.00s

I'll apply the attached later to document this.

cheers,
Pádraig
[coreutils-hex-duration-suffixes.patch (text/x-patch, attachment)]

Information forwarded to bug-coreutils <at> gnu.org:
bug#77535; Package coreutils. (Sat, 05 Apr 2025 20:58:02 GMT) Full text and rfc822 format available.

Message #31 received at 77535 <at> debbugs.gnu.org (full text, mbox):

From: Pádraig Brady <P <at> draigBrady.com>
To: Paul Eggert <eggert <at> cs.ucla.edu>, 77535 <at> debbugs.gnu.org
Cc: nicolas <at> boichat.ch
Subject: Re: bug#77535: timeout treats very short durations as `0`
Date: Sat, 5 Apr 2025 21:56:53 +0100
[Message part 1 (text/plain, inline)]
On 05/04/2025 12:03, Pádraig Brady wrote:
> On 05/04/2025 09:03, Paul Eggert wrote:
>> On 2025-04-04 12:27, Pádraig Brady wrote:
>>> +  /* Clamp underflow to 1ns, as 0 disables the timeout.  */
>>> +  if (duration == 0 && errno == ERANGE)
>>> +    duration = 1e-9;
>>
>> That isn't exactly right as the 1e-9 double-rounds to 2e-9 when we
>> compute the struct timespec. Also, even with the patch the code
>> mishandles 16777216.000000001 (2**24 + 10**-9) by treating it as if it
>> were just 16777216. These are tiny bugs, I know, but I took up the
>> challenge of doing this things more correctly by installing the attached.
> 
> This is the first time coreutils is linking with libm.
> So practically that does add measurable overhead:
> 
>     $ time seq 10000 | xargs -n1 src/timeout-old >/dev/null
>     real    0m7.139s
> 
>     $ time seq 10000 | xargs -n1 src/timeout >/dev/null
>     real    0m8.161s
> 
> So on my 2.6GHz i7-5600U we're adding 102,200 ns to the startup time,
> to address theoretical single nanosecond issues.
> Given the code is also more complex, I'm thinking using libm is not worth it?
> 
> BTW I'll apply the attached cleanups if we keep the libm dependency.

I'm going to apply the attached later to remove the libm dependency.
It's just too much of a dependency for minimal functionality.
All existing tests are maintained, and still pass.

cheers,
Pádraig
[timeout-remove-libm.patch (text/x-patch, attachment)]

Information forwarded to bug-coreutils <at> gnu.org:
bug#77535; Package coreutils. (Mon, 07 Apr 2025 08:09:02 GMT) Full text and rfc822 format available.

Message #34 received at 77535 <at> debbugs.gnu.org (full text, mbox):

From: Paul Eggert <eggert <at> cs.ucla.edu>
To: Pádraig Brady <P <at> draigBrady.com>
Cc: 77535 <at> debbugs.gnu.org, nicolas <at> boichat.ch
Subject: Re: bug#77535: timeout treats very short durations as `0`
Date: Mon, 7 Apr 2025 01:08:43 -0700
[Message part 1 (text/plain, inline)]
On 2025-04-05 13:56, Pádraig Brady wrote:
> I'm going to apply the attached later to remove the libm dependency.
> It's just too much of a dependency for minimal functionality.

Thanks, agreed about the dependency. Still, 'timeout' (and, now that I 
think about it, 'sleep' and 'tail') should always wait for at least the 
time requested.

After thinking about it I installed the attached further patch, which 
addresses the problem in a different way without needing libm on 
GNU/Linux. Although we could tighten the bounds further in typical cases 
(by using the algorithm 'date' uses to parse seconds, if the string is 
of that form) I tried to keep it simple, as the main point is to not 
sleep for less than the time requested.

Floating point can be such a pain sometimes, even for simple things. 
(Read the cited paper and see....)
[0001-timeout-don-t-sleep-less-than-requested.patch (text/x-patch, attachment)]

Information forwarded to bug-coreutils <at> gnu.org:
bug#77535; Package coreutils. (Mon, 07 Apr 2025 08:24:01 GMT) Full text and rfc822 format available.

Message #37 received at 77535 <at> debbugs.gnu.org (full text, mbox):

From: Paul Eggert <eggert <at> cs.ucla.edu>
To: Pádraig Brady <P <at> draigBrady.com>
Cc: 77535 <at> debbugs.gnu.org, nicolas <at> boichat.ch,
 Philip Rowlands <phr+coreutils <at> dimebar.com>
Subject: Re: bug#77535: timeout treats very short durations as `0`
Date: Mon, 7 Apr 2025 01:23:46 -0700
On 2025-04-05 12:41, Pádraig Brady wrote:
> +Note it's best to avoid combining suffixes with hexadecimal arguments,
> +as any @samp{d} will @emph{not} be interpreted as a suffix.

But 'd' is interpreted as a suffix with hexadecimal arguments that have 
an exponent. For example, 'sleep 0x1p0d' sleeps for 1 day. (Also, "Note" 
isn't needed.) Perhaps change to:

A hexadecimal number can precede a @samp{d} suffix only if the number 
has a @samp{p} style exponent, e.g., @samp{0x1p0d} means one day.




Information forwarded to bug-coreutils <at> gnu.org:
bug#77535; Package coreutils. (Mon, 07 Apr 2025 17:46:02 GMT) Full text and rfc822 format available.

Message #40 received at 77535 <at> debbugs.gnu.org (full text, mbox):

From: Pádraig Brady <P <at> draigBrady.com>
To: Paul Eggert <eggert <at> cs.ucla.edu>
Cc: 77535 <at> debbugs.gnu.org, nicolas <at> boichat.ch,
 Philip Rowlands <phr+coreutils <at> dimebar.com>
Subject: Re: bug#77535: timeout treats very short durations as `0`
Date: Mon, 7 Apr 2025 18:45:22 +0100
On 07/04/2025 09:23, Paul Eggert wrote:
> On 2025-04-05 12:41, Pádraig Brady wrote:
>> +Note it's best to avoid combining suffixes with hexadecimal arguments,
>> +as any @samp{d} will @emph{not} be interpreted as a suffix.
> 
> But 'd' is interpreted as a suffix with hexadecimal arguments that have
> an exponent. For example, 'sleep 0x1p0d' sleeps for 1 day. (Also, "Note"
> isn't needed.) Perhaps change to:
> 
> A hexadecimal number can precede a @samp{d} suffix only if the number
> has a @samp{p} style exponent, e.g., @samp{0x1p0d} means one day.

Cool,

It's better to document a workaround than a restriction.
I pushed the above adjustment in your name.

cheers,
Pádraig




Information forwarded to bug-coreutils <at> gnu.org:
bug#77535; Package coreutils. (Mon, 07 Apr 2025 17:47:02 GMT) Full text and rfc822 format available.

Message #43 received at 77535 <at> debbugs.gnu.org (full text, mbox):

From: Pádraig Brady <P <at> draigBrady.com>
To: Paul Eggert <eggert <at> cs.ucla.edu>
Cc: nicolas <at> boichat.ch, 77535 <at> debbugs.gnu.org
Subject: Re: bug#77535: timeout treats very short durations as `0`
Date: Mon, 7 Apr 2025 18:46:31 +0100
On 07/04/2025 09:08, Paul Eggert wrote:
> On 2025-04-05 13:56, Pádraig Brady wrote:
>> I'm going to apply the attached later to remove the libm dependency.
>> It's just too much of a dependency for minimal functionality.
> 
> Thanks, agreed about the dependency. Still, 'timeout' (and, now that I
> think about it, 'sleep' and 'tail') should always wait for at least the
> time requested.
> 
> After thinking about it I installed the attached further patch, which
> addresses the problem in a different way without needing libm on
> GNU/Linux. Although we could tighten the bounds further in typical cases
> (by using the algorithm 'date' uses to parse seconds, if the string is
> of that form) I tried to keep it simple, as the main point is to not
> sleep for less than the time requested.
> 
> Floating point can be such a pain sometimes, even for simple things.
> (Read the cited paper and see....)

Nice.
I hadn't realized gnulib's signbit avoided the dependency on libm.

thanks!
Pádraig




bug archived. Request was from Debbugs Internal Request <help-debbugs <at> gnu.org> to internal_control <at> debbugs.gnu.org. (Tue, 06 May 2025 11:24:13 GMT) Full text and rfc822 format available.

This bug report was last modified 30 days ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.