1
0
Fork 0
mirror of git://git.code.sf.net/p/cdesktopenv/code synced 2025-02-15 04:32:24 +00:00

Fix three bugs in the sleep builtin (#77)

This commit backports the main changes to sh_delay from ksh93v-
and ksh2020, which fixes the following bugs:

- Microsecond amounts of less than one millisecond are no longer
  ignored. The following loop will now take a minimum of one
  second to complete:
  for ((i = 0; i != 10000; i++)) do
    sleep PT100U
  done

- 'sleep 30' no longer adds an extra 30 milliseconds to the total
  amount of time to sleep. This bug is hard to notice since 30
  milliseconds can be considered within the margin of error. The
  only reason why longer delays weren't affected is because the old
  code masked the bug when the interval is greater than 30 seconds:
  else if(n > 30)
  {
      sleep(n);
      t -= n;
  }
  This caused 'sleep -s' to break with intervals greater than 30
  seconds, so an actual fix is used instead of a workaround.

- 'sleep -s' now functions correctly with intervals of more than
  30 seconds as the new code doesn't need the old workaround. This
  is done by handling '-s' in sh_delay.

src/cmd/ksh93/bltins/sleep.c:
- Remove the replacement for sleep(3) from the sleep builtin.
- Replace the old sh_delay function with the newer one from ksh2020.
  The new function uses tvsleep, which uses nanosleep(3) internally.

src/cmd/ksh93/include/shell.h,
src/cmd/ksh93/edit/edit.c,
src/cmd/ksh93/sh/jobs.c,
src/cmd/ksh93/sh/xec.c,
src/cmd/ksh93/shell.3:
- Update sh_delay documentation and usage since the function now
  requires two arguments.

src/cmd/ksh93/tests/builtins.sh:
- Add a regression test for 'sleep -s' when the interval is greater
  than 30 seconds. The other bugs can't be tested for in a feasible
  manner across all systems:
  https://github.com/ksh93/ksh/pull/72#issuecomment-657215616
This commit is contained in:
Johnothan King 2020-07-16 21:00:28 -07:00 committed by GitHub
parent ea5b25b93a
commit 2db9953ae0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 45 additions and 108 deletions

7
NEWS
View file

@ -8,6 +8,13 @@ Any uppercase BUG_* names are modernish shell bug IDs.
- The 'history' and 'r' default aliases have been made regular built-ins,
leaving zero default aliases.
- Fixed a bug that caused 'sleep -s' to have no effect with intervals longer
than 30 seconds.
- The accuracy of the sleep builtin has been improved. It no longer ignores
microseconds and doesn't add extra milliseconds when the interval is less
than 31 seconds.
2020-07-15:
- The 'autoload', 'compound', 'float', 'functions', 'integer' and 'nameref'

View file

@ -26,9 +26,7 @@
*
*/
#define sleep ______sleep
#include "defs.h"
#undef sleep
#include <error.h>
#include <errno.h>
#include <tmx.h>
@ -114,7 +112,7 @@ skip:
time_t now;
errno = 0;
shp->lastsig=0;
sh_delay(d);
sh_delay(d,sflag);
if(sflag || tloc==0 || errno!=EINTR || shp->lastsig)
break;
sh_sigcheck(shp);
@ -127,109 +125,22 @@ skip:
return(0);
}
static void completed(void * handle)
{
char *expired = (char*)handle;
*expired = 1;
}
unsigned int sleep(unsigned int sec)
{
Shell_t *shp = sh_getinterp();
pid_t newpid, curpid=getpid();
void *tp;
char expired = 0;
shp->lastsig = 0;
tp = (void*)sh_timeradd(1000*sec, 0, completed, (void*)&expired);
do
{
if(!shp->gd->waitevent || (*shp->gd->waitevent)(-1,-1L,0)==0)
pause();
if(shp->sigflag[SIGALRM]&SH_SIGTRAP)
sh_timetraps(shp);
if((newpid=getpid()) != curpid)
{
curpid = newpid;
shp->lastsig = 0;
shp->trapnote &= ~SH_SIGSET;
if(expired)
expired = 0;
else
timerdel(tp);
tp = (void*)sh_timeradd(1000*sec, 0, completed, (void*)&expired);
}
}
while(!expired && shp->lastsig==0);
if(!expired)
timerdel(tp);
sh_sigcheck(shp);
return(0);
}
/*
* delay execution for time <t>
*/
void sh_delay(double t)
void sh_delay(double t, int sflag)
{
register int n = (int)t;
Shell_t *shp = sh_getinterp();
#ifdef _lib_poll
struct pollfd fd;
if(t<=0)
return;
else if(n > 30)
Shell_t *shp = sh_getinterp();
int n = (int)t;
Tv_t ts, tx;
ts.tv_sec = n;
ts.tv_nsec = 1000000000 * (t - (double)n);
while(tvsleep(&ts, &tx) < 0 && errno == EINTR)
{
sleep(n);
t -= n;
}
if(n=(int)(1000*t))
{
if(!shp->gd->waitevent || (*shp->gd->waitevent)(-1,(long)n,0)==0)
poll(&fd,0,n);
}
#else
# if defined(_lib_select) && defined(_mem_tv_usec_timeval)
struct timeval timeloc;
if(t<=0)
return;
if(n=(int)(1000*t) && shp->gd->waitevent && (*shp->gd->waitevent)(-1,(long)n,0))
return;
n = (int)t;
timeloc.tv_sec = n;
timeloc.tv_usec = 1000000*(t-(double)n);
select(0,(fd_set*)0,(fd_set*)0,(fd_set*)0,&timeloc);
# else
# ifdef _lib_select
/* for 9th edition machines */
if(t<=0)
if ((shp->trapnote & (SH_SIGSET | SH_SIGTRAP)) || sflag)
return;
if(n > 30)
{
sleep(n);
t -= n;
}
if(n=(int)(1000*t))
{
if(!shp->gd->waitevent || (*shp->gd->waitevent)(-1,(long)n,0)==0)
select(0,(fd_set*)0,(fd_set*)0,n);
}
# else
struct tms tt;
if(t<=0)
return;
sleep(n);
t -= n;
if(t)
{
clock_t begin = times(&tt);
if(begin==0)
return;
t *= shp->gd->lim.clk_tck;
n += (t+.5);
while((times(&tt)-begin) < n);
}
# endif
# endif
#endif /* _lib_poll */
ts = tx;
}
}

View file

@ -855,7 +855,7 @@ int ed_read(void *context, int fd, char *buff, int size, int reedit)
}
ep->sh->winch = 0;
ed_flush(ep);
sh_delay(.05);
sh_delay(.05,0);
astwinsize(2,&rows,&newsize);
ep->e_winsz = newsize-1;
if(ep->e_winsz < MINWINDOW)

View file

@ -176,7 +176,7 @@ extern void sh_subfork(void);
extern Shell_t *sh_init(int,char*[],Shinit_f);
extern int sh_reinit(char*[]);
extern int sh_eval(Sfio_t*,int);
extern void sh_delay(double);
extern void sh_delay(double,int);
extern void *sh_parse(Shell_t*, Sfio_t*,int);
extern int sh_trap(const char*,int);
extern int sh_fun(Namval_t*,Namval_t*, char*[]);

View file

@ -1188,7 +1188,7 @@ int job_kill(register struct process *pw,register int sig)
job_unstop(pw);
#endif /* SIGTSTP */
if(r>=0)
sh_delay(.05);
sh_delay(.05,0);
}
while(pw && pw->p_pgrp==0 && (r=kill(pw->p_pid,sig))>=0)
{
@ -1211,7 +1211,7 @@ int job_kill(register struct process *pw,register int sig)
sfprintf(sfstderr,"kill: %s: %s\n",job_string, msg);
r = 2;
}
sh_delay(.001);
sh_delay(.001,0);
job_unlock();
return(r);
}

View file

@ -214,7 +214,7 @@ static void iounpipe(Shell_t *shp)
job.in_critical--;
break;
}
sh_delay(1);
sh_delay(1,0);
}
if((n = read(subpipe[0],buff,sizeof(buff)))==0)
break;
@ -1935,7 +1935,7 @@ int sh_exec(register const Shnode_t *t, int flags)
timerdel(fifo_timer);
sh_iorenumber(shp,fn,fd);
sh_close(fn);
sh_delay(.001);
sh_delay(.001,0);
unlink(shp->fifo);
free(shp->fifo);
shp->fifo = 0;

View file

@ -48,7 +48,7 @@ Shscope_t *sh_setscope(Shscope_t *\fIscope\fP);
int (*sh_fdnotify(int(*\fIfn\fP)(int,int)))(int,int);
char *sh_fmtq(const char *\fIstring\fP);
void *sh_waitnotify(Shwait_f \fIfn\fP);
void sh_delay(double \fIsec\fP);
void sh_delay(double \fIsec\fP, int \fIsflag\fP);
Sfio_t *sh_iogetiop(int \fIfd\fP, int \fImode\fP);
int sh_sigcheck(void);
.ft R
@ -349,6 +349,9 @@ where the most recent built-in was invoked, or where
.PP
The \f5sh_delay()\fP function is used to cause the
shell to sleep for a period of time defined by \fIsec\fP.
If \fIsflag\fP is true, the shell will stop sleeping when
any signal is received; otherwise signals such as \f5SIGCONT\fP
and \f5SIGINFO\fP are treated normally.
.PP
The \f5sh_fmtq()\fP function can be used to convert a string
into a string that is quoted so that it can be reinput

View file

@ -846,5 +846,21 @@ IMPLEMENTATION
[[ $actual == "$expect" ]] || err_exit "getopts: '--man' output" \
"(expected $(printf %q "$expect"), got $(printf %q "$actual"))"
# ======
# 'sleep -s' should work in interactive shells when seconds > 30.
sleepsig="$tmp/sleepsig.sh"
cat >| "$sleepsig" << 'EOF'
sleep -s 31 &
sleep .001
kill -CONT $!
if kill -0 $!; then
kill -TERM $! # Don't leave a lingering background process
exit 1
else
exit 0
fi
EOF
"$SHELL" -i "$sleepsig" 2> /dev/null || err_exit "'sleep -s' doesn't work with intervals of more than 30 seconds"
# ======
exit $((Errors<125?Errors:125))