1
0
Fork 0
mirror of git://git.code.sf.net/p/cdesktopenv/code synced 2025-03-09 15:50:02 +00:00

sleep: guarantee sleeping specified time at minimum (#174)

With this patch, the Korn shell can now guarantee that calls to
sleep on systems using the select or poll method always result in
the system clock advancing by that much time, assuming no
interruptions. This compensates for deficiencies in certain
systems, including SCO UnixWare.

Discussion: https://github.com/ksh93/ksh/pull/174

src/lib/libast/tm/tvsleep.c:
- Ensure that at least the time requested to sleep has elapsed
  for the select and poll methods.
- Simplify the logic of calculating the time remaining to
  sleep and handle the case of an argument of greater than
  10e9 nanoseconds being passed to tvsleep.

src/cmd/ksh93/bltins/sleep.c:
- Eliminate the check for EINTR to handle other cases wherein
  we have not slept enough.

src/cmd/ksh93/tests/variables.sh:
- Improve the diagnostic message when the sleep test fails.
- Revise the SECONDS function test to expect that we always
  sleep for at least the time specified.

src/cmd/ksh93/tests/functions.h:
- Redirect ps stderr to /dev/null. UnixWare ps prints an error
  message about not being able to find the controlling terminal
  when shtests output is piped, but we are only using ps to find
  the PID.
This commit is contained in:
Lev Kujawski 2021-02-14 00:27:04 -07:00 committed by GitHub
parent 0e9aaf1635
commit e2d54b7169
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 50 additions and 45 deletions

View file

@ -139,7 +139,7 @@ void sh_delay(double t, int sflag)
ts.tv_sec = n; ts.tv_sec = n;
ts.tv_nsec = 1000000000 * (t - (double)n); ts.tv_nsec = 1000000000 * (t - (double)n);
while(tvsleep(&ts, &tx) < 0 && errno == EINTR) while(tvsleep(&ts, &tx) < 0)
{ {
if ((shp->trapnote & (SH_SIGSET | SH_SIGTRAP)) || sflag) if ((shp->trapnote & (SH_SIGSET | SH_SIGTRAP)) || sflag)
return; return;

View file

@ -1126,7 +1126,7 @@ function gosleep
"$bin_sleep" 1 "$bin_sleep" 1
} }
x=$( x=$(
(sleep .25; pid=; ps | grep sleep | read pid extra; [[ $pid ]] && kill -- "$pid") & (sleep .25; pid=; ps 2>/dev/null | grep sleep | read pid extra; [[ $pid ]] && kill -- "$pid") &
gosleep 2> /dev/null gosleep 2> /dev/null
print ok print ok
) )

View file

@ -40,11 +40,15 @@ if (( RANDOM==RANDOM || $RANDOM==$RANDOM ))
then err_exit RANDOM variable not working then err_exit RANDOM variable not working
fi fi
# SECONDS # SECONDS
let SECONDS=0.0 float secElapsed=0.0 secSleep=0.001
sleep .001 let SECONDS=$secElapsed
if (( SECONDS < .001 )) sleep $secSleep
then err_exit "either 'sleep' or \$SECONDS not working" secElapsed=SECONDS
if (( secElapsed < secSleep ))
then err_exit "slept ${secElapsed} seconds instead of ${secSleep}: " \
"either 'sleep' or \$SECONDS not working"
fi fi
unset -v secElapsed secSleep
# _ # _
set abc def set abc def
if [[ $_ != def ]] if [[ $_ != def ]]
@ -508,12 +512,11 @@ fi
function foo function foo
{ {
typeset SECONDS=0 typeset SECONDS=0
sleep .002 sleep 0.002
print $SECONDS print $SECONDS
} }
x=$(foo) x=$(foo)
(( x >.001 && x < 1 )) (( x >= 0.002 && x < 1 ))
' '
} 2> /dev/null || err_exit 'SECONDS not working in function' } 2> /dev/null || err_exit 'SECONDS not working in function'
cat > $tmp/script <<-\! cat > $tmp/script <<-\!

View file

@ -41,6 +41,8 @@
# endif # endif
#endif #endif
#define NANOSECONDS 1000000000L
/* /*
* sleep for tv * sleep for tv
* non-zero exit if sleep did not complete * non-zero exit if sleep did not complete
@ -53,11 +55,12 @@ int
tvsleep(register const Tv_t* tv, register Tv_t* rv) tvsleep(register const Tv_t* tv, register Tv_t* rv)
{ {
int r;
#if _lib_nanosleep #if _lib_nanosleep
struct timespec stv; struct timespec stv;
struct timespec srv; struct timespec srv;
int r;
stv.tv_sec = tv->tv_sec; stv.tv_sec = tv->tv_sec;
stv.tv_nsec = tv->tv_nsec; stv.tv_nsec = tv->tv_nsec;
@ -77,13 +80,11 @@ tvsleep(register const Tv_t* tv, register Tv_t* rv)
#if _lib_select #if _lib_select
struct timeval stv; struct timeval tvSleep = { tv->tv_sec, tv->tv_nsec / 1000 };
if (tv->tv_nsec % 1000)
++tvSleep.tv_usec;
stv.tv_sec = tv->tv_sec; r = select(0, NiL, NiL, NiL, &tvSleep);
if (!(stv.tv_usec = tv->tv_nsec / 1000))
stv.tv_usec = 1;
if (select(0, NiL, NiL, NiL, &stv) >= 0)
return 0;
#else #else
@ -162,51 +163,52 @@ tvsleep(register const Tv_t* tv, register Tv_t* rv)
if (!(t = (n + 999999L) / 1000000L)) if (!(t = (n + 999999L) / 1000000L))
t = 1; t = 1;
if (poll(&pfd, 0, t) >= 0) r = poll(&pfd, 0, t);
return 0;
#endif #endif
#endif #endif
} }
bad:
if (errno == EINTR && rv) /* Ascertain whether we actually slept for a sufficient time.
* It is preferable to sleep a little more than necessary than too little.
*/
{ {
tvgettime(rv); struct timespec tsAfter;
if (rv->tv_nsec < bv.tv_nsec) long sec, nsec;
tvgettime(&tsAfter);
/* Calculate seconds left to sleep */
sec = (long)(tv->tv_sec + tv->tv_nsec / NANOSECONDS) -
((long)tsAfter.tv_sec - (long)bv.tv_sec);
/* Calculate nanoseconds left to sleep */
nsec = (long)(tv->tv_nsec % NANOSECONDS) -
((long)tsAfter.tv_nsec - (long)bv.tv_nsec);
if (nsec >= NANOSECONDS)
{ {
rv->tv_nsec += 1000000000L; ++sec;
rv->tv_sec--; nsec -= NANOSECONDS;
} }
rv->tv_nsec -= bv.tv_nsec; else if (nsec < 0)
rv->tv_sec -= bv.tv_sec;
if (rv->tv_sec > tv->tv_sec)
{ {
rv->tv_sec = 0; --sec;
rv->tv_nsec = 0; nsec += NANOSECONDS;
} }
else
if (sec >= 0 && (sec > 0 || nsec > 0))
{ {
rv->tv_sec = tv->tv_sec - rv->tv_sec; if (rv)
if (rv->tv_nsec > tv->tv_nsec)
{ {
if (!rv->tv_sec) rv->tv_sec = sec;
{ rv->tv_nsec = nsec;
rv->tv_sec = 0;
rv->tv_nsec = 0;
}
else
{
rv->tv_sec--;
rv->tv_nsec = 1000000000L - rv->tv_nsec + tv->tv_nsec;
}
}
else
rv->tv_nsec = tv->tv_nsec - rv->tv_nsec;
}
} }
return -1; return -1;
}
}
return r;
#endif #endif