Le Wed, Dec 03, 2025 at 09:59:40AM -0700, Stan Marsh a écrit :
> From: Chet Ramey
> Subject: Re: Philosophical bash(1) bug
> > (It's not really a float -- it's seconds and microseconds separated by a 
> > `.').

Using *fixed float* is like using *integer* and add a radix at fixed emplacment.

> 
> Greg's results (as well as my own testing) suggest otherwise.  That a
> TMOUT value of 3.1 means 3.1 (decimal) seconds.
>  Unless "time" is also lying to us...

I don't know if it's "time()", or "gettimeofday()", but! (See:
  https://lists.gnu.org/archive/html/bug-bash/2020-04/msg00075.html
)

I was trying to create a function to reach next *rounded period in
nanosecond agains UNIXEPOCH*, for monitoring, something like:

isUnsignedNumber () {
    case $1 in 
        '' | . | *[!0-9.]* | *.*.*)
            echo "ERROR: $1 is not a number" 1>&2
            return 1
            ;;
    esac
}    
sleepNext () {
    isUnsignedNumber $1 || return 1
    local _reqSlp _toSlp
    printf -v _reqSlp %.6f "$1"
    _reqSlp=$((10#${_reqSlp/.}))
    _toSlp=00000$(( _reqSlp - ( ${EPOCHREALTIME/.} % _reqSlp ) ))
    printf -v _toSlp '%.6f' ${_toSlp::-6}.${_toSlp: -6}
    ! read -sn 1 -t $_toSlp ${2:-_}
}

$ while sleepNext .5;do
     IFS=. read nowSec nowMus <<<$EPOCHREALTIME;
     printf '%(%a %d %T)T.%s\n' "$nowSec" "$nowMus";
  done
Fri 05 11:25:34.500606
Fri 05 11:25:35.000273
Fri 05 11:25:35.500798
Fri 05 11:25:36.000776
Fri 05 11:25:36.500764

All new expansion of EPOCHREALTIME was done less than 1'000 nanoseconds
after *rounded period*, this look like expected!

But with bigger timeout, this "delay" will grown:

$ while sleepNext 10;do
     IFS=. read nowSec nowMus <<<$EPOCHREALTIME;
     printf '%(%a %d %T)T.%s\n' "$nowSec" "$nowMus";
  done
Fri 05 11:25:50.002760
Fri 05 11:26:00.006771
Fri 05 11:26:10.006732
Fri 05 11:26:20.006705

With a timeout of 10 seconds, this delay become bigger than 6'000 nanoseconds!

$ while sleepNext 900;do
     IFS=. read nowSec nowMus <<<$EPOCHREALTIME;
     printf '%(%a %d %T)T.%s\n' "$nowSec" "$nowMus";
  done
Fri 05 12:15:00.098612
Fri 05 12:30:00.098717

... Where this "gap" become less and less negligible!

To work around this, my solution is (for now, with two tunneable variables):

sleepNextHR() {
    isUnsignedNumber $1 || return 1
    local -ir _minSleepNS=1'000'00  _initialPercentSleep=96  # May be tunned!
    local _reqSlp _toSlp
    while printf -v _reqSlp %.6f "$1"
          _reqSlp=$((10#${_reqSlp/.}))
          _toSlp=00000$(( _reqSlp - ( ${EPOCHREALTIME/.} % _reqSlp ) ))
          printf -v _toSlp '%.6f' ${_toSlp::-6}.${_toSlp: -6}
          (( 10#${_toSlp/.} > _minSleepNS )); do
        _toSlp=00000$(( 10#${_toSlp/.} * _initialPercentSleep / 100 ))
        printf -v _toSlp '%.6f' ${_toSlp::-6}.${_toSlp: -6}
        read -sn 1 -t $_toSlp ${2:-_} && return 1
    done
    ! read -sn 1 -t $_toSlp ${2:-_}
}

$ while sleepNextHR .5;do
     IFS=. read nowSec nowMus <<<$EPOCHREALTIME;
     printf '%(%a %d %T)T.%s\n' "$nowSec" "$nowMus";
  done
Fri 05 12:36:03.000173
Fri 05 12:36:03.500183
Fri 05 12:36:04.000189
Fri 05 12:36:04.500188

$ while sleepNextHR 10;do
     IFS=. read nowSec nowMus <<<$EPOCHREALTIME;
     printf '%(%a %d %T)T.%s\n' "$nowSec" "$nowMus";
  done
Fri 05 12:37:00.000188
Fri 05 12:37:10.000208
Fri 05 12:37:20.000179

Trick for tracing this:

$ BASH_XTRACEFD=$marker
$ set -x
$ while sleepNextHR .5;do
     IFS=. read nowSec nowMus <<<$EPOCHREALTIME;
     printf '\e[1m%(%a %d %T)T.%s\e[0m\n' "$nowSec" "$nowMus";
  done
+ read -sn 1 -t 0.142758 _
+ read -sn 1 -t 0.005446 _
Fri 05 12:48:14.500337
+ read -sn 1 -t 0.479283 _
+ read -sn 1 -t 0.019118 _
Fri 05 12:48:15.000388
+ read -sn 1 -t 0.479083 _
+ read -sn 1 -t 0.019041 _
Fri 05 12:48:15.500503
+ read -sn 1 -t 0.478937 _
+ read -sn 1 -t 0.019060 _
Fri 05 12:48:16.000308

$ while sleepNextHR 10;do
     IFS=. read nowSec nowMus <<<$EPOCHREALTIME;
     printf '\e[1m%(%a %d %T)T.%s\e[0m\n' "$nowSec" "$nowMus";
  done
+ read -sn 1 -t 5.435048 _
+ read -sn 1 -t 0.212304 _
+ read -sn 1 -t 0.008227 _
Fri 05 12:48:40.000235
+ read -sn 1 -t 9.599453 _
+ read -sn 1 -t 0.377568 _
+ read -sn 1 -t 0.015063 _
Fri 05 12:48:50.000250
+ read -sn 1 -t 9.599509 _
+ read -sn 1 -t 0.377583 _
+ read -sn 1 -t 0.015073 _
Fri 05 12:49:00.000264

Where using `set -x` will have some impact (~190ns without, ~300ns with "-x",
but still acceptable for my needs)

$ set -x;unset BASH_XTRACEFD;exec {marker}>&-

So as I said, I don't know who are lying, but I don't really care.

-- 
 Félix Hauri  -  <[email protected]>  -  http://www.f-hauri.ch

Reply via email to